package org.infinispan.marshall.core;
import java.io.EOFException;
import java.io.IOException;
import org.jboss.marshalling.util.IdentityIntMap;
final class Primitives {
// 0x03-0x0F reserved
static final int ID_BYTE_ARRAY = 0x01; // byte[].class
static final int ID_STRING = 0x02; // String.class
static final int ID_BOOLEAN_OBJ = 0x10; // Boolean.class
static final int ID_BYTE_OBJ = 0x11; // ..etc..
static final int ID_CHAR_OBJ = 0x12;
static final int ID_DOUBLE_OBJ = 0x13;
static final int ID_FLOAT_OBJ = 0x14;
static final int ID_INT_OBJ = 0x15;
static final int ID_LONG_OBJ = 0x16;
static final int ID_SHORT_OBJ = 0x17;
static final int ID_BOOLEAN_ARRAY = 0x18; // boolean[].class
static final int ID_CHAR_ARRAY = 0x19; // ..etc..
static final int ID_DOUBLE_ARRAY = 0x1A;
static final int ID_FLOAT_ARRAY = 0x1B;
static final int ID_INT_ARRAY = 0x1C;
static final int ID_LONG_ARRAY = 0x1D;
static final int ID_SHORT_ARRAY = 0x1E;
// 0x1F-0x27 unused
static final int ID_ARRAY_EMPTY = 0x28; // zero elements
static final int ID_ARRAY_SMALL = 0x29; // <=0x100 elements
static final int ID_ARRAY_MEDIUM = 0x2A; // <=0x10000 elements
static final int ID_ARRAY_LARGE = 0x2B; // <0x80000000 elements
static final int SMALL_ARRAY_MIN = 0x1;
static final int SMALL_ARRAY_MAX = 0x100;
static final int MEDIUM_ARRAY_MIN = 0x101;
static final int MEDIUM_ARRAY_MAX = 0x10100;
static final IdentityIntMap<Class<?>> PRIMITIVES = new IdentityIntMap<>(0x0.6p0f);
static {
PRIMITIVES.put(String.class, ID_STRING);
PRIMITIVES.put(byte[].class, ID_BYTE_ARRAY);
PRIMITIVES.put(Boolean.class, ID_BOOLEAN_OBJ);
PRIMITIVES.put(Byte.class, ID_BYTE_OBJ);
PRIMITIVES.put(Character.class, ID_CHAR_OBJ);
PRIMITIVES.put(Double.class, ID_DOUBLE_OBJ);
PRIMITIVES.put(Float.class, ID_FLOAT_OBJ);
PRIMITIVES.put(Integer.class, ID_INT_OBJ);
PRIMITIVES.put(Long.class, ID_LONG_OBJ);
PRIMITIVES.put(Short.class, ID_SHORT_OBJ);
PRIMITIVES.put(boolean[].class, ID_BOOLEAN_ARRAY);
PRIMITIVES.put(char[].class, ID_CHAR_ARRAY);
PRIMITIVES.put(double[].class, ID_DOUBLE_ARRAY);
PRIMITIVES.put(float[].class, ID_FLOAT_ARRAY);
PRIMITIVES.put(int[].class, ID_INT_ARRAY);
PRIMITIVES.put(long[].class, ID_LONG_ARRAY);
PRIMITIVES.put(short[].class, ID_SHORT_ARRAY);
}
private Primitives() {
}
static void writePrimitive(Object obj, BytesObjectOutput out, int id) throws IOException {
out.writeByte(id);
writeRawPrimitive(obj, out, id);
}
static void writeRawPrimitive(Object obj, BytesObjectOutput out, int id) throws IOException {
switch (id) {
case ID_BYTE_ARRAY:
Primitives.writeByteArray((byte[]) obj, out);
break;
case ID_STRING:
out.writeString((String) obj);
break;
case ID_BOOLEAN_OBJ:
out.writeBoolean((boolean) obj);
break;
case ID_BYTE_OBJ:
out.writeByte((byte) obj);
break;
case ID_CHAR_OBJ:
out.writeChar((char) obj);
break;
case ID_DOUBLE_OBJ:
out.writeDouble((double) obj);
break;
case ID_FLOAT_OBJ:
out.writeFloat((float) obj);
break;
case ID_INT_OBJ:
out.writeInt((int) obj);
break;
case ID_LONG_OBJ:
out.writeLong((long) obj);
break;
case ID_SHORT_OBJ:
out.writeShort((short) obj);
break;
case ID_BOOLEAN_ARRAY:
Primitives.writeBooleanArray((boolean[]) obj, out);
break;
case ID_CHAR_ARRAY:
Primitives.writeCharArray((char[]) obj, out);
break;
case ID_DOUBLE_ARRAY:
Primitives.writeDoubleArray((double[]) obj, out);
break;
case ID_FLOAT_ARRAY:
Primitives.writeFloatArray((float[]) obj, out);
break;
case ID_INT_ARRAY:
Primitives.writeIntArray((int[]) obj, out);
break;
case ID_LONG_ARRAY:
Primitives.writeLongArray((long[]) obj, out);
break;
case ID_SHORT_ARRAY:
Primitives.writeShortArray((short[]) obj, out);
break;
default:
throw new IOException("Unknown primitive type: " + obj);
}
}
static Object readPrimitive(BytesObjectInput in) throws IOException, ClassNotFoundException {
int subId = in.readUnsignedByte();
return readRawPrimitive(in, subId);
}
static Object readRawPrimitive(BytesObjectInput in, int subId) throws IOException {
switch (subId) {
case ID_BYTE_ARRAY:
return readByteArray(in);
case ID_STRING:
return in.readString();
case ID_BOOLEAN_OBJ:
return in.readBoolean();
case ID_BYTE_OBJ:
return in.readByte();
case ID_CHAR_OBJ:
return in.readChar();
case ID_DOUBLE_OBJ:
return in.readDouble();
case ID_FLOAT_OBJ:
return in.readFloat();
case ID_INT_OBJ:
return in.readInt();
case ID_LONG_OBJ:
return in.readLong();
case ID_SHORT_OBJ:
return in.readShort();
case ID_BOOLEAN_ARRAY:
return readBooleanArray(in);
case ID_CHAR_ARRAY:
return readCharArray(in);
case ID_DOUBLE_ARRAY:
return readDoubleArray(in);
case ID_FLOAT_ARRAY:
return readFloatArray(in);
case ID_INT_ARRAY:
return readIntArray(in);
case ID_LONG_ARRAY:
return readLongArray(in);
case ID_SHORT_ARRAY:
return readShortArray(in);
default:
throw new IOException("Unknown primitive sub id: " + Integer.toHexString(subId));
}
}
private static void writeByteArray(byte[] obj, BytesObjectOutput out) {
final int len = obj.length;
if (len == 0) {
out.writeByte(ID_ARRAY_EMPTY);
} else if (len <= SMALL_ARRAY_MAX) {
out.writeByte(ID_ARRAY_SMALL);
out.writeByte(len - SMALL_ARRAY_MIN);
out.write(obj, 0, len);
} else if (len <= MEDIUM_ARRAY_MAX) {
out.writeByte(ID_ARRAY_MEDIUM);
out.writeShort(len - MEDIUM_ARRAY_MIN);
out.write(obj, 0, len);
} else {
out.writeByte(ID_ARRAY_LARGE);
out.writeInt(len);
out.write(obj, 0, len);
}
}
private static void writeBooleanArray(boolean[] obj, BytesObjectOutput out) {
final int len = obj.length;
if (len == 0) {
out.writeByte(ID_ARRAY_EMPTY);
} else if (len <= SMALL_ARRAY_MAX) {
out.writeByte(ID_ARRAY_SMALL);
out.writeByte(len - SMALL_ARRAY_MIN);
writeBooleans(obj, out);
} else if (len <= MEDIUM_ARRAY_MAX) {
out.writeByte(ID_ARRAY_MEDIUM);
out.writeShort(len - MEDIUM_ARRAY_MIN);
writeBooleans(obj, out);
} else {
out.writeByte(ID_ARRAY_LARGE);
out.writeInt(len);
writeBooleans(obj, out);
}
}
private static void writeBooleans(boolean[] obj, BytesObjectOutput out) {
final int len = obj.length;
final int bc = len & ~7;
for (int i = 0; i < bc;) {
out.write(
(obj[i++] ? 1 : 0)
| (obj[i++] ? 2 : 0)
| (obj[i++] ? 4 : 0)
| (obj[i++] ? 8 : 0)
| (obj[i++] ? 16 : 0)
| (obj[i++] ? 32 : 0)
| (obj[i++] ? 64 : 0)
| (obj[i++] ? 128 : 0)
);
}
if (bc < len) {
int o = 0;
int bit = 1;
for (int i = bc; i < len; i++) {
if (obj[i]) o |= bit;
bit <<= 1;
}
out.writeByte(o);
}
}
private static void writeCharArray(char[] obj, BytesObjectOutput out) {
final int len = obj.length;
if (len == 0) {
out.writeByte(ID_ARRAY_EMPTY);
} else if (len <= SMALL_ARRAY_MAX) {
out.writeByte(ID_ARRAY_SMALL);
out.writeByte(len - SMALL_ARRAY_MIN);
for (char v : obj) out.writeChar(v);
} else if (len <= MEDIUM_ARRAY_MAX) {
out.writeByte(ID_ARRAY_MEDIUM);
out.writeShort(len - MEDIUM_ARRAY_MIN);
for (char v : obj) out.writeChar(v);
} else {
out.writeByte(ID_ARRAY_LARGE);
out.writeInt(len);
for (char v : obj) out.writeChar(v);
}
}
private static void writeDoubleArray(double[] obj, BytesObjectOutput out) {
final int len = obj.length;
if (len == 0) {
out.writeByte(ID_ARRAY_EMPTY);
} else if (len <= SMALL_ARRAY_MAX) {
out.writeByte(ID_ARRAY_SMALL);
out.writeByte(len - SMALL_ARRAY_MIN);
for (double v : obj) out.writeDouble(v);
} else if (len <= MEDIUM_ARRAY_MAX) {
out.writeByte(ID_ARRAY_MEDIUM);
out.writeShort(len - MEDIUM_ARRAY_MIN);
for (double v : obj) out.writeDouble(v);
} else {
out.writeByte(ID_ARRAY_LARGE);
out.writeInt(len);
for (double v : obj) out.writeDouble(v);
}
}
private static void writeFloatArray(float[] obj, BytesObjectOutput out) {
final int len = obj.length;
if (len == 0) {
out.writeByte(ID_ARRAY_EMPTY);
} else if (len <= SMALL_ARRAY_MAX) {
out.writeByte(ID_ARRAY_SMALL);
out.writeByte(len - SMALL_ARRAY_MIN);
for (float v : obj) out.writeFloat(v);
} else if (len <= MEDIUM_ARRAY_MAX) {
out.writeByte(ID_ARRAY_MEDIUM);
out.writeShort(len - MEDIUM_ARRAY_MIN);
for (float v : obj) out.writeFloat(v);
} else {
out.writeByte(ID_ARRAY_LARGE);
out.writeInt(len);
for (float v : obj) out.writeFloat(v);
}
}
private static void writeIntArray(int[] obj, BytesObjectOutput out) {
final int len = obj.length;
if (len == 0) {
out.writeByte(ID_ARRAY_EMPTY);
} else if (len <= SMALL_ARRAY_MAX) {
out.writeByte(ID_ARRAY_SMALL);
out.writeByte(len - SMALL_ARRAY_MIN);
for (int v : obj) out.writeInt(v);
} else if (len <= MEDIUM_ARRAY_MAX) {
out.writeByte(ID_ARRAY_MEDIUM);
out.writeShort(len - MEDIUM_ARRAY_MIN);
for (int v : obj) out.writeInt(v);
} else {
out.writeByte(ID_ARRAY_LARGE);
out.writeInt(len);
for (int v : obj) out.writeInt(v);
}
}
private static void writeLongArray(long[] obj, BytesObjectOutput out) {
final int len = obj.length;
if (len == 0) {
out.writeByte(ID_ARRAY_EMPTY);
} else if (len <= SMALL_ARRAY_MAX) {
out.writeByte(ID_ARRAY_SMALL);
out.writeByte(len - SMALL_ARRAY_MIN);
for (long v : obj) out.writeLong(v);
} else if (len <= MEDIUM_ARRAY_MAX) {
out.writeByte(ID_ARRAY_MEDIUM);
out.writeShort(len - MEDIUM_ARRAY_MIN);
for (long v : obj) out.writeLong(v);
} else {
out.writeByte(ID_ARRAY_LARGE);
out.writeInt(len);
for (long v : obj) out.writeLong(v);
}
}
private static void writeShortArray(short[] obj, BytesObjectOutput out) {
final int len = obj.length;
if (len == 0) {
out.writeByte(ID_ARRAY_EMPTY);
} else if (len <= SMALL_ARRAY_MAX) {
out.writeByte(ID_ARRAY_SMALL);
out.writeByte(len - SMALL_ARRAY_MIN);
for (short v : obj) out.writeShort(v);
} else if (len <= MEDIUM_ARRAY_MAX) {
out.writeByte(ID_ARRAY_MEDIUM);
out.writeShort(len - MEDIUM_ARRAY_MIN);
for (short v : obj) out.writeShort(v);
} else {
out.writeByte(ID_ARRAY_LARGE);
out.writeInt(len);
for (short v : obj) out.writeShort(v);
}
}
private static byte[] readByteArray(BytesObjectInput in) throws IOException {
byte type = in.readByte();
switch (type) {
case ID_ARRAY_EMPTY:
return new byte[]{};
case ID_ARRAY_SMALL:
return readFully(mkByteArray(in.readUnsignedByte() + SMALL_ARRAY_MIN), in);
case ID_ARRAY_MEDIUM:
return readFully(mkByteArray(in.readUnsignedShort() + MEDIUM_ARRAY_MIN), in);
case ID_ARRAY_LARGE:
return readFully(new byte[in.readInt()], in);
default:
throw new IOException("Unknown array type: " + Integer.toHexString(type));
}
}
private static byte[] mkByteArray(int len) {
return new byte[len];
}
private static byte[] readFully(byte[] arr, BytesObjectInput in) {
in.readFully(arr);
return arr;
}
private static boolean[] readBooleanArray(BytesObjectInput in) throws IOException {
byte type = in.readByte();
switch (type) {
case ID_ARRAY_EMPTY:
return new boolean[]{};
case ID_ARRAY_SMALL:
return readBooleans(mkBooleanArray(in.readUnsignedByte() + SMALL_ARRAY_MIN), in);
case ID_ARRAY_MEDIUM:
return readBooleans(mkBooleanArray(in.readUnsignedShort() + MEDIUM_ARRAY_MIN), in);
case ID_ARRAY_LARGE:
return readBooleans(new boolean[in.readInt()], in);
default:
throw new IOException("Unknown array type: " + Integer.toHexString(type));
}
}
private static boolean[] mkBooleanArray(int len) {
return new boolean[len];
}
private static boolean[] readBooleans(boolean[] arr, BytesObjectInput in) throws EOFException {
final int len = arr.length;
int v;
int bc = len & ~7;
for (int i = 0; i < bc; ) {
v = in.readByte();
arr[i++] = (v & 1) != 0;
arr[i++] = (v & 2) != 0;
arr[i++] = (v & 4) != 0;
arr[i++] = (v & 8) != 0;
arr[i++] = (v & 16) != 0;
arr[i++] = (v & 32) != 0;
arr[i++] = (v & 64) != 0;
arr[i++] = (v & 128) != 0;
}
if (bc < len) {
v = in.readByte();
switch (len & 7) {
case 7:
arr[bc + 6] = (v & 64) != 0;
case 6:
arr[bc + 5] = (v & 32) != 0;
case 5:
arr[bc + 4] = (v & 16) != 0;
case 4:
arr[bc + 3] = (v & 8) != 0;
case 3:
arr[bc + 2] = (v & 4) != 0;
case 2:
arr[bc + 1] = (v & 2) != 0;
case 1:
arr[bc] = (v & 1) != 0;
}
}
return arr;
}
private static char[] readCharArray(BytesObjectInput in) throws IOException {
byte type = in.readByte();
switch (type) {
case ID_ARRAY_EMPTY:
return new char[]{};
case ID_ARRAY_SMALL:
return readChars(mkCharArray(in.readUnsignedByte() + SMALL_ARRAY_MIN), in);
case ID_ARRAY_MEDIUM:
return readChars(mkCharArray(in.readUnsignedShort() + MEDIUM_ARRAY_MIN), in);
case ID_ARRAY_LARGE:
return readChars(new char[in.readInt()], in);
default:
throw new IOException("Unknown array type: " + Integer.toHexString(type));
}
}
private static char[] mkCharArray(int len) {
return new char[len];
}
private static char[] readChars(char[] arr, BytesObjectInput in) {
final int len = arr.length;
for (int i = 0; i < len; i ++) arr[i] = in.readChar();
return arr;
}
private static double[] readDoubleArray(BytesObjectInput in) throws IOException {
byte type = in.readByte();
switch (type) {
case ID_ARRAY_EMPTY:
return new double[]{};
case ID_ARRAY_SMALL:
return readDoubles(new double[in.readUnsignedByte() + SMALL_ARRAY_MIN], in);
case ID_ARRAY_MEDIUM:
return readDoubles(new double[in.readUnsignedShort() + MEDIUM_ARRAY_MIN], in);
case ID_ARRAY_LARGE:
return readDoubles(new double[in.readInt()], in);
default:
throw new IOException("Unknown array type: " + Integer.toHexString(type));
}
}
private static double[] readDoubles(double[] arr, BytesObjectInput in) {
final int len = arr.length;
for (int i = 0; i < len; i ++) arr[i] = in.readDouble();
return arr;
}
private static float[] readFloatArray(BytesObjectInput in) throws IOException {
byte type = in.readByte();
switch (type) {
case ID_ARRAY_EMPTY:
return new float[]{};
case ID_ARRAY_SMALL:
return readFloats(new float[in.readUnsignedByte() + SMALL_ARRAY_MIN], in);
case ID_ARRAY_MEDIUM:
return readFloats(new float[in.readUnsignedShort() + MEDIUM_ARRAY_MIN], in);
case ID_ARRAY_LARGE:
return readFloats(new float[in.readInt()], in);
default:
throw new IOException("Unknown array type: " + Integer.toHexString(type));
}
}
private static float[] readFloats(float[] arr, BytesObjectInput in) {
final int len = arr.length;
for (int i = 0; i < len; i ++) arr[i] = in.readFloat();
return arr;
}
private static int[] readIntArray(BytesObjectInput in) throws IOException {
byte type = in.readByte();
switch (type) {
case ID_ARRAY_EMPTY:
return new int[]{};
case ID_ARRAY_SMALL:
return readInts(new int[in.readUnsignedByte() + SMALL_ARRAY_MIN], in);
case ID_ARRAY_MEDIUM:
return readInts(new int[in.readUnsignedShort() + MEDIUM_ARRAY_MIN], in);
case ID_ARRAY_LARGE:
return readInts(new int[in.readInt()], in);
default:
throw new IOException("Unknown array type: " + Integer.toHexString(type));
}
}
private static int[] readInts(int[] arr, BytesObjectInput in) {
final int len = arr.length;
for (int i = 0; i < len; i ++) arr[i] = in.readInt();
return arr;
}
private static long[] readLongArray(BytesObjectInput in) throws IOException {
byte type = in.readByte();
switch (type) {
case ID_ARRAY_EMPTY:
return new long[]{};
case ID_ARRAY_SMALL:
return readLongs(new long[in.readUnsignedByte() + SMALL_ARRAY_MIN], in);
case ID_ARRAY_MEDIUM:
return readLongs(new long[in.readUnsignedShort() + MEDIUM_ARRAY_MIN], in);
case ID_ARRAY_LARGE:
return readLongs(new long[in.readInt()], in);
default:
throw new IOException("Unknown array type: " + Integer.toHexString(type));
}
}
private static long[] readLongs(long[] arr, BytesObjectInput in) {
final int len = arr.length;
for (int i = 0; i < len; i ++) arr[i] = in.readLong();
return arr;
}
private static short[] readShortArray(BytesObjectInput in) throws IOException {
byte type = in.readByte();
switch (type) {
case ID_ARRAY_EMPTY:
return new short[]{};
case ID_ARRAY_SMALL:
return readShorts(new short[in.readUnsignedByte() + SMALL_ARRAY_MIN], in);
case ID_ARRAY_MEDIUM:
return readShorts(new short[in.readUnsignedShort() + MEDIUM_ARRAY_MIN], in);
case ID_ARRAY_LARGE:
return readShorts(new short[in.readInt()], in);
default:
throw new IOException("Unknown array type: " + Integer.toHexString(type));
}
}
private static short[] readShorts(short[] arr, BytesObjectInput in) {
final int len = arr.length;
for (int i = 0; i < len; i ++) arr[i] = in.readShort();
return arr;
}
}