/* * Copyright 2010 NCHOVY * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.codec; import java.io.UnsupportedEncodingException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; public class EncodingRule { public static final byte NULL_TYPE = 0; public static final byte BOOLEAN_TYPE = 1; // INT16_TYPE, INT32_TYPE, INT64_TYPE are not used after 2.0 public static final byte INT16_TYPE = 2; public static final byte INT32_TYPE = 3; public static final byte INT64_TYPE = 4; public static final byte STRING_TYPE = 5; // utf-8 only public static final byte DATE_TYPE = 6; public static final byte IP4_TYPE = 7; public static final byte IP6_TYPE = 8; public static final byte MAP_TYPE = 9; public static final byte ARRAY_TYPE = 10; public static final byte BLOB_TYPE = 11; public static final byte ZINT16_TYPE = 12; public static final byte ZINT32_TYPE = 13; public static final byte ZINT64_TYPE = 14; public static final byte FLOAT_TYPE = 15; public static final byte DOUBLE_TYPE = 16; private EncodingRule() { } public static void encode(ByteBuffer bb, Object value) { encode(bb, value, null); } @SuppressWarnings("unchecked") public static void encode(ByteBuffer bb, Object value, CustomCodec cc) { if (value == null) { encodeNull(bb); } else if (value instanceof String) { encodeString(bb, (String) value); } else if (value instanceof Long) { encodeLong(bb, (Long) value); } else if (value instanceof Integer) { encodeInt(bb, (Integer) value); } else if (value instanceof Short) { encodeShort(bb, (Short) value); } else if (value instanceof Date) { encodeDate(bb, (Date) value); } else if (value instanceof Inet4Address) { encodeIp4(bb, (Inet4Address) value); } else if (value instanceof Inet6Address) { encodeIp6(bb, (Inet6Address) value); } else if (value instanceof Map<?, ?>) { encodeMap(bb, (Map<String, Object>) value, cc); } else if (value instanceof List<?>) { encodeArray(bb, (List<?>) value, cc); } else if (value.getClass().isArray()) { Class<?> c = value.getClass().getComponentType(); if (c == byte.class) { encodeBlob(bb, (byte[]) value); } else if (c == int.class) { encodeArray(bb, (int[]) value); } else if (c == long.class) { encodeArray(bb, (long[]) value); } else if (c == short.class) { encodeArray(bb, (short[]) value); } else if (c == boolean.class) { encodeArray(bb, (boolean[]) value); } else if (c == double.class) { encodeArray(bb, (double[]) value); } else if (c == float.class) { encodeArray(bb, (float[]) value); } else if (c == char.class) { throw new UnsupportedTypeException(value.getClass().getName()); } else { encodeArray(bb, (Object[]) value, cc); } } else if (value instanceof Boolean) { encodeBoolean(bb, (Boolean) value); } else if (value instanceof Float) { encodeFloat(bb, (Float) value); } else if (value instanceof Double) { encodeDouble(bb, (Double) value); } else { if (cc != null) cc.encode(bb, value); else throw new UnsupportedTypeException(value.getClass().getName()); } } @Deprecated public static int length(Object value) { return lengthOf(value); } public static int lengthOf(Object value) { return lengthOf(value, null); } @SuppressWarnings("unchecked") public static int lengthOf(Object value, CustomCodec cc) { if (value == null) { return lengthOfNull(); } else if (value instanceof String) { return lengthOfString((String) value); } else if (value instanceof Long) { return lengthOfLong((Long) value); } else if (value instanceof Integer) { return lengthOfInt((Integer) value); } else if (value instanceof Short) { return lengthOfShort((Short) value); } else if (value instanceof Date) { return lengthOfDate(); } else if (value instanceof Inet4Address) { return lengthOfIp4((Inet4Address) value); } else if (value instanceof Inet6Address) { return lengthOfIp6((Inet6Address) value); } else if (value instanceof Map<?, ?>) { return lengthOfMap((Map<String, Object>) value, cc); } else if (value instanceof List<?>) { return lengthOfArray((List<?>) value, cc); } else if (value.getClass().isArray()) { Class<?> c = value.getClass().getComponentType(); if (c == byte.class) { return lengthOfBlob((byte[]) value); } else if (c == int.class) { return lengthOfArray((int[]) value); } else if (c == long.class) { return lengthOfArray((long[]) value); } else if (c == short.class) { return lengthOfArray((short[]) value); } else if (c == boolean.class) { return lengthOfArray((boolean[]) value); } else if (c == double.class) { return lengthOfArray((double[]) value); } else if (c == float.class) { return lengthOfArray((float[]) value); } else if (c == char.class) { throw new UnsupportedTypeException(value.getClass().getName()); } else { return lengthOfArray((Object[]) value, cc); } } else if (value instanceof Boolean) { return lengthOfBoolean((Boolean) value); } else if (value instanceof Float) { return lengthOfFloat((Float) value); } else if (value instanceof Double) { return lengthOfDouble((Double) value); } else { if (cc != null) return cc.lengthOf(value); else throw new UnsupportedTypeException(value.getClass().getName()); } } /* * Return single object byte length(include type byte). ByteBuffer's * position will not move. */ public static int getObjectLength(ByteBuffer bb) { return getObjectLength(bb, null); } public static int getObjectLength(ByteBuffer bb, CustomCodec cc) { ByteBuffer buf = bb.duplicate(); int typeByte = buf.get(); switch (typeByte) { case NULL_TYPE: return 1; case STRING_TYPE: case MAP_TYPE: case ARRAY_TYPE: case BLOB_TYPE: { int pos = buf.position(); return 1 + (int) decodeRawNumber(buf) + (buf.position() - pos); } case INT32_TYPE: throw new UnsupportedTypeException("deprecated number type"); case INT16_TYPE: throw new UnsupportedTypeException("deprecated number type"); case INT64_TYPE: throw new UnsupportedTypeException("deprecated number type"); case DATE_TYPE: { int pos = buf.position(); buf.getLong(); return 1 + (buf.position() - pos); } case IP4_TYPE: return 1 + 4; case IP6_TYPE: return 1 + 16; case BOOLEAN_TYPE: return 1 + 1; case ZINT32_TYPE: case ZINT16_TYPE: case ZINT64_TYPE: { int pos = buf.position(); decodeRawNumber(buf); return 1 + (buf.position() - pos); } case FLOAT_TYPE: return 1 + 4; case DOUBLE_TYPE: return 1 + 8; } if (cc != null) return cc.getObjectLength(buf); else throw new UnsupportedTypeException("type: " + typeByte); } public static Object decode(ByteBuffer bb) { return decode(bb, null); } public static Object decode(ByteBuffer bb, CustomCodec cc) { int typeByte = bb.get(bb.position()); switch (typeByte) { case NULL_TYPE: { bb.get(); return null; } case STRING_TYPE: return decodeString(bb); case INT32_TYPE: throw new UnsupportedTypeException("deprecated number type"); case INT16_TYPE: throw new UnsupportedTypeException("deprecated number type"); case INT64_TYPE: throw new UnsupportedTypeException("deprecated number type"); case DATE_TYPE: return decodeDate(bb); case IP4_TYPE: return decodeIp4(bb); case IP6_TYPE: return decodeIp6(bb); case MAP_TYPE: return decodeMap(bb, cc); case ARRAY_TYPE: return decodeArray(bb, cc); case BLOB_TYPE: return decodeBlob(bb); case BOOLEAN_TYPE: return decodeBoolean(bb); case ZINT32_TYPE: return (int) decodeInt(bb); case ZINT16_TYPE: return (short) decodeShort(bb); case ZINT64_TYPE: return (long) decodeLong(bb); case FLOAT_TYPE: return (float) decodeFloat(bb); case DOUBLE_TYPE: return (double) decodeDouble(bb); } if (cc != null) return cc.decode(bb); else throw new UnsupportedTypeException("type: " + typeByte); } public static void encodeNull(ByteBuffer bb) { bb.put(NULL_TYPE); } public static void encodeNumber(ByteBuffer bb, Class<?> clazz, long value) { if (clazz.equals(int.class)) { encodeInt(bb, (int) value); } else if (clazz.equals(long.class)) { encodeLong(bb, value); } else if (clazz.equals(short.class)) { encodeShort(bb, (short) value); } else { throw new UnsupportedTypeException("invalid number type: " + clazz.getName()); } } public static void encodeRawNumber(ByteBuffer bb, Class<?> clazz, long value) { int len = lengthOfRawNumber(clazz, value); for (int i = 0; i < len; ++i) { byte signalBit = (byte) (i != len - 1 ? 0x80 : 0); byte data = (byte) (signalBit | (byte) (value >> (7 * (len - i - 1)) & 0x7F)); bb.put(data); } } public static long decodeRawNumber(ByteBuffer bb) { long value = 0L; byte b; do { value = value << 7; b = bb.get(); value |= b & 0x7F; } while ((b & 0x80) == 0x80); return value; } public static void encodePlainLong(ByteBuffer bb, long value) { bb.put(INT64_TYPE); encodeRawNumber(bb, long.class, value); } public static long decodePlainLong(ByteBuffer bb) { byte type = bb.get(); if (type != INT64_TYPE) throw new TypeMismatchException(INT64_TYPE, type, bb.position() - 1); return (long) decodeRawNumber(bb); } public static void encodePlainInt(ByteBuffer bb, int value) { bb.put(INT32_TYPE); encodeRawNumber(bb, int.class, value); } public static int decodePlainInt(ByteBuffer bb) { byte type = bb.get(); if (type != INT32_TYPE) throw new TypeMismatchException(INT32_TYPE, type, bb.position() - 1); return (int) decodeRawNumber(bb); } public static void encodePlainShort(ByteBuffer bb, short value) { bb.put(INT16_TYPE); encodeRawNumber(bb, short.class, value); } public static short decodePlainShort(ByteBuffer bb) { byte type = bb.get(); if (type != INT16_TYPE) throw new TypeMismatchException(INT16_TYPE, type, bb.position() - 1); return (short) decodeRawNumber(bb); } public static void encodeLong(ByteBuffer bb, long value) { bb.put(ZINT64_TYPE); long zvalue = (value << 1) ^ (value >> 63); encodeRawNumber(bb, long.class, zvalue); } public static long decodeLong(ByteBuffer bb) { byte type = bb.get(); if (type != ZINT64_TYPE) throw new TypeMismatchException(ZINT64_TYPE, type, bb.position() - 1); long zvalue = (long) decodeRawNumber(bb); return ((zvalue >> 1) & 0x7FFFFFFFFFFFFFFFL) ^ -(zvalue & 1); } public static void encodeInt(ByteBuffer bb, int value) { bb.put(ZINT32_TYPE); long zvalue = ((long) value << 1) ^ ((long) value >> 31); encodeRawNumber(bb, int.class, zvalue); } public static int decodeInt(ByteBuffer bb) { byte type = bb.get(); if (type != ZINT32_TYPE) throw new TypeMismatchException(ZINT32_TYPE, type, bb.position() - 1); int zvalue = (int) decodeRawNumber(bb); int v = (int) (((zvalue >> 1) & 0x7FFFFFFF) ^ -(zvalue & 1)); return v; } public static void encodeShort(ByteBuffer bb, short value) { bb.put(ZINT16_TYPE); long zvalue = ((long) value << 1) ^ ((long) value >> 15); encodeRawNumber(bb, short.class, zvalue); } public static short decodeShort(ByteBuffer bb) { byte type = bb.get(); if (type != ZINT16_TYPE) throw new TypeMismatchException(ZINT16_TYPE, type, bb.position() - 1); long zvalue = decodeRawNumber(bb); return (short) (((zvalue >> 1) & 0x7FFF) ^ -(zvalue & 1)); } public static void encodeString(ByteBuffer bb, String value) { bb.put(STRING_TYPE); try { byte[] buffer = value.getBytes("utf-8"); encodeRawNumber(bb, int.class, buffer.length); bb.put(buffer); } catch (UnsupportedEncodingException e) { } } private static final Charset utf8 = Charset.forName("utf-8"); public static String decodeString(ByteBuffer bb) { byte type = bb.get(); if (type != STRING_TYPE) throw new TypeMismatchException(STRING_TYPE, type, bb.position() - 1); int length = (int) decodeRawNumber(bb); int oldLimit = bb.limit(); bb.limit(bb.position() + length); CharBuffer cb = utf8.decode(bb); String value = cb.toString(); bb.limit(oldLimit); return value; } public static void encodeDate(ByteBuffer bb, Date value) { bb.put(DATE_TYPE); bb.putLong(value.getTime()); } public static Date decodeDate(ByteBuffer bb) { byte type = bb.get(); if (type != DATE_TYPE) throw new TypeMismatchException(DATE_TYPE, type, bb.position() - 1); return new Date(bb.getLong()); } public static void encodeBoolean(ByteBuffer bb, boolean value) { bb.put(BOOLEAN_TYPE); bb.put((byte) (value ? 1 : 0)); } public static boolean decodeBoolean(ByteBuffer bb) { byte type = bb.get(); if (type != BOOLEAN_TYPE) throw new TypeMismatchException(BOOLEAN_TYPE, type, bb.position() - 1); byte value = bb.get(); return value == 1; } public static void encodeIp4(ByteBuffer bb, Inet4Address value) { bb.put(IP4_TYPE); bb.put(value.getAddress()); } public static InetAddress decodeIp4(ByteBuffer bb) { byte type = bb.get(); if (type != IP4_TYPE) throw new TypeMismatchException(IP4_TYPE, type, bb.position() - 1); byte[] address = new byte[4]; bb.get(address); try { return Inet4Address.getByAddress(address); } catch (UnknownHostException e) { // bytes always correct. ignore. return null; } } public static void encodeIp6(ByteBuffer bb, Inet6Address value) { bb.put(IP6_TYPE); bb.put(value.getAddress()); } public static InetAddress decodeIp6(ByteBuffer bb) { byte type = bb.get(); if (type != IP6_TYPE) throw new TypeMismatchException(IP6_TYPE, type, bb.position() - 1); byte[] address = new byte[16]; bb.get(address); try { return Inet6Address.getByAddress(address); } catch (UnknownHostException e) { // bytes always correct. ignore. return null; } } public static void encodeMap(ByteBuffer bb, Map<String, Object> map) { encodeMap(bb, map, null); } public static void encodeMap(ByteBuffer bb, Map<String, Object> map, CustomCodec cc) { bb.put(MAP_TYPE); int length = preencodeMap(map, 0, cc); encodeRawNumber(bb, int.class, length); for (String key : map.keySet()) { EncodedStringCache k = EncodedStringCache.getEncodedString(key); bb.put(STRING_TYPE); encodeRawNumber(bb, int.class, k.value().length); bb.put(k.value()); Object value = map.get(key); if (value instanceof String) { EncodedStringCache v = EncodedStringCache.getEncodedString((String) value); bb.put(STRING_TYPE); encodeRawNumber(bb, int.class, v.value().length); bb.put(v.value()); } else encode(bb, value, cc); } } /** * preencodeMap track down recursively and sum all content length * * @return content length except type and length bytes */ @SuppressWarnings("unchecked") private static int preencodeMap(Map<String, Object> map, int depth, CustomCodec cc) { int length = 0; for (String key : map.keySet()) { EncodedStringCache k = EncodedStringCache.getEncodedString(key); length += k.length(); Object value = map.get(key); if (value instanceof String) length += EncodedStringCache.getEncodedString((String) value).length(); else if (value instanceof Map) length += preencodeMap((Map<String, Object>) value, depth + 1, cc); else if (value instanceof List) length += preencodeArray((List<?>) value, depth + 1, cc); else if (value instanceof Object[]) length += preencodeArray((Object[]) value, depth + 1, cc); else length += lengthOf(value, cc); } if (depth == 0) return length; else return 1 + lengthOfRawNumber(int.class, length) + length; } public static Map<String, Object> decodeMap(ByteBuffer bb) { return decodeMap(bb, null); } public static Map<String, Object> decodeMap(ByteBuffer bb, CustomCodec cc) { byte type = bb.get(); if (type != MAP_TYPE) throw new TypeMismatchException(MAP_TYPE, type, bb.position() - 1); int length = (int) decodeRawNumber(bb); HashMap<String, Object> m = new HashMap<String, Object>(); while (length > 0) { int before = bb.remaining(); String key = decodeString(bb); Object value = decode(bb, cc); int after = bb.remaining(); m.put(key, value); length -= before - after; } return m; } public static void encodeArray(ByteBuffer bb, List<?> array) { encodeArray(bb, array, null); } public static void encodeArray(ByteBuffer bb, List<?> array, CustomCodec cc) { bb.put(ARRAY_TYPE); int length = preencodeArray(array, 0, cc); encodeRawNumber(bb, int.class, length); for (Object obj : array) { if (obj instanceof String) { EncodedStringCache es = EncodedStringCache.getEncodedString((String) obj); bb.put(STRING_TYPE); encodeRawNumber(bb, int.class, es.value().length); bb.put(es.value()); } else encode(bb, obj, cc); } } public static void encodeArray(ByteBuffer bb, int[] array) { bb.put(ARRAY_TYPE); int contentLength = 0; for (int i : array) contentLength += lengthOfInt(i); encodeRawNumber(bb, int.class, contentLength); for (int i : array) encodeInt(bb, i); } public static void encodeArray(ByteBuffer bb, long[] array) { bb.put(ARRAY_TYPE); int contentLength = 0; for (long i : array) contentLength += lengthOfLong(i); encodeRawNumber(bb, int.class, contentLength); for (long i : array) encodeLong(bb, i); } public static void encodeArray(ByteBuffer bb, short[] array) { bb.put(ARRAY_TYPE); int contentLength = 0; for (short i : array) contentLength += lengthOfShort(i); encodeRawNumber(bb, int.class, contentLength); for (short i : array) encodeShort(bb, i); } public static void encodeArray(ByteBuffer bb, double[] array) { bb.put(ARRAY_TYPE); int contentLength = 0; for (double i : array) contentLength += lengthOfDouble(i); encodeRawNumber(bb, int.class, contentLength); for (double i : array) encodeDouble(bb, i); } public static void encodeArray(ByteBuffer bb, float[] array) { bb.put(ARRAY_TYPE); int contentLength = 0; for (float i : array) contentLength += lengthOfFloat(i); encodeRawNumber(bb, int.class, contentLength); for (float i : array) encodeFloat(bb, i); } public static void encodeArray(ByteBuffer bb, boolean[] array) { bb.put(ARRAY_TYPE); int contentLength = 0; for (boolean i : array) contentLength += lengthOfBoolean(i); encodeRawNumber(bb, int.class, contentLength); for (boolean i : array) encodeBoolean(bb, i); } /** * preencodeArray track down recursively and sum all content length * * @return content length except type and length bytes */ @SuppressWarnings("unchecked") private static int preencodeArray(List<?> array, int depth, CustomCodec cc) { int length = 0; for (Object object : array) { if (object instanceof String) length += EncodedStringCache.getEncodedString((String) object).length(); else if (object instanceof Map) length += preencodeMap((Map<String, Object>) object, depth + 1, cc); else if (object instanceof List) length += preencodeArray((List<?>) object, depth + 1, cc); else if (object instanceof Object[]) length += preencodeArray((Object[]) object, depth + 1, cc); else length += lengthOf(object, cc); } if (depth == 0) return length; else return 1 + lengthOfRawNumber(int.class, length) + length; } public static void encodeArray(ByteBuffer bb, Object[] array) { encodeArray(bb, array, null); } public static void encodeArray(ByteBuffer bb, Object[] array, CustomCodec cc) { encodeArray(bb, Arrays.asList(array), cc); } private static int preencodeArray(Object[] array, int depth, CustomCodec cc) { return preencodeArray(Arrays.asList(array), depth, cc); } public static Object[] decodeArray(ByteBuffer bb) { return decodeArray(bb, null); } public static Object[] decodeArray(ByteBuffer bb, CustomCodec cc) { byte type = bb.get(); if (type != ARRAY_TYPE) throw new TypeMismatchException(ARRAY_TYPE, type, bb.position() - 1); int length = (int) decodeRawNumber(bb); ArrayList<Object> l = new ArrayList<Object>(); while (length > 0) { int before = bb.remaining(); l.add(decode(bb, cc)); int after = bb.remaining(); length -= before - after; } return l.toArray(); } public static void encodeBlob(ByteBuffer bb, byte[] buffer) { bb.put(BLOB_TYPE); encodeRawNumber(bb, int.class, buffer.length); bb.put(buffer); } public static byte[] decodeBlob(ByteBuffer bb) { byte type = bb.get(); if (type != BLOB_TYPE) throw new TypeMismatchException(BLOB_TYPE, type, bb.position() - 1); int length = (int) decodeRawNumber(bb); byte[] blob = new byte[length]; bb.get(blob); return blob; } public static void encodeFloat(ByteBuffer bb, float value) { bb.put(FLOAT_TYPE); int v = Float.floatToIntBits(value); byte[] b = new byte[4]; for (int i = 3; i >= 0; i--) { b[i] = (byte) (v & 0xFF); v >>= 8; } bb.put(b); } public static float decodeFloat(ByteBuffer bb) { byte type = bb.get(); if (type != FLOAT_TYPE) throw new TypeMismatchException(FLOAT_TYPE, type, bb.position() - 1); byte[] b = new byte[4]; bb.get(b); int v = 0; for (int i = 0; i < 4; i++) { v <<= 8; v |= b[i] & 0xFF; } return Float.intBitsToFloat(v); } public static void encodeDouble(ByteBuffer bb, double value) { bb.put(DOUBLE_TYPE); long v = Double.doubleToLongBits(value); byte[] b = new byte[8]; for (int i = 7; i >= 0; i--) { b[i] = (byte) (v & 0xFF); v >>= 8; } bb.put(b); } public static double decodeDouble(ByteBuffer bb) { byte type = bb.get(); if (type != DOUBLE_TYPE) throw new TypeMismatchException(DOUBLE_TYPE, type, bb.position() - 1); byte[] b = new byte[8]; bb.get(b); long v = 0; for (int i = 0; i < 8; i++) { v <<= 8; v |= b[i] & 0xFF; } return Double.longBitsToDouble(v); } public static int lengthOfLong(long value) { long zvalue = (value << 1) ^ (value >> 63); return 1 + lengthOfRawNumber(long.class, zvalue); } public static <T> int lengthOfRawNumber(Class<T> clazz, long value) { if (value < 0) { if (long.class == clazz) return 10; // max length for long else if (int.class == clazz) return 5; // max length for int else return 3; // max length for short } else { if (value <= 127) return 1; if (value <= 16383) return 2; } return (63 - Long.numberOfLeadingZeros(value)) / 7 + 1; } public static <T> int lengthOfNumber(Class<T> clazz, long value) { if (clazz.equals(int.class)) { return lengthOfInt((int) value); } else if (clazz.equals(long.class)) { return lengthOfLong(value); } else if (clazz.equals(short.class)) { return lengthOfShort((short) value); } else { throw new UnsupportedTypeException("invalid number type: " + clazz.getName()); } } public static int lengthOfInt(int value) { int zvalue = (value << 1) ^ (value >> 31); return 1 + lengthOfRawNumber(int.class, zvalue); } public static int lengthOfNull() { return 1; } public static int lengthOfShort(short value) { short zvalue = (short) ((value << 1) ^ (value >> 15)); return 1 + lengthOfRawNumber(short.class, zvalue); } public static int lengthOfString(String value) { byte[] buffer = null; try { buffer = value.getBytes("utf-8"); } catch (UnsupportedEncodingException e) { } return 1 + lengthOfRawNumber(int.class, buffer.length) + buffer.length; } public static int lengthOfDate() { return 1 + 8; } public static int lengthOfBoolean(boolean value) { return 2; } public static int lengthOfIp4(Inet4Address value) { return 1 + value.getAddress().length; } public static int lengthOfIp6(Inet6Address value) { return 1 + value.getAddress().length; } public static int lengthOfMap(Map<String, Object> value) { return lengthOfMap(value, null); } public static int lengthOfMap(Map<String, Object> value, CustomCodec cc) { int contentLength = 0; for (String key : value.keySet()) { contentLength += lengthOfString(key); contentLength += lengthOf(value.get(key), cc); } return 1 + lengthOfRawNumber(int.class, contentLength) + contentLength; } public static int lengthOfArray(List<?> value) { return lengthOfArray(value, null); } public static int lengthOfArray(int[] value) { int contentLength = 0; for (int obj : value) { contentLength += lengthOfInt(obj); } return 1 + lengthOfRawNumber(int.class, contentLength) + contentLength; } public static int lengthOfArray(long[] value) { int contentLength = 0; for (long obj : value) { contentLength += lengthOfLong(obj); } return 1 + lengthOfRawNumber(int.class, contentLength) + contentLength; } public static int lengthOfArray(short[] value) { int contentLength = 0; for (short obj : value) { contentLength += lengthOfShort(obj); } return 1 + lengthOfRawNumber(int.class, contentLength) + contentLength; } public static int lengthOfArray(boolean[] value) { int contentLength = 0; for (boolean obj : value) { contentLength += lengthOfBoolean(obj); } return 1 + lengthOfRawNumber(int.class, contentLength) + contentLength; } public static int lengthOfArray(double[] value) { int contentLength = 0; for (double obj : value) { contentLength += lengthOfDouble(obj); } return 1 + lengthOfRawNumber(int.class, contentLength) + contentLength; } public static int lengthOfArray(float[] value) { int contentLength = 0; for (float obj : value) { contentLength += lengthOfFloat(obj); } return 1 + lengthOfRawNumber(int.class, contentLength) + contentLength; } public static int lengthOfArray(List<?> value, CustomCodec cc) { int contentLength = 0; for (Object obj : value) { contentLength += lengthOf(obj, cc); } return 1 + lengthOfRawNumber(int.class, contentLength) + contentLength; } public static int lengthOfArray(Object[] value) { return lengthOfArray(value, null); } public static int lengthOfArray(Object[] value, CustomCodec cc) { return lengthOfArray(Arrays.asList(value), cc); } public static int lengthOfBlob(byte[] value) { return 1 + lengthOfRawNumber(int.class, value.length) + value.length; } public static int lengthOfFloat(float value) { return 1 + 4; } public static int lengthOfDouble(double value) { return 1 + 8; } }