/*
* Copyright 2013 Future Systems
*
* 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.net.Inet4Address;
import java.net.Inet6Address;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class FastEncodingRule {
public byte[] encodeRawNumber(Class<?> clazz, long value) {
int len = EncodingRule.lengthOfRawNumber(clazz, value);
byte[] b = new byte[len];
// fast path
if (len == 1) {
b[0] = (byte) value;
return b;
}
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));
b[i] = data;
}
return b;
}
public ByteBuffer encode(Object value) {
return encode(value, null);
}
public ByteBuffer encode(Object value, FastCustomCodec cc) {
BinaryForm bf = preencode(value, cc);
ByteBuffer bb = ByteBuffer.allocate(bf.totalLength);
encode(bb, bf);
bb.flip();
return bb;
}
public void encode(ByteBuffer bb, BinaryForm bf) {
bb.put((byte) bf.type);
switch (bf.type) {
case EncodingRule.NULL_TYPE:
break;
case EncodingRule.STRING_TYPE:
case EncodingRule.BLOB_TYPE:
bb.put(bf.lengthBytes);
bb.put(bf.payloadBytes);
break;
case EncodingRule.ZINT16_TYPE:
case EncodingRule.ZINT32_TYPE:
case EncodingRule.ZINT64_TYPE:
case EncodingRule.DATE_TYPE:
case EncodingRule.IP4_TYPE:
case EncodingRule.IP6_TYPE:
case EncodingRule.BOOLEAN_TYPE:
case EncodingRule.FLOAT_TYPE:
case EncodingRule.DOUBLE_TYPE:
bb.put(bf.payloadBytes);
break;
case EncodingRule.MAP_TYPE:
case EncodingRule.ARRAY_TYPE:
bb.put(bf.lengthBytes);
for (BinaryForm c : bf.children)
encode(bb, c);
break;
default:
bb.put(bf.lengthBytes);
if (bf.payloadBytes != null) {
bb.put(bf.payloadBytes);
} else if (bf.children != null) {
for (BinaryForm c : bf.children)
encode(bb, c);
}
}
}
public BinaryForm preencode(Object value) {
return preencode(value, null);
}
public BinaryForm preencode(Object value, FastCustomCodec cc) {
if (value == null) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.NULL_TYPE;
bf.totalLength = 1;
return bf;
} else if (value instanceof String) {
return preencodeString(value);
} else if (value instanceof Integer) {
return preencodeInt(value);
} else if (value instanceof Long) {
return preencodeLong((Long) value);
} else if (value instanceof Short) {
return preencodeShort((Short) value);
} else if (value instanceof Date) {
return preencodeDate((Date) value);
} else if (value instanceof Inet4Address) {
return preencodeIp4((Inet4Address) value);
} else if (value instanceof Inet6Address) {
return preencodeIp6((Inet6Address) value);
} else if (value instanceof Boolean) {
return preencodeBoolean((Boolean) value);
} else if (value instanceof Float) {
return preencodeFloat(value);
} else if (value instanceof Double) {
return preencodeDouble(value);
} else if (value instanceof Map<?, ?>) {
return preencodeMap(value, cc);
} else if (value instanceof List<?>) {
return preencodeArray((List<?>) value, cc);
} else if (value.getClass().isArray()) {
Class<?> c = value.getClass().getComponentType();
if (c == byte.class) {
return preencodeBlob((byte[]) value);
} else if (c == int.class) {
return preencodeArray((int[]) value);
} else if (c == long.class) {
return preencodeArray((long[]) value);
} else if (c == short.class) {
return preencodeArray((short[]) value);
} else if (c == boolean.class) {
return preencodeArray((boolean[]) value);
} else if (c == double.class) {
return preencodeArray((double[]) value);
} else if (c == float.class) {
return preencodeArray((float[]) value);
} else if (c == char.class) {
throw new UnsupportedTypeException(value.getClass().getName());
} else {
return preencodeArray((Object[]) value, cc);
}
} else {
if (cc != null)
return cc.preencode(this, value);
else
throw new UnsupportedTypeException(value.getClass().getName());
}
}
private BinaryForm preencodeIp6(Inet6Address value) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.IP6_TYPE;
bf.payloadBytes = ((Inet6Address) value).getAddress();
bf.totalLength = 1 + bf.payloadBytes.length;
bf.value = value;
return bf;
}
private BinaryForm preencodeBoolean(boolean value) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.BOOLEAN_TYPE;
bf.payloadBytes = new byte[] { (byte) ((Boolean) value ? 1 : 0) };
bf.totalLength = 2;
bf.value = value;
return bf;
}
private BinaryForm preencodeMap(Object value, FastCustomCodec cc) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) value;
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.MAP_TYPE;
bf.children = new BinaryForm[map.size() * 2];
int i = 0;
int payloadLength = 0;
for (Entry<String, Object> e : map.entrySet()) {
BinaryForm k = preencodeString(e.getKey());
BinaryForm v = preencode(e.getValue(), cc);
bf.children[i++] = k;
bf.children[i++] = v;
payloadLength += k.totalLength + v.totalLength;
}
bf.lengthBytes = encodeRawNumber(int.class, payloadLength);
bf.totalLength = 1 + bf.lengthBytes.length + payloadLength;
bf.value = value;
return bf;
}
private BinaryForm preencodeDouble(Object value) {
long v = Double.doubleToLongBits((Double) value);
byte[] b = new byte[8];
for (int i = 7; i >= 0; i--) {
b[i] = (byte) (v & 0xFF);
v >>= 8;
}
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.DOUBLE_TYPE;
bf.payloadBytes = b;
bf.totalLength = 1 + bf.payloadBytes.length;
bf.value = value;
return bf;
}
private BinaryForm preencodeFloat(Object value) {
int v = Float.floatToIntBits((Float) value);
byte[] b = new byte[4];
for (int i = 3; i >= 0; i--) {
b[i] = (byte) (v & 0xFF);
v >>= 8;
}
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.FLOAT_TYPE;
bf.payloadBytes = b;
bf.totalLength = 1 + bf.payloadBytes.length;
bf.value = value;
return bf;
}
private BinaryForm preencodeIp4(Inet4Address value) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.IP4_TYPE;
bf.payloadBytes = value.getAddress();
bf.totalLength = 1 + bf.payloadBytes.length;
bf.value = value;
return bf;
}
private BinaryForm preencodeInt(Object value) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ZINT32_TYPE;
int v = (Integer) value;
long zvalue = ((long) v << 1) ^ ((long) v >> 31);
bf.payloadBytes = encodeRawNumber(int.class, zvalue);
bf.totalLength = 1 + bf.payloadBytes.length;
bf.value = v;
return bf;
}
private BinaryForm preencodeLong(long value) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ZINT64_TYPE;
long v = (Long) value;
long zvalue = ((long) v << 1) ^ ((long) v >> 63);
bf.payloadBytes = encodeRawNumber(long.class, zvalue);
bf.totalLength = 1 + bf.payloadBytes.length;
bf.value = v;
return bf;
}
private BinaryForm preencodeDate(Date value) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.DATE_TYPE;
long l = ((Date) value).getTime();
byte[] b = new byte[8];
for (int i = 7; i >= 0; i--) {
b[i] = (byte) (l & 0xff);
l >>= 8;
}
bf.payloadBytes = b;
bf.totalLength = 1 + bf.payloadBytes.length;
bf.value = value;
return bf;
}
private BinaryForm preencodeShort(short value) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ZINT16_TYPE;
int v = (int) value;
long zvalue = ((long) v << 1) ^ ((long) v >> 31);
bf.payloadBytes = encodeRawNumber(short.class, zvalue);
bf.totalLength = 1 + bf.payloadBytes.length;
bf.value = v;
return bf;
}
private BinaryForm preencodeString(Object value) {
BinaryForm bf = new BinaryForm();
EncodedStringCache k = EncodedStringCache.getEncodedString((String) value);
bf.type = EncodingRule.STRING_TYPE;
bf.payloadBytes = k.value();
bf.lengthBytes = encodeRawNumber(int.class, bf.payloadBytes.length);
bf.totalLength = 1 + bf.lengthBytes.length + bf.payloadBytes.length;
bf.value = value;
return bf;
}
public BinaryForm preencodeBlob(byte[] b) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.BLOB_TYPE;
bf.payloadBytes = b;
bf.lengthBytes = encodeRawNumber(int.class, bf.payloadBytes.length);
bf.totalLength = 1 + bf.lengthBytes.length + bf.payloadBytes.length;
bf.value = b;
return bf;
}
public BinaryForm preencodeArray(Object[] array) {
return preencodeArray(array, null);
}
public BinaryForm preencodeArray(Object[] array, FastCustomCodec cc) {
return preencodeArray(Arrays.asList(array), cc);
}
public BinaryForm preencodeArray(List<?> array, FastCustomCodec cc) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ARRAY_TYPE;
bf.children = new BinaryForm[array.size()];
int payloadLength = 0;
int p = 0;
for (Object obj : array) {
BinaryForm c = preencode(obj, cc);
payloadLength += c.totalLength;
bf.children[p++] = c;
}
bf.lengthBytes = encodeRawNumber(int.class, payloadLength);
bf.totalLength = 1 + bf.lengthBytes.length + payloadLength;
bf.value = array;
return bf;
}
public BinaryForm preencodeArray(int[] array) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ARRAY_TYPE;
bf.children = new BinaryForm[array.length];
int p = 0;
int payloadLength = 0;
for (int i : array) {
BinaryForm c = preencodeInt(i);
bf.children[p++] = c;
payloadLength += c.totalLength;
}
bf.lengthBytes = encodeRawNumber(int.class, payloadLength);
bf.totalLength = 1 + bf.lengthBytes.length + payloadLength;
bf.value = array;
return bf;
}
public BinaryForm preencodeArray(long[] array) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ARRAY_TYPE;
bf.children = new BinaryForm[array.length];
int p = 0;
int payloadLength = 0;
for (long i : array) {
BinaryForm c = preencodeLong(i);
bf.children[p++] = c;
payloadLength += c.totalLength;
}
bf.lengthBytes = encodeRawNumber(int.class, payloadLength);
bf.totalLength = 1 + bf.lengthBytes.length + payloadLength;
bf.value = array;
return bf;
}
public BinaryForm preencodeArray(short[] array) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ARRAY_TYPE;
bf.children = new BinaryForm[array.length];
int p = 0;
int payloadLength = 0;
for (short i : array) {
BinaryForm c = preencodeShort(i);
bf.children[p++] = c;
payloadLength += c.totalLength;
}
bf.lengthBytes = encodeRawNumber(int.class, payloadLength);
bf.totalLength = 1 + bf.lengthBytes.length + payloadLength;
bf.value = array;
return bf;
}
public BinaryForm preencodeArray(double[] array) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ARRAY_TYPE;
bf.children = new BinaryForm[array.length];
int p = 0;
int payloadLength = 0;
for (double i : array) {
BinaryForm c = preencodeDouble(i);
bf.children[p++] = c;
payloadLength += c.totalLength;
}
bf.lengthBytes = encodeRawNumber(int.class, payloadLength);
bf.totalLength = 1 + bf.lengthBytes.length + payloadLength;
bf.value = array;
return bf;
}
public BinaryForm preencodeArray(float[] array) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ARRAY_TYPE;
bf.children = new BinaryForm[array.length];
int p = 0;
int payloadLength = 0;
for (float i : array) {
BinaryForm c = preencodeFloat(i);
bf.children[p++] = c;
payloadLength += c.totalLength;
}
bf.lengthBytes = encodeRawNumber(int.class, payloadLength);
bf.totalLength = 1 + bf.lengthBytes.length + payloadLength;
bf.value = array;
return bf;
}
public BinaryForm preencodeArray(boolean[] array) {
BinaryForm bf = new BinaryForm();
bf.type = EncodingRule.ARRAY_TYPE;
bf.children = new BinaryForm[array.length];
int p = 0;
int payloadLength = 0;
for (boolean i : array) {
BinaryForm c = preencodeBoolean(i);
bf.children[p++] = c;
payloadLength += c.totalLength;
}
bf.lengthBytes = encodeRawNumber(int.class, payloadLength);
bf.totalLength = 1 + bf.lengthBytes.length + payloadLength;
bf.value = array;
return bf;
}
}