PROTON-2057: fix small/array8 array encodings over 128 bytes
Changes split out from #34 by Ang Iongchun, plus tests from me (Robbie).
See also b666d6a165af7835bde12e0e41de2ca6109126d9.
This closes #34.
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/codec/ArrayType.java b/proton-j/src/main/java/org/apache/qpid/proton/codec/ArrayType.java
index 418a251..7d4de49 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/codec/ArrayType.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/codec/ArrayType.java
@@ -697,10 +697,10 @@
public void writeValue(final boolean[] a)
{
BooleanType.BooleanEncoding underlyingEncoder = getUnderlyingEncoding(a);
- byte encodedValueSize = (byte)(1 + underlyingEncoder.getConstructorSize() +
- a.length * underlyingEncoder.getValueSize(null));
+ int encodedValueSize = 1 + underlyingEncoder.getConstructorSize() +
+ a.length * underlyingEncoder.getValueSize(null);
getEncoder().getBuffer().ensureRemaining(encodedValueSize);
- getEncoder().writeRaw(encodedValueSize);
+ getEncoder().writeRaw((byte)encodedValueSize);
getEncoder().writeRaw((byte)a.length);
underlyingEncoder.writeConstructor();
for(boolean b : a)
@@ -713,10 +713,10 @@
public void writeValue(final byte[] a)
{
ByteType.ByteEncoding underlyingEncoder = getUnderlyingEncoding(a);
- byte encodedValueSize = (byte)(1 + underlyingEncoder.getConstructorSize() +
- a.length * underlyingEncoder.getValueSize(null));
+ int encodedValueSize = 1 + underlyingEncoder.getConstructorSize() +
+ a.length * underlyingEncoder.getValueSize(null);
getEncoder().getBuffer().ensureRemaining(encodedValueSize);
- getEncoder().writeRaw(encodedValueSize);
+ getEncoder().writeRaw((byte)encodedValueSize);
getEncoder().writeRaw((byte)a.length);
underlyingEncoder.writeConstructor();
for(byte b : a)
@@ -729,10 +729,10 @@
public void writeValue(final short[] a)
{
ShortType.ShortEncoding underlyingEncoder = getUnderlyingEncoding(a);
- byte encodedValueSize = (byte)(1 + underlyingEncoder.getConstructorSize() +
- a.length * underlyingEncoder.getValueSize(null));
+ int encodedValueSize = 1 + underlyingEncoder.getConstructorSize() +
+ a.length * underlyingEncoder.getValueSize(null);
getEncoder().getBuffer().ensureRemaining(encodedValueSize);
- getEncoder().writeRaw(encodedValueSize);
+ getEncoder().writeRaw((byte)encodedValueSize);
getEncoder().writeRaw((byte)a.length);
underlyingEncoder.writeConstructor();
for(short b : a)
@@ -745,10 +745,10 @@
public void writeValue(final int[] a)
{
IntegerType.IntegerEncoding underlyingEncoder = getUnderlyingEncoding(a);
- byte encodedValueSize = (byte)(1 + underlyingEncoder.getConstructorSize() +
- a.length * underlyingEncoder.getValueSize(null));
+ int encodedValueSize = 1 + underlyingEncoder.getConstructorSize() +
+ a.length * underlyingEncoder.getValueSize(null);
getEncoder().getBuffer().ensureRemaining(encodedValueSize);
- getEncoder().writeRaw(encodedValueSize);
+ getEncoder().writeRaw((byte)encodedValueSize);
getEncoder().writeRaw((byte)a.length);
underlyingEncoder.writeConstructor();
for(int b : a)
@@ -761,10 +761,10 @@
public void writeValue(final long[] a)
{
LongType.LongEncoding underlyingEncoder = getUnderlyingEncoding(a);
- byte encodedValueSize = (byte)(1 + underlyingEncoder.getConstructorSize() +
- a.length * underlyingEncoder.getValueSize(null));
+ int encodedValueSize = 1 + underlyingEncoder.getConstructorSize() +
+ a.length * underlyingEncoder.getValueSize(null);
getEncoder().getBuffer().ensureRemaining(encodedValueSize);
- getEncoder().writeRaw(encodedValueSize);
+ getEncoder().writeRaw((byte)encodedValueSize);
getEncoder().writeRaw((byte)a.length);
underlyingEncoder.writeConstructor();
for(long b : a)
@@ -777,10 +777,10 @@
public void writeValue(final float[] a)
{
FloatType.FloatEncoding underlyingEncoder = getUnderlyingEncoding(a);
- byte encodedValueSize = (byte)(1 + underlyingEncoder.getConstructorSize() +
- a.length * underlyingEncoder.getValueSize(null));
+ int encodedValueSize = 1 + underlyingEncoder.getConstructorSize() +
+ a.length * underlyingEncoder.getValueSize(null);
getEncoder().getBuffer().ensureRemaining(encodedValueSize);
- getEncoder().writeRaw(encodedValueSize);
+ getEncoder().writeRaw((byte)encodedValueSize);
getEncoder().writeRaw((byte)a.length);
underlyingEncoder.writeConstructor();
for(float b : a)
@@ -793,10 +793,10 @@
public void writeValue(final double[] a)
{
DoubleType.DoubleEncoding underlyingEncoder = getUnderlyingEncoding(a);
- byte encodedValueSize = (byte)(1 + underlyingEncoder.getConstructorSize() +
- a.length * underlyingEncoder.getValueSize(null));
+ int encodedValueSize = 1 + underlyingEncoder.getConstructorSize() +
+ a.length * underlyingEncoder.getValueSize(null);
getEncoder().getBuffer().ensureRemaining(encodedValueSize);
- getEncoder().writeRaw(encodedValueSize);
+ getEncoder().writeRaw((byte)encodedValueSize);
getEncoder().writeRaw((byte)a.length);
underlyingEncoder.writeConstructor();
for(double b : a)
@@ -809,10 +809,10 @@
public void writeValue(final char[] a)
{
CharacterType.CharacterEncoding underlyingEncoder = getUnderlyingEncoding(a);
- byte encodedValueSize = (byte)(1 + underlyingEncoder.getConstructorSize() +
- a.length * underlyingEncoder.getValueSize(null));
+ int encodedValueSize = 1 + underlyingEncoder.getConstructorSize() +
+ a.length * underlyingEncoder.getValueSize(null);
getEncoder().getBuffer().ensureRemaining(encodedValueSize);
- getEncoder().writeRaw(encodedValueSize);
+ getEncoder().writeRaw((byte)encodedValueSize);
getEncoder().writeRaw((byte)a.length);
underlyingEncoder.writeConstructor();
for(char b : a)
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/codec/ArrayTypeCodecTest.java b/proton-j/src/test/java/org/apache/qpid/proton/codec/ArrayTypeCodecTest.java
index 024534f..2b50723 100644
--- a/proton-j/src/test/java/org/apache/qpid/proton/codec/ArrayTypeCodecTest.java
+++ b/proton-j/src/test/java/org/apache/qpid/proton/codec/ArrayTypeCodecTest.java
@@ -308,8 +308,24 @@
}
@Test
- public void testEncodeDecodeByteArray() throws IOException {
- int count = 100;
+ public void testEncodeDecodeByteArray100() throws Throwable {
+ // byte array8 less than 128 bytes
+ doEncodeDecodeByteArrayTestImpl(100);
+ }
+
+ @Test
+ public void testEncodeDecodeByteArray192() throws Throwable {
+ // byte array8 greater than 128 bytes
+ doEncodeDecodeByteArrayTestImpl(192);
+ }
+
+ @Test
+ public void testEncodeDecodeByteArray384() throws Throwable {
+ // byte array32
+ doEncodeDecodeByteArrayTestImpl(384);
+ }
+
+ private void doEncodeDecodeByteArrayTestImpl(int count) throws Throwable {
byte[] source = createPayloadArrayBytes(count);
try {
@@ -380,4 +396,710 @@
return payload;
}
+
+ @Test
+ public void testEncodeDecodeBooleanArray100() throws Throwable {
+ // boolean array8 less than 128 bytes
+ doEncodeDecodeBooleanArrayTestImpl(100);
+ }
+
+ @Test
+ public void testEncodeDecodeBooleanArray192() throws Throwable {
+ // boolean array8 greater than 128 bytes
+ doEncodeDecodeBooleanArrayTestImpl(192);
+ }
+
+ @Test
+ public void testEncodeDecodeBooleanArray384() throws Throwable {
+ // boolean array32
+ doEncodeDecodeBooleanArrayTestImpl(384);
+ }
+
+ private void doEncodeDecodeBooleanArrayTestImpl(int count) throws Throwable {
+ boolean[] source = createPayloadArrayBooleans(count);
+
+ try {
+ assertEquals("Unexpected source array length", count, source.length);
+
+ int encodingWidth = count < 254 ? 1 : 4; // less than 254 and not 256, since we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ int arrayPayloadSize = encodingWidth + 1 + count; // variable width for element count + byte type descriptor + number of elements
+ int expectedEncodedArraySize = 1 + encodingWidth + arrayPayloadSize; // array type code + variable width for array size + other encoded payload
+ byte[] expectedEncoding = new byte[expectedEncodedArraySize];
+ ByteBuffer expectedEncodingWrapper = ByteBuffer.wrap(expectedEncoding);
+
+ // Write the array encoding code, array size, and element count
+ if(count < 254) {
+ expectedEncodingWrapper.put((byte) 0xE0); // 'array8' type descriptor code
+ expectedEncodingWrapper.put((byte) arrayPayloadSize);
+ expectedEncodingWrapper.put((byte) count);
+ } else {
+ expectedEncodingWrapper.put((byte) 0xF0); // 'array32' type descriptor code
+ expectedEncodingWrapper.putInt(arrayPayloadSize);
+ expectedEncodingWrapper.putInt(count);
+ }
+
+ // Write the type descriptor
+ expectedEncodingWrapper.put((byte) 0x56); // 'boolean' type descriptor code
+
+ // Write the elements
+ for (int i = 0; i < count; i++) {
+ byte booleanCode = (byte) (source[i] ? 0x01 : 0x00); // 0x01 true, 0x00 false.
+ expectedEncodingWrapper.put(booleanCode);
+ }
+
+ assertFalse("Should have filled expected encoding array", expectedEncodingWrapper.hasRemaining());
+
+ // Now verify against the actual encoding of the array
+ assertEquals("Unexpected buffer position", 0, buffer.position());
+ encoder.writeArray(source);
+ assertEquals("Unexpected encoded payload length", expectedEncodedArraySize, buffer.position());
+
+ byte[] actualEncoding = new byte[expectedEncodedArraySize];
+ buffer.flip();
+ buffer.get(actualEncoding);
+ assertFalse("Should have drained the encoder buffer contents", buffer.hasRemaining());
+
+ assertArrayEquals("Unexpected actual array encoding", expectedEncoding, actualEncoding);
+
+ // Now verify against the decoding
+ buffer.flip();
+ Object decoded = decoder.readObject();
+ assertNotNull(decoded);
+ assertTrue(decoded.getClass().isArray());
+ assertTrue(decoded.getClass().getComponentType().isPrimitive());
+ assertEquals(boolean.class, decoded.getClass().getComponentType());
+
+ assertArrayEquals("Unexpected decoding", source, (boolean[]) decoded);
+ }
+ catch (Throwable t) {
+ System.err.println("Error during test, source array: " + Arrays.toString(source));
+ throw t;
+ }
+ }
+
+ private static boolean[] createPayloadArrayBooleans(int length) {
+ Random rand = new Random(System.currentTimeMillis());
+
+ boolean[] payload = new boolean[length];
+ for (int i = 0; i < length; i++) {
+ payload[i] = rand.nextBoolean();
+ }
+
+ return payload;
+ }
+
+ @Test
+ public void testEncodeDecodeShortArray50() throws Throwable {
+ // short array8 less than 128 bytes
+ doEncodeDecodeShortArrayTestImpl(50);
+ }
+
+ @Test
+ public void testEncodeDecodeShortArray100() throws Throwable {
+ // short array8 greater than 128 bytes
+ doEncodeDecodeShortArrayTestImpl(100);
+ }
+
+ @Test
+ public void testEncodeDecodeShortArray384() throws Throwable {
+ // short array32
+ doEncodeDecodeShortArrayTestImpl(384);
+ }
+
+ private void doEncodeDecodeShortArrayTestImpl(int count) throws Throwable {
+ short[] source = createPayloadArrayShorts(count);
+
+ try {
+ assertEquals("Unexpected source array length", count, source.length);
+
+ int encodingWidth = count < 127 ? 1 : 4; // less than 127, since each element is 2 bytes, but we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ int arrayPayloadSize = encodingWidth + 1 + (count * 2); // variable width for element count + byte type descriptor + (number of elements * size)
+ int expectedEncodedArraySize = 1 + encodingWidth + arrayPayloadSize; // array type code + variable width for array size + other encoded payload
+ byte[] expectedEncoding = new byte[expectedEncodedArraySize];
+ ByteBuffer expectedEncodingWrapper = ByteBuffer.wrap(expectedEncoding);
+
+ // Write the array encoding code, array size, and element count
+ if(count < 254) {
+ expectedEncodingWrapper.put((byte) 0xE0); // 'array8' type descriptor code
+ expectedEncodingWrapper.put((byte) arrayPayloadSize);
+ expectedEncodingWrapper.put((byte) count);
+ } else {
+ expectedEncodingWrapper.put((byte) 0xF0); // 'array32' type descriptor code
+ expectedEncodingWrapper.putInt(arrayPayloadSize);
+ expectedEncodingWrapper.putInt(count);
+ }
+
+ // Write the type descriptor
+ expectedEncodingWrapper.put((byte) 0x61); // 'short' type descriptor code
+
+ // Write the elements
+ for (int i = 0; i < count; i++) {
+ expectedEncodingWrapper.putShort(source[i]);
+ }
+
+ assertFalse("Should have filled expected encoding array", expectedEncodingWrapper.hasRemaining());
+
+ // Now verify against the actual encoding of the array
+ assertEquals("Unexpected buffer position", 0, buffer.position());
+ encoder.writeArray(source);
+ assertEquals("Unexpected encoded payload length", expectedEncodedArraySize, buffer.position());
+
+ byte[] actualEncoding = new byte[expectedEncodedArraySize];
+ buffer.flip();
+ buffer.get(actualEncoding);
+ assertFalse("Should have drained the encoder buffer contents", buffer.hasRemaining());
+
+ assertArrayEquals("Unexpected actual array encoding", expectedEncoding, actualEncoding);
+
+ // Now verify against the decoding
+ buffer.flip();
+ Object decoded = decoder.readObject();
+ assertNotNull(decoded);
+ assertTrue(decoded.getClass().isArray());
+ assertTrue(decoded.getClass().getComponentType().isPrimitive());
+ assertEquals(short.class, decoded.getClass().getComponentType());
+
+ assertArrayEquals("Unexpected decoding", source, (short[]) decoded);
+ }
+ catch (Throwable t) {
+ System.err.println("Error during test, source array: " + Arrays.toString(source));
+ throw t;
+ }
+ }
+
+ private static short[] createPayloadArrayShorts(int length) {
+ Random rand = new Random(System.currentTimeMillis());
+
+ short[] payload = new short[length];
+ for (int i = 0; i < length; i++) {
+ payload[i] = (short) (64 + 1 + rand.nextInt(9));
+ }
+
+ return payload;
+ }
+
+ @Test
+ public void testEncodeDecodeIntArray10() throws Throwable {
+ // int array8 less than 128 bytes
+ doEncodeDecodeIntArrayTestImpl(10, false);
+ }
+
+ @Test
+ public void testEncodeDecodeIntArray50() throws Throwable {
+ // int array8 greater than 128 bytes
+ doEncodeDecodeIntArrayTestImpl(50, false);
+ }
+
+ @Test
+ public void testEncodeDecodeIntArray384() throws Throwable {
+ // int array32
+ doEncodeDecodeIntArrayTestImpl(384, false);
+ }
+
+ @Test
+ public void testEncodeDecodeSmallIntArray100() throws Throwable {
+ // small-int array8 less than 128 bytes
+ doEncodeDecodeIntArrayTestImpl(100, true);
+ }
+
+ @Test
+ public void testEncodeDecodeSmallIntArray192() throws Throwable {
+ // small-int array8 greater than 128 bytes
+ doEncodeDecodeIntArrayTestImpl(192, true);
+ }
+
+ @Test
+ public void testEncodeDecodeSmallIntArray384() throws Throwable {
+ // small-int array32
+ doEncodeDecodeIntArrayTestImpl(384, true);
+ }
+
+ private void doEncodeDecodeIntArrayTestImpl(int count, boolean smallInt) throws Throwable {
+ int[] source = createPayloadArrayInts(count, smallInt);
+
+ try {
+ assertEquals("Unexpected source array length", count, source.length);
+
+ int encodingWidth;
+ int elementWidth;
+ if(smallInt) {
+ elementWidth = 1;
+ encodingWidth = count < 254 ? 1 : 4; // less than 254 and not 256, since each element is 1 byte, but we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ } else {
+ elementWidth = 4;
+ encodingWidth = count < 63 ? 1 : 4; // less than 63, since each element is 4 bytes, but we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ }
+
+ int arrayPayloadSize = encodingWidth + 1 + (count * elementWidth); // variable width for element count + byte type descriptor + (number of elements * size)
+ int expectedEncodedArraySize = 1 + encodingWidth + arrayPayloadSize; // array type code + variable width for array size + other encoded payload
+ byte[] expectedEncoding = new byte[expectedEncodedArraySize];
+ ByteBuffer expectedEncodingWrapper = ByteBuffer.wrap(expectedEncoding);
+
+ // Write the array encoding code, array size, and element count
+ if(count < 254) {
+ expectedEncodingWrapper.put((byte) 0xE0); // 'array8' type descriptor code
+ expectedEncodingWrapper.put((byte) arrayPayloadSize);
+ expectedEncodingWrapper.put((byte) count);
+ } else {
+ expectedEncodingWrapper.put((byte) 0xF0); // 'array32' type descriptor code
+ expectedEncodingWrapper.putInt(arrayPayloadSize);
+ expectedEncodingWrapper.putInt(count);
+ }
+
+ // Write the type descriptor
+ if(smallInt) {
+ expectedEncodingWrapper.put((byte) 0x54); // 'small int' type descriptor code
+ } else {
+ expectedEncodingWrapper.put((byte) 0x71); // 'int' type descriptor code
+ }
+
+ // Write the elements
+ for (int i = 0; i < count; i++) {
+ int j = source[i];
+ if(smallInt) {
+ // Using put as small int is 1 byte
+ assertTrue("Unexpected value: + j", j <= 127 && j >=-128);
+ expectedEncodingWrapper.put((byte) j);
+ } else {
+ expectedEncodingWrapper.putInt(j);
+ }
+ }
+
+ assertFalse("Should have filled expected encoding array", expectedEncodingWrapper.hasRemaining());
+
+ // Now verify against the actual encoding of the array
+ assertEquals("Unexpected buffer position", 0, buffer.position());
+ encoder.writeArray(source);
+ assertEquals("Unexpected encoded payload length", expectedEncodedArraySize, buffer.position());
+
+ byte[] actualEncoding = new byte[expectedEncodedArraySize];
+ buffer.flip();
+ buffer.get(actualEncoding);
+ assertFalse("Should have drained the encoder buffer contents", buffer.hasRemaining());
+
+ assertArrayEquals("Unexpected actual array encoding", expectedEncoding, actualEncoding);
+
+ // Now verify against the decoding
+ buffer.flip();
+ Object decoded = decoder.readObject();
+ assertNotNull(decoded);
+ assertTrue(decoded.getClass().isArray());
+ assertTrue(decoded.getClass().getComponentType().isPrimitive());
+ assertEquals(int.class, decoded.getClass().getComponentType());
+
+ assertArrayEquals("Unexpected decoding", source, (int[]) decoded);
+ }
+ catch (Throwable t) {
+ System.err.println("Error during test, source array: " + Arrays.toString(source));
+ throw t;
+ }
+ }
+
+ private static int[] createPayloadArrayInts(int length, boolean smallInt) {
+ Random rand = new Random(System.currentTimeMillis());
+
+ int[] payload = new int[length];
+ for (int i = 0; i < length; i++) {
+ if(smallInt) {
+ payload[i] = 64 + 1 + rand.nextInt(9);
+ } else {
+ payload[i] = 128 + 1 + rand.nextInt(9);
+ }
+ }
+
+ return payload;
+ }
+
+ @Test
+ public void testEncodeDecodeLongArray10() throws Throwable {
+ // long array8 less than 128 bytes
+ doEncodeDecodeLongArrayTestImpl(10, false);
+ }
+
+ @Test
+ public void testEncodeDecodeLongArray25() throws Throwable {
+ // long array8 greater than 128 bytes
+ doEncodeDecodeLongArrayTestImpl(25, false);
+ }
+
+ @Test
+ public void testEncodeDecodeLongArray384() throws Throwable {
+ // long array32
+ doEncodeDecodeLongArrayTestImpl(384, false);
+ }
+
+ @Test
+ public void testEncodeDecodeSmallLongArray100() throws Throwable {
+ // small-long array8 less than 128 bytes
+ doEncodeDecodeLongArrayTestImpl(100, true);
+ }
+
+ @Test
+ public void testEncodeDecodeSmallLongArray192() throws Throwable {
+ // small-long array8 greater than 128 bytes
+ doEncodeDecodeLongArrayTestImpl(192, true);
+ }
+
+ @Test
+ public void testEncodeDecodeSmallLongArray384() throws Throwable {
+ // small-long array32
+ doEncodeDecodeLongArrayTestImpl(384, true);
+ }
+
+ private void doEncodeDecodeLongArrayTestImpl(int count, boolean smallLong) throws Throwable {
+ long[] source = createPayloadArrayLongs(count, smallLong);
+
+ try {
+ assertEquals("Unexpected source array length", count, source.length);
+
+ int encodingWidth;
+ int elementWidth;
+ if(smallLong) {
+ elementWidth = 1;
+ encodingWidth = count < 254 ? 1 : 4; // less than 254 and not 256, since each element is 1 byte, but we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ } else {
+ elementWidth = 8;
+ encodingWidth = count < 31 ? 1 : 4; // less than 31, since each element is 8 bytes, but we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ }
+
+ int arrayPayloadSize = encodingWidth + 1 + (count * elementWidth); // variable width for element count + byte type descriptor + (number of elements * size)
+ int expectedEncodedArraySize = 1 + encodingWidth + arrayPayloadSize; // array type code + variable width for array size + other encoded payload
+ byte[] expectedEncoding = new byte[expectedEncodedArraySize];
+ ByteBuffer expectedEncodingWrapper = ByteBuffer.wrap(expectedEncoding);
+
+ // Write the array encoding code, array size, and element count
+ if(count < 254) {
+ expectedEncodingWrapper.put((byte) 0xE0); // 'array8' type descriptor code
+ expectedEncodingWrapper.put((byte) arrayPayloadSize);
+ expectedEncodingWrapper.put((byte) count);
+ } else {
+ expectedEncodingWrapper.put((byte) 0xF0); // 'array32' type descriptor code
+ expectedEncodingWrapper.putInt(arrayPayloadSize);
+ expectedEncodingWrapper.putInt(count);
+ }
+
+ // Write the type descriptor
+ if(smallLong) {
+ expectedEncodingWrapper.put((byte) 0x55); // 'small long' type descriptor code
+ } else {
+ expectedEncodingWrapper.put((byte) 0x81); // 'long' type descriptor code
+ }
+
+ // Write the elements
+ for (int i = 0; i < count; i++) {
+ long j = source[i];
+ if(smallLong) {
+ // Using put as small long is 1 byte
+ assertTrue("Unexpected value: + j", j <= 127 && j >=-128);
+ expectedEncodingWrapper.put((byte) j);
+ } else {
+ expectedEncodingWrapper.putLong(j);
+ }
+ }
+
+ assertFalse("Should have filled expected encoding array", expectedEncodingWrapper.hasRemaining());
+
+ // Now verify against the actual encoding of the array
+ assertEquals("Unexpected buffer position", 0, buffer.position());
+ encoder.writeArray(source);
+ assertEquals("Unexpected encoded payload length", expectedEncodedArraySize, buffer.position());
+
+ byte[] actualEncoding = new byte[expectedEncodedArraySize];
+ buffer.flip();
+ buffer.get(actualEncoding);
+ assertFalse("Should have drained the encoder buffer contents", buffer.hasRemaining());
+
+ assertArrayEquals("Unexpected actual array encoding", expectedEncoding, actualEncoding);
+
+ // Now verify against the decoding
+ buffer.flip();
+ Object decoded = decoder.readObject();
+ assertNotNull(decoded);
+ assertTrue(decoded.getClass().isArray());
+ assertTrue(decoded.getClass().getComponentType().isPrimitive());
+ assertEquals(long.class, decoded.getClass().getComponentType());
+
+ assertArrayEquals("Unexpected decoding", source, (long[]) decoded);
+ }
+ catch (Throwable t) {
+ System.err.println("Error during test, source array: " + Arrays.toString(source));
+ throw t;
+ }
+ }
+
+ private static long[] createPayloadArrayLongs(int length, boolean smallLong) {
+ Random rand = new Random(System.currentTimeMillis());
+
+ long[] payload = new long[length];
+ for (int i = 0; i < length; i++) {
+ if(smallLong) {
+ payload[i] = 64 + 1 + rand.nextInt(9);
+ } else {
+ payload[i] = 128 + 1 + rand.nextInt(9);
+ }
+ }
+
+ return payload;
+ }
+
+ @Test
+ public void testEncodeDecodeFloatArray25() throws Throwable {
+ // float array8 less than 128 bytes
+ doEncodeDecodeFloatArrayTestImpl(25);
+ }
+
+ @Test
+ public void testEncodeDecodeFloatArray50() throws Throwable {
+ // float array8 greater than 128 bytes
+ doEncodeDecodeFloatArrayTestImpl(50);
+ }
+
+ @Test
+ public void testEncodeDecodeFloatArray384() throws Throwable {
+ // float array32
+ doEncodeDecodeFloatArrayTestImpl(384);
+ }
+
+ private void doEncodeDecodeFloatArrayTestImpl(int count) throws Throwable {
+ float[] source = createPayloadArrayFloats(count);
+
+ try {
+ assertEquals("Unexpected source array length", count, source.length);
+
+ int encodingWidth = count < 63 ? 1 : 4; // less than 63, since each element is 4 bytes, but we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ int arrayPayloadSize = encodingWidth + 1 + (count * 4); // variable width for element count + byte type descriptor + (number of elements * size)
+ int expectedEncodedArraySize = 1 + encodingWidth + arrayPayloadSize; // array type code + variable width for array size + other encoded payload
+ byte[] expectedEncoding = new byte[expectedEncodedArraySize];
+ ByteBuffer expectedEncodingWrapper = ByteBuffer.wrap(expectedEncoding);
+
+ // Write the array encoding code, array size, and element count
+ if(count < 254) {
+ expectedEncodingWrapper.put((byte) 0xE0); // 'array8' type descriptor code
+ expectedEncodingWrapper.put((byte) arrayPayloadSize);
+ expectedEncodingWrapper.put((byte) count);
+ } else {
+ expectedEncodingWrapper.put((byte) 0xF0); // 'array32' type descriptor code
+ expectedEncodingWrapper.putInt(arrayPayloadSize);
+ expectedEncodingWrapper.putInt(count);
+ }
+
+ // Write the type descriptor
+ expectedEncodingWrapper.put((byte) 0x72); // 'float' type descriptor code
+
+ // Write the elements
+ for (int i = 0; i < count; i++) {
+ expectedEncodingWrapper.putFloat(source[i]);
+ }
+
+ assertFalse("Should have filled expected encoding array", expectedEncodingWrapper.hasRemaining());
+
+ // Now verify against the actual encoding of the array
+ assertEquals("Unexpected buffer position", 0, buffer.position());
+ encoder.writeArray(source);
+ assertEquals("Unexpected encoded payload length", expectedEncodedArraySize, buffer.position());
+
+ byte[] actualEncoding = new byte[expectedEncodedArraySize];
+ buffer.flip();
+ buffer.get(actualEncoding);
+ assertFalse("Should have drained the encoder buffer contents", buffer.hasRemaining());
+
+ assertArrayEquals("Unexpected actual array encoding", expectedEncoding, actualEncoding);
+
+ // Now verify against the decoding
+ buffer.flip();
+ Object decoded = decoder.readObject();
+ assertNotNull(decoded);
+ assertTrue(decoded.getClass().isArray());
+ assertTrue(decoded.getClass().getComponentType().isPrimitive());
+ assertEquals(float.class, decoded.getClass().getComponentType());
+
+ assertArrayEquals("Unexpected decoding", source, (float[]) decoded, 0.0F);
+ }
+ catch (Throwable t) {
+ System.err.println("Error during test, source array: " + Arrays.toString(source));
+ throw t;
+ }
+ }
+
+ private static float[] createPayloadArrayFloats(int length) {
+ Random rand = new Random(System.currentTimeMillis());
+
+ float[] payload = new float[length];
+ for (int i = 0; i < length; i++) {
+ payload[i] = 64 + 1 + rand.nextInt(9);
+ }
+
+ return payload;
+ }
+
+ @Test
+ public void testEncodeDecodeDoubleArray10() throws Throwable {
+ // double array8 less than 128 bytes
+ doEncodeDecodeDoubleArrayTestImpl(10);
+ }
+
+ @Test
+ public void testEncodeDecodeDoubleArray25() throws Throwable {
+ // double array8 greater than 128 bytes
+ doEncodeDecodeDoubleArrayTestImpl(25);
+ }
+
+ @Test
+ public void testEncodeDecodeDoubleArray384() throws Throwable {
+ // double array32
+ doEncodeDecodeDoubleArrayTestImpl(384);
+ }
+
+ private void doEncodeDecodeDoubleArrayTestImpl(int count) throws Throwable {
+ double[] source = createPayloadArrayDoubles(count);
+
+ try {
+ assertEquals("Unexpected source array length", count, source.length);
+
+ int encodingWidth = count < 31 ? 1 : 4; // less than 31, since each element is 8 bytes, but we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ int arrayPayloadSize = encodingWidth + 1 + (count * 8); // variable width for element count + byte type descriptor + (number of elements * size)
+ int expectedEncodedArraySize = 1 + encodingWidth + arrayPayloadSize; // array type code + variable width for array size + other encoded payload
+ byte[] expectedEncoding = new byte[expectedEncodedArraySize];
+ ByteBuffer expectedEncodingWrapper = ByteBuffer.wrap(expectedEncoding);
+
+ // Write the array encoding code, array size, and element count
+ if(count < 254) {
+ expectedEncodingWrapper.put((byte) 0xE0); // 'array8' type descriptor code
+ expectedEncodingWrapper.put((byte) arrayPayloadSize);
+ expectedEncodingWrapper.put((byte) count);
+ } else {
+ expectedEncodingWrapper.put((byte) 0xF0); // 'array32' type descriptor code
+ expectedEncodingWrapper.putInt(arrayPayloadSize);
+ expectedEncodingWrapper.putInt(count);
+ }
+
+ // Write the type descriptor
+ expectedEncodingWrapper.put((byte) 0x82); // 'double' type descriptor code
+
+ // Write the elements
+ for (int i = 0; i < count; i++) {
+ expectedEncodingWrapper.putDouble(source[i]);
+ }
+
+ assertFalse("Should have filled expected encoding array", expectedEncodingWrapper.hasRemaining());
+
+ // Now verify against the actual encoding of the array
+ assertEquals("Unexpected buffer position", 0, buffer.position());
+ encoder.writeArray(source);
+ assertEquals("Unexpected encoded payload length", expectedEncodedArraySize, buffer.position());
+
+ byte[] actualEncoding = new byte[expectedEncodedArraySize];
+ buffer.flip();
+ buffer.get(actualEncoding);
+ assertFalse("Should have drained the encoder buffer contents", buffer.hasRemaining());
+
+ assertArrayEquals("Unexpected actual array encoding", expectedEncoding, actualEncoding);
+
+ // Now verify against the decoding
+ buffer.flip();
+ Object decoded = decoder.readObject();
+ assertNotNull(decoded);
+ assertTrue(decoded.getClass().isArray());
+ assertTrue(decoded.getClass().getComponentType().isPrimitive());
+ assertEquals(double.class, decoded.getClass().getComponentType());
+
+ assertArrayEquals("Unexpected decoding", source, (double[]) decoded, 0.0F);
+ }
+ catch (Throwable t) {
+ System.err.println("Error during test, source array: " + Arrays.toString(source));
+ throw t;
+ }
+ }
+
+ private static double[] createPayloadArrayDoubles(int length) {
+ Random rand = new Random(System.currentTimeMillis());
+
+ double[] payload = new double[length];
+ for (int i = 0; i < length; i++) {
+ payload[i] = 64 + 1 + rand.nextInt(9);
+ }
+
+ return payload;
+ }
+
+ @Test
+ public void testEncodeCharArray25() throws Throwable {
+ // char array8 less than 128 bytes
+ doEncodeCharArrayTestImpl(25);
+ }
+
+ @Test
+ public void testEncodeCharArray50() throws Throwable {
+ // char array8 greater than 128 bytes
+ doEncodeCharArrayTestImpl(50);
+ }
+
+ @Test
+ public void testEncodeCharArray384() throws Throwable {
+ // char array32
+ doEncodeCharArrayTestImpl(384);
+ }
+
+ private void doEncodeCharArrayTestImpl(int count) throws Throwable {
+ char[] source = createPayloadArrayChars(count);
+
+ try {
+ assertEquals("Unexpected source array length", count, source.length);
+
+ int encodingWidth = count < 63 ? 1 : 4; // less than 63, since each element is 4 bytes, but we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
+ int arrayPayloadSize = encodingWidth + 1 + (count * 4); // variable width for element count + byte type descriptor + (number of elements * size)
+ int expectedEncodedArraySize = 1 + encodingWidth + arrayPayloadSize; // array type code + variable width for array size + other encoded payload
+ byte[] expectedEncoding = new byte[expectedEncodedArraySize];
+ ByteBuffer expectedEncodingWrapper = ByteBuffer.wrap(expectedEncoding);
+
+ // Write the array encoding code, array size, and element count
+ if(count < 254) {
+ expectedEncodingWrapper.put((byte) 0xE0); // 'array8' type descriptor code
+ expectedEncodingWrapper.put((byte) arrayPayloadSize);
+ expectedEncodingWrapper.put((byte) count);
+ } else {
+ expectedEncodingWrapper.put((byte) 0xF0); // 'array32' type descriptor code
+ expectedEncodingWrapper.putInt(arrayPayloadSize);
+ expectedEncodingWrapper.putInt(count);
+ }
+
+ // Write the type descriptor
+ expectedEncodingWrapper.put((byte) 0x73); // 'char' type descriptor code
+
+ // Write the elements
+ for (int i = 0; i < count; i++) {
+ expectedEncodingWrapper.putInt(source[i]); //4 byte encoding
+ }
+
+ assertFalse("Should have filled expected encoding array", expectedEncodingWrapper.hasRemaining());
+
+ // Now verify against the actual encoding of the array
+ assertEquals("Unexpected buffer position", 0, buffer.position());
+ encoder.writeArray(source);
+ assertEquals("Unexpected encoded payload length", expectedEncodedArraySize, buffer.position());
+
+ byte[] actualEncoding = new byte[expectedEncodedArraySize];
+ buffer.flip();
+ buffer.get(actualEncoding);
+ assertFalse("Should have drained the encoder buffer contents", buffer.hasRemaining());
+
+ assertArrayEquals("Unexpected actual array encoding", expectedEncoding, actualEncoding);
+ }
+ catch (Throwable t) {
+ System.err.println("Error during test, source array: " + Arrays.toString(source));
+ throw t;
+ }
+ }
+
+ private static char[] createPayloadArrayChars(int length) {
+ Random rand = new Random(System.currentTimeMillis());
+
+ char[] payload = new char[length];
+ for (int i = 0; i < length; i++) {
+ payload[i] = (char) (64 + 1 + rand.nextInt(9));
+ }
+
+ return payload;
+ }
+
}
\ No newline at end of file