diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORConstants.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORConstants.java index 3446340cd..43b1cb823 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORConstants.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORConstants.java @@ -38,6 +38,10 @@ public final class CBORConstants */ public final static int SUFFIX_INDEFINITE = 0x1F; + public final static int SUFFIX_UINT8_ELEMENTS = 0x18; + public final static int SUFFIX_UINT16_ELEMENTS = 0x19; + public final static int SUFFIX_UINT32_ELEMENTS = 0x1A; + public final static int SUFFIX_UINT64_ELEMENTS = 0x1B; public final static int MASK_MAJOR_TYPE = 0xE0; @@ -60,10 +64,10 @@ public final class CBORConstants */ public final static byte BYTE_ARRAY_INDEFINITE = (byte) (PREFIX_TYPE_ARRAY + SUFFIX_INDEFINITE); - public final static byte BYTE_OBJECT_INDEFINITE = (byte) (PREFIX_TYPE_OBJECT + SUFFIX_INDEFINITE); - // 2-element array commonly used (for big float, f.ex.) public final static byte BYTE_ARRAY_2_ELEMENTS = (byte) (PREFIX_TYPE_ARRAY + 2); + + public final static byte BYTE_OBJECT_INDEFINITE = (byte) (PREFIX_TYPE_OBJECT + SUFFIX_INDEFINITE); public final static byte BYTE_FALSE = (byte) (PREFIX_TYPE_MISC + 20); public final static byte BYTE_TRUE = (byte) (PREFIX_TYPE_MISC + 21); diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java index eeba2d851..11a60ffd2 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java @@ -1,13 +1,17 @@ package com.fasterxml.jackson.dataformat.cbor; -import java.io.*; -import java.math.BigDecimal; -import java.math.BigInteger; - import com.fasterxml.jackson.core.*; -import com.fasterxml.jackson.core.io.*; -import com.fasterxml.jackson.core.json.JsonWriteContext; import com.fasterxml.jackson.core.base.GeneratorBase; +import com.fasterxml.jackson.core.io.IOContext; +import com.fasterxml.jackson.core.json.JsonWriteContext; +import java.util.List; +import java.util.ArrayList; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.math.BigInteger; import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.*; @@ -130,6 +134,21 @@ public int getMask() { protected boolean _cfgMinimalInts; + /** + * Special value that is use to keep tracks of arrays and maps opened with infinite length + */ + private final int INDEFINITE_LENGTH = Integer.MIN_VALUE -1; + + /** + * List that contains the number of remaining elements for parents arrays and objects. + */ + protected final List remainingElementsList = new ArrayList(); + + /** + * Number of elements remaining in the current complex structure (if any). + */ + private int currentRemainingElementsCount = INDEFINITE_LENGTH; + /* * /********************************************************** /* Output * buffering /********************************************************** @@ -369,6 +388,7 @@ public final void writeFieldName(String name) throws IOException { if (_writeContext.writeFieldName(name) == JsonWriteContext.STATUS_EXPECT_VALUE) { _reportError("Can not write a field name, expecting a value"); } + decrementElementsRemainingCount(); _writeString(name); } @@ -385,6 +405,7 @@ public final void writeFieldName(SerializableString name) _writeByte(BYTE_EMPTY_STRING); return; } + decrementElementsRemainingCount(); _writeLengthMarker(PREFIX_TYPE_TEXT, len); _writeBytes(raw, 0, len); } @@ -402,9 +423,20 @@ public final void writeStringField(String fieldName, String value) return; } _verifyValueWrite("write String value"); + decrementElementsRemainingCount(); _writeString(value); } + + public final void writeFieldLong(long size) throws IOException { + if (_writeContext.writeFieldName(String.valueOf(size)) == JsonWriteContext.STATUS_EXPECT_VALUE) { + _reportError("Can not write a field name, expecting a value"); + } + decrementElementsRemainingCount(); + _writeNumberNoCheck(size); + } + + /* * /********************************************************** /* Overridden * methods, copying with tag-awareness @@ -452,25 +484,25 @@ public final void writeStartArray() throws IOException { _verifyValueWrite("start an array"); _writeContext = _writeContext.createChildArrayContext(); _writeByte(BYTE_ARRAY_INDEFINITE); + + decrementElementsRemainingCount(); + openComplexElement(INDEFINITE_LENGTH); // set an unsized tag in the list for this new array } - // TODO: implement this for CBOR? /* - * Unlike with JSON, this method could use slightly optimized version since + * Unlike with JSON, this method is using slightly optimized version since * CBOR has a variant that allows embedding length in array start marker. - * But it mostly (or only?) makes sense for small arrays, cases where length - * marker fits within type marker byte; otherwise we might as well just use - * "indefinite" notation. */ + @Override public void writeStartArray(int size) throws IOException { _verifyValueWrite("start an array"); _writeContext = _writeContext.createChildArrayContext(); - /* - * if (size >= 31 || size < 0) { _writeByte(BYTE_ARRAY_INDEFINITE); } - * else { } - */ - _writeByte(BYTE_ARRAY_INDEFINITE); + + _writeLengthMarker(PREFIX_TYPE_ARRAY, size); + + decrementElementsRemainingCount(); + openComplexElement(size); } @Override @@ -478,7 +510,8 @@ public final void writeEndArray() throws IOException { if (!_writeContext.inArray()) { _reportError("Current context not Array but "+_writeContext.typeDesc()); } - _writeByte(BYTE_BREAK); + closeComplexElement(); + _writeContext = _writeContext.getParent(); } @@ -487,6 +520,8 @@ public final void writeStartObject() throws IOException { _verifyValueWrite("start an object"); _writeContext = _writeContext.createChildObjectContext(); _writeByte(BYTE_OBJECT_INDEFINITE); + decrementElementsRemainingCount(); + openComplexElement(INDEFINITE_LENGTH); // set an unsized tag in the list for this new map } @Override @@ -499,6 +534,20 @@ public final void writeStartObject(Object forValue) throws IOException { ctxt.setCurrentValue(forValue); } _writeByte(BYTE_OBJECT_INDEFINITE); + decrementElementsRemainingCount(); + openComplexElement(INDEFINITE_LENGTH); // set an unsized tag in the list for this new map + } + + public final void writeStartObject(int size) throws IOException { + _verifyValueWrite("start an object"); + + JsonWriteContext ctxt = _writeContext.createChildObjectContext(); + _writeContext = ctxt; + + _writeLengthMarker(PREFIX_TYPE_OBJECT, size); + + decrementElementsRemainingCount(); + openComplexElement(size * 2); // pair = 2 elements } @Override @@ -507,7 +556,7 @@ public final void writeEndObject() throws IOException { _reportError("Current context not Object but "+ _writeContext.typeDesc()); } _writeContext = _writeContext.getParent(); - _writeByte(BYTE_BREAK); + closeComplexElement(); } @Override // since 2.8 @@ -565,14 +614,14 @@ private final void _writeNumberNoCheck(int i) throws IOException { return; } if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (marker + 24); + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT8_ELEMENTS); _outputBuffer[_outputTail++] = (byte) i; return; } b0 = (byte) i; i >>= 8; if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (marker + 25); + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT16_ELEMENTS); _outputBuffer[_outputTail++] = (byte) i; _outputBuffer[_outputTail++] = b0; return; @@ -581,7 +630,7 @@ private final void _writeNumberNoCheck(int i) throws IOException { b0 = (byte) i; i >>= 8; } - _outputBuffer[_outputTail++] = (byte) (marker + 26); + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT32_ELEMENTS); _outputBuffer[_outputTail++] = (byte) (i >> 16); _outputBuffer[_outputTail++] = (byte) (i >> 8); _outputBuffer[_outputTail++] = (byte) i; @@ -599,9 +648,9 @@ private final void _writeNumberNoCheck(long l) throws IOException { if (l < 0L) { l += 1; l = -l; - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + 27); + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); } else { - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + 27); + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); } int i = (int) (l >> 32); _outputBuffer[_outputTail++] = (byte) (i >> 24); @@ -650,12 +699,14 @@ public void writeString(String text) throws IOException { return; } _verifyValueWrite("write String value"); + decrementElementsRemainingCount(); _writeString(text); } @Override public final void writeString(SerializableString sstr) throws IOException { _verifyValueWrite("write String value"); + decrementElementsRemainingCount(); byte[] raw = sstr.asUnquotedUTF8(); final int len = raw.length; if (len == 0) { @@ -670,6 +721,7 @@ public final void writeString(SerializableString sstr) throws IOException { public void writeString(char[] text, int offset, int len) throws IOException { _verifyValueWrite("write String value"); + decrementElementsRemainingCount(); if (len == 0) { _writeByte(BYTE_EMPTY_STRING); return; @@ -681,6 +733,7 @@ public void writeString(char[] text, int offset, int len) public void writeRawUTF8String(byte[] raw, int offset, int len) throws IOException { _verifyValueWrite("write String value"); + decrementElementsRemainingCount(); if (len == 0) { _writeByte(BYTE_EMPTY_STRING); return; @@ -753,6 +806,7 @@ public void writeBinary(Base64Variant b64variant, byte[] data, int offset, return; } _verifyValueWrite("write Binary value"); + decrementElementsRemainingCount(); _writeLengthMarker(PREFIX_TYPE_BYTES, len); _writeBytes(data, offset, len); } @@ -770,6 +824,7 @@ public int writeBinary(InputStream data, int dataLength) throws IOException { "Must pass actual length for CBOR encoded data"); } _verifyValueWrite("write Binary value"); + decrementElementsRemainingCount(); int missing; _writeLengthMarker(PREFIX_TYPE_BYTES, dataLength); @@ -796,6 +851,7 @@ public int writeBinary(Base64Variant b64variant, InputStream data, @Override public void writeBoolean(boolean state) throws IOException { _verifyValueWrite("write boolean value"); + decrementElementsRemainingCount(); if (state) { _writeByte(BYTE_TRUE); } else { @@ -806,12 +862,14 @@ public void writeBoolean(boolean state) throws IOException { @Override public void writeNull() throws IOException { _verifyValueWrite("write null value"); + decrementElementsRemainingCount(); _writeByte(BYTE_NULL); } @Override public void writeNumber(int i) throws IOException { _verifyValueWrite("write number"); + decrementElementsRemainingCount(); int marker; if (i < 0) { i = -i - 1; @@ -819,7 +877,6 @@ public void writeNumber(int i) throws IOException { } else { marker = PREFIX_TYPE_INT_POS; } - _ensureRoomForOutput(5); byte b0; if (_cfgMinimalInts) { @@ -828,14 +885,14 @@ public void writeNumber(int i) throws IOException { return; } if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (marker + 24); + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT8_ELEMENTS); _outputBuffer[_outputTail++] = (byte) i; return; } b0 = (byte) i; i >>= 8; if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (marker + 25); + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT16_ELEMENTS); _outputBuffer[_outputTail++] = (byte) i; _outputBuffer[_outputTail++] = b0; return; @@ -844,7 +901,7 @@ public void writeNumber(int i) throws IOException { b0 = (byte) i; i >>= 8; } - _outputBuffer[_outputTail++] = (byte) (marker + 26); + _outputBuffer[_outputTail++] = (byte) (marker + SUFFIX_UINT32_ELEMENTS); _outputBuffer[_outputTail++] = (byte) (i >> 16); _outputBuffer[_outputTail++] = (byte) (i >> 8); _outputBuffer[_outputTail++] = (byte) i; @@ -861,13 +918,14 @@ public void writeNumber(long l) throws IOException { } } _verifyValueWrite("write number"); + decrementElementsRemainingCount(); _ensureRoomForOutput(9); if (l < 0L) { l += 1; l = -l; - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + 27); + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); } else { - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + 27); + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); } int i = (int) (l >> 32); _outputBuffer[_outputTail++] = (byte) (i >> 24); @@ -888,6 +946,7 @@ public void writeNumber(BigInteger v) throws IOException { return; } _verifyValueWrite("write number"); + decrementElementsRemainingCount(); _write(v); } @@ -914,6 +973,7 @@ protected void _write(BigInteger v) throws IOException { @Override public void writeNumber(double d) throws IOException { _verifyValueWrite("write number"); + decrementElementsRemainingCount(); _ensureRoomForOutput(11); /* * 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems @@ -941,6 +1001,7 @@ public void writeNumber(float f) throws IOException { // Ok, now, we needed token type byte plus 5 data bytes (7 bits each) _ensureRoomForOutput(6); _verifyValueWrite("write number"); + decrementElementsRemainingCount(); /* * 17-Apr-2010, tatu: could also use 'floatToIntBits', but it seems more @@ -963,6 +1024,7 @@ public void writeNumber(BigDecimal dec) throws IOException { return; } _verifyValueWrite("write number"); + decrementElementsRemainingCount(); /* * Supported by using type tags, as per spec: major type for tag '6'; 5 * LSB 4. And then a two-element array; integer exponent, and int/bigint @@ -1450,9 +1512,9 @@ private final void _writeLongValue(long l) throws IOException { if (l < 0) { l += 1; l = -l; - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + 27); + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + SUFFIX_UINT64_ELEMENTS); } else { - _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + 27); + _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); } int i = (int) (l >> 32); _outputBuffer[_outputTail++] = (byte) (i >> 24); @@ -1474,19 +1536,19 @@ private final void _writeLengthMarker(int majorType, int i) return; } if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (majorType + 24); + _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT8_ELEMENTS); _outputBuffer[_outputTail++] = (byte) i; return; } final byte b0 = (byte) i; i >>= 8; if (i <= 0xFF) { - _outputBuffer[_outputTail++] = (byte) (majorType + 25); + _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT16_ELEMENTS); _outputBuffer[_outputTail++] = (byte) i; _outputBuffer[_outputTail++] = b0; return; } - _outputBuffer[_outputTail++] = (byte) (majorType + 26); + _outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT32_ELEMENTS); _outputBuffer[_outputTail++] = (byte) (i >> 16); _outputBuffer[_outputTail++] = (byte) (i >> 8); _outputBuffer[_outputTail++] = (byte) i; @@ -1592,4 +1654,29 @@ protected final void _flushBuffer() throws IOException { protected UnsupportedOperationException _notSupported() { return new UnsupportedOperationException(); } + + /* + * /********************************************************** + * Internal methods, size control for array and objects + * /********************************************************** + */ + private final void decrementElementsRemainingCount() throws IOException { + if (currentRemainingElementsCount != INDEFINITE_LENGTH) { + --currentRemainingElementsCount; + } + } + + private final void openComplexElement(int size) throws IOException { + remainingElementsList.add(currentRemainingElementsCount); + currentRemainingElementsCount = size; + } + + private final void closeComplexElement() throws IOException { + if (currentRemainingElementsCount == INDEFINITE_LENGTH) { + _writeByte(BYTE_BREAK); + } else if (currentRemainingElementsCount != 0) { + _reportError("ARRAY or MAP size mismatch: number of element encoded is not equal to the array/map size."); + } + currentRemainingElementsCount = remainingElementsList.remove(remainingElementsList.size() - 1); + } } diff --git a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/MapAndArrayTest.java b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/MapAndArrayTest.java new file mode 100644 index 000000000..dee327683 --- /dev/null +++ b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/MapAndArrayTest.java @@ -0,0 +1,518 @@ +package com.fasterxml.jackson.dataformat.cbor; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + + +public class MapAndArrayTest extends TestCase { + + /** + * Test for verifying complex Array and Map generation with limited and unlimited size + */ + + public void testCborBasicMap() throws IOException { + /* + {_"Fun": true, "Amt": -2} + 0xbf6346756ef563416d7421ff + BF -- Start indefinite-length map + 63 -- First key, UTF-8 string length 3 + 46756e -- "Fun" + F5 -- First value, true + 63 -- Second key, UTF-8 string length 3 + 416d74 -- "Amt" + 21 -- -2 + FF -- "break" + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartObject(); + gen.writeBooleanField("Fun", true); + gen.writeNumberField("Amt", -2); + gen.writeEndObject(); + gen.close(); + + byte [] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("bf6346756ef563416d7421ff")); + } + + public void testCborUnsizedMap() throws IOException { + /* {_"Fun": true, 1504: -33, 1505: false, 13171233041: 22} + + 0xbf6346756ef51905e038201905e1f41b000000031111111116ff + BF -- Start indefinite-length map + 63 -- First key, UTF-8 string length 3 + 46756e -- "Fun" + F5 -- First value, true + 19 -- Second key, Unsigned integer (two-byte uint16_t) + 05E0 -- 1504 + 38 -- Second value, Negative integer -1-n (one-byte uint8_t) + 20 -- 33 as unsigned + 19 -- Third key, Unsigned integer (two-byte uint16_t) + 05E1 -- 1505 + F4 -- Third value, false + 1B -- Fourth key, Unsigned integer (eight-byte uint64_t) + 0000000311111111 -- 13171233041 + 16 -- Fourth value, 22 + FF -- "break" + */ + + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartObject(); + gen.writeFieldName("Fun"); + gen.writeBoolean(true); + gen.writeFieldLong(1504); + gen.writeNumber(-33); + gen.writeFieldLong(1505); + gen.writeBoolean(false); + gen.writeFieldLong(13171233041L); + gen.writeNumber(22); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("bf6346756ef51905e038201905e1f41b000000031111111116ff")); + } + + + + public void testCborSizedMap() throws IOException { + /* + {1504: -33, 1505: false, 1506: "Fun", "Amt": [2, 3], 1507: false} + + 0xA51905e038201905e1f41905E26346756e63416d748202031905E3F4 + A5 -- Start definite-length map of 4 pairs + 19 -- First key, Unsigned integer (two-byte uint16_t) + 05E0 -- 1504 + 38 -- First value, Negative integer -1-n (one-byte uint8_t) + 20 -- 33 as unsigned + 19 -- Second key, Unsigned integer (two-byte uint16_t) + 05E1 -- 1505 + F4 -- Second value, false + 19 -- Third key, Unsigned integer (two-byte uint16_t) + 05E2 -- 1506 + 63 -- Third value, UTF-8 string length 3 + 46756e -- "Fun" + 63 -- Fourth key, UTF-8 string length 3 + 416d74 -- "Amt" + 82 -- Fourth value, array of 2 elements + 0203 -- array elements: 2, 3 + 19 -- Fifth key, Unsigned integer (two-byte uint16_t) + 05E3 -- 1507 + F4 -- Fifth value, false + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartObject(5); + gen.writeFieldLong(1504); + gen.writeNumber(-33); + gen.writeFieldLong(1505); + gen.writeBoolean(false); + gen.writeFieldLong(1506); + gen.writeString("Fun"); + gen.writeFieldName("Amt"); + gen.writeStartArray(2); + gen.writeNumber(2); + gen.writeNumber(3); + gen.writeEndArray(); + gen.writeFieldLong(1507); + gen.writeBoolean(false); + gen.writeEndObject(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("A51905e038201905e1f41905E26346756e63416d748202031905E3F4")); + } + + public void testCborSizedMapWithParserTest() throws IOException { + /* + {_ 1504:-33, 1505:false, 1506:"Fun", 1507: [_"c", 3, false], 13171233041:false } + + 0xBF1905e038201905e1f41905E26346756e63416d748202031905E3F4 + BF -- Start undefinite-length map + 19 -- First key, Unsigned integer (two-byte uint16_t) + 05E0 -- 1504 + 38 -- First value, Negative integer -1-n (one-byte uint8_t) + 20 -- 33 as unsigned + 19 -- Second key, Unsigned integer (two-byte uint16_t) + 05E1 -- 1505 + F4 -- Second value, false + 19 -- Third key, Unsigned integer (two-byte uint16_t) + 05E2 -- 1506 + 63 -- Third value, UTF-8 string length 3 + 46756e -- "Fun" + 63 -- Fourth key, UTF-8 string length 3 + 416d74 -- "Amt" + 82 -- Fourth value, array of 2 elements + 0203 -- array elements: 2, 3 + 1B -- Fifth key, Unsigned integer (eight-byte uint64_t) + 0000000311111111 -- 13171233041 + F4 -- Fifth value, false + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartObject(); + gen.writeFieldLong(1504); + gen.writeNumber(-33); + gen.writeFieldLong(1505); + gen.writeBoolean(false); + gen.writeFieldLong(1506); + gen.writeString("Fun"); + gen.writeFieldLong(1507); + gen.writeStartArray(); + gen.writeString("c"); + gen.writeNumber(3); + gen.writeBoolean(false); + gen.writeEndArray(); + gen.writeFieldLong(13171233041L); + gen.writeBoolean(false); + gen.writeEndObject(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("BF1905e038201905e1f41905E26346756e1905E39F616303F4FF1B0000000311111111F4FF")); + + /* + Parser test for the first element + */ + CBORParser parser = factory.createParser(bytes); + parser.nextToken(); + parser.nextToken(); + assertTrue(parser.getCurrentName().equals("1504")); + } + + public void testCborUnsizedMapWithArrayAsKey() throws IOException { + /* + {_ "a": 1, "b": [_ 2, 3]} + + 0xbf61610161629f0203ffff + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartObject(); + gen.writeFieldName("a"); + gen.writeNumber(1); + gen.writeFieldName("b"); + gen.writeStartArray(); + gen.writeNumber(2); + gen.writeNumber(3); + gen.writeEndArray(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("bf61610161629f0203ffff")); + } + + public void testCborMultilevelMapWithMultilevelArrays() throws IOException { + /* + { "a": 1, "b": [_ 2, 3], 1501: ["Fun", 44, [_ 45, 46, [ 47, 48]], { "key": {_"complex": 50}, 51: "52"}, 53], 1502: {_54: "value", 55: {56:61, 57:62}}} + + 0xa0... + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartObject(4); + gen.writeFieldName("a"); + gen.writeNumber(1); + + gen.writeFieldName("b"); + gen.writeStartArray(); + gen.writeNumber(2); + gen.writeNumber(3); + gen.writeEndArray(); + gen.writeFieldLong(1501); + gen.writeStartArray(5); + gen.writeString("Fun"); + gen.writeNumber(44); + gen.writeStartArray(); + gen.writeNumber(45); + gen.writeNumber(46); + gen.writeStartArray(2); + gen.writeNumber(47); + gen.writeNumber(48); + gen.writeEndArray(); + gen.writeEndArray(); + gen.writeStartObject(2); + gen.writeFieldName("key"); + gen.writeStartObject(); + gen.writeFieldName("complex"); + gen.writeNumber(50); + gen.writeEndObject(); + gen.writeFieldLong(51); + gen.writeString("52"); + gen.writeEndObject(); // + gen.writeNumber(53); + gen.writeEndArray(); + gen.writeFieldLong(1502); + gen.writeStartObject(); + gen.writeFieldLong(54); + gen.writeString("value"); + gen.writeFieldLong(55); + gen.writeStartObject(2); + gen.writeFieldLong(56); + gen.writeNumber(61); + gen.writeFieldLong(57); + gen.writeNumber(62); + gen.writeEndObject(); + gen.writeEndObject(); + gen.writeEndObject(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("a461610161629f0203ff1905dd856346756e182c9f182d182e82182f1830ffa2636b6579bf67636f6d706c65781832ff183362353218351905debf18366576616c75651837a21838183d1839183eff")); + } + + public void testCborUnsizedMapWithAllInside() throws IOException { + /* + {_ 1504: { 2504:-33}, 1505:false, 1506:"Fun", 1507: [_"c", 3, false], 13171233041:false } + + 0xBF1905e0A11909C838201905e1f41905E26346756e1905E39F616303F4FF1B0000000311111111F4FF + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartObject(); + gen.writeFieldLong(1504); + gen.writeStartObject(1); + gen.writeFieldLong(2504); + gen.writeNumber(-33); + gen.writeEndObject(); + gen.writeFieldLong(1505); + gen.writeBoolean(false); + gen.writeFieldLong(1506); + gen.writeString("Fun"); + gen.writeFieldLong(1507); + gen.writeStartArray(); + gen.writeString("c"); + gen.writeNumber(3); + gen.writeBoolean(false); + gen.writeEndArray(); + gen.writeFieldLong(13171233041L); + gen.writeBoolean(false); + gen.writeEndObject(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("BF1905e0A11909C838201905e1f41905E26346756e1905E39F616303F4FF1B0000000311111111F4FF")); + } + + public void testCborArraysInArray() throws IOException { + /* + [_ 1, [2, 3], [_ 4, 5]] + + 0x9f018202039f0405ffff + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartArray(); + gen.writeNumber(1); + gen.writeStartArray(2); + gen.writeNumber(2); + gen.writeNumber(3); + gen.writeEndArray(); + gen.writeStartArray(); + gen.writeNumber(4); + gen.writeNumber(5); + gen.writeEndArray(); + gen.writeEndArray(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("9f018202039f0405ffff")); + } + + public void testCborArraysInUnsizedArray() throws IOException { + /* + [_ 1, [2, 3], [_ 4, 5], [6, 7, [_ 8, 8, [1, 1]]], [9, 9], [_ 0, 1] ] + 0x9f018202039f0405ff8306079f080808820101ff8209099f0001ffff + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + gen.writeStartArray(); + gen.writeNumber(1); + gen.writeStartArray(2); + gen.writeNumber(2); + gen.writeNumber(3); + gen.writeEndArray(); + gen.writeStartArray(); + gen.writeNumber(4); + gen.writeNumber(5); + gen.writeEndArray(); + gen.writeStartArray(3); + gen.writeNumber(6); + gen.writeNumber(7); + gen.writeStartArray(); + gen.writeNumber(8); + gen.writeNumber(8); + gen.writeNumber(8); + gen.writeStartArray(2); + gen.writeNumber(1); + gen.writeNumber(1); + gen.writeEndArray(); + gen.writeEndArray(); + gen.writeEndArray(); + gen.writeStartArray(2); + gen.writeNumber(9); + gen.writeNumber(9); + gen.writeEndArray(); + gen.writeStartArray(); + gen.writeNumber(0); + gen.writeNumber(1); + gen.writeEndArray(); + gen.writeEndArray(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("9f018202039f0405ff8306079f080808820101ff8209099f0001ffff")); + } + + public void testCborArraysInSizedArray() throws IOException { + /* + [1, [_2, 3, 4], [_ 4, [5, [_6, 6, 6]]], [7, 8, [_ 9, 10]]] + + 0x84019f020304ff9f0482059f060606ffff8307089f090aff + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartArray(4);// [ + gen.writeNumber(1); // [1 + gen.writeStartArray(); // [1,[_ + gen.writeNumber(2); // [1,[_2, + gen.writeNumber(3); // [1,[_2,3, + gen.writeNumber(4); // [1,[_2,3,4, + gen.writeEndArray(); // [1,[_2,3,4,_] + gen.writeStartArray(); // [1,[_2,3,4,_][_ + gen.writeNumber(4); // [1,[_2,3,4,_][_4, + gen.writeStartArray(2);// [1,[_2,3,4,_][_4,[ + gen.writeNumber(5); // [1,[_2,3,4,_][_4,[5, + gen.writeStartArray(); // [1,[_2,3,4,_][_4,[5,[ + gen.writeNumber(6); // [1,[_2,3,4,_][_4,[5,[_6 + gen.writeNumber(6); // [1,[_2,3,4,_][_4,[5,[_6,6 + gen.writeNumber(6); // [1,[_2,3,4,_][_4,[5,[_6,6,6 + gen.writeEndArray(); // [1,[_2,3,4,_][_4,[5,[_6,6,6] + gen.writeEndArray(); // [1,[_2,3,4,_][_4,[5,[_6,6,6]] + gen.writeEndArray(); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]] + gen.writeStartArray(3);// [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[ + gen.writeNumber(7); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[7 + gen.writeNumber(8); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[7,8 + gen.writeStartArray(); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[7,8,[_ + gen.writeNumber(9); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[7,8,[_9, + gen.writeNumber(10); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[7,8,[_9,10 + gen.writeEndArray(); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[7,8,[_9,10] + gen.writeEndArray(); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[7,8,[_9,10]] + gen.writeEndArray(); // [1,[_2,3,4,_][_4,[5,[_6,6,6]]],[7,8,[_9,10]]] + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("84019f020304ff9f0482059f060606ffff8307089f090aff")); + } + + public void testCborSizedArray() throws IOException { + + /* [ 33, [256, 255, ..., 0], 34 ] + 0x8318219901000102030405...18FE18FF1901001822 + 258 elements array with an array of 256 inside uint16 size marker: + 83 -- Start finite-length array of 3 + 01 -- First byte (MSB) of size marker + 02 -- Second byte (LSB) of size marker (total size of 0x102) + 18 -- Unsigned integer (one-byte uint8_t follows) + 21 -- 33 decimal value + 99 -- Start finite-length array of 256 with two bytes of size marker + 0100 -- Two bytes (MSB) of size marker (total size of 0x100) + 01 -- 01 value + 02 -- 02 value + ... + 18 -- Unsigned integer (one-byte uint8_t follows) + FF -- 255 value + 19 -- Unsigned integer (two-byte uint16_t follows) + 0100 -- 256 value + 18 -- Unsigned integer (one-byte uint8_t follows) + 22 -- 34 decimal value (last value) + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + int size_finite_array = 258; + + gen.writeStartArray(3); + gen.writeNumber(33); + gen.writeStartArray(size_finite_array - 2); + for (int i = 0; i < size_finite_array - 2; i++) { + gen.writeNumber(i + 1); + } + gen.writeEndArray(); + gen.writeNumber(34); + gen.writeEndArray(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("8318219901000102030405060708090A0B0C0D0E0F101112131415161718181819181A181B181C181D181E181F1820182118221823182418251826182718281829182A182B182C182D182E182F1830183118321833183418351836183718381839183A183B183C183D183E183F1840184118421843184418451846184718481849184A184B184C184D184E184F1850185118521853185418551856185718581859185A185B185C185D185E185F1860186118621863186418651866186718681869186A186B186C186D186E186F1870187118721873187418751876187718781879187A187B187C187D187E187F1880188118821883188418851886188718881889188A188B188C188D188E188F1890189118921893189418951896189718981899189A189B189C189D189E189F18A018A118A218A318A418A518A618A718A818A918AA18AB18AC18AD18AE18AF18B018B118B218B318B418B518B618B718B818B918BA18BB18BC18BD18BE18BF18C018C118C218C318C418C518C618C718C818C918CA18CB18CC18CD18CE18CF18D018D118D218D318D418D518D618D718D818D918DA18DB18DC18DD18DE18DF18E018E118E218E318E418E518E618E718E818E918EA18EB18EC18ED18EE18EF18F018F118F218F318F418F518F618F718F818F918FA18FB18FC18FD18FE18FF1901001822")); + } + + public void testCborSizedArrayWithMap() throws IOException { + /* + ["a", {_ "b": "c", "d": "e", }] + 0x826161bf6162616361646165ff + */ + ByteArrayOutputStream payloadOut = new ByteArrayOutputStream(); + CBORFactory factory = new CBORFactory(); + CBORGenerator gen = factory.createGenerator(payloadOut); + + gen.writeStartArray(2); + gen.writeString("a"); + gen.writeStartObject(); + gen.writeFieldName("b"); + gen.writeString("c"); + gen.writeFieldName("d"); + gen.writeString("e"); + gen.writeEndObject(); + gen.writeEndArray(); + gen.close(); + + byte[] bytes = payloadOut.toByteArray(); + String hexData = javax.xml.bind.DatatypeConverter.printHexBinary(bytes); + + assertTrue(hexData.equalsIgnoreCase("826161bf6162616361646165ff")); + } +} \ No newline at end of file