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 0863ec0ec..e9c30891e 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 @@ -1142,6 +1142,21 @@ public void writeNull() throws IOException { _writeByte(BYTE_NULL); } + /** + * Method for outputting given value as an unsigned integer. + * Can be called in any context where a value is expected + * (Array value, Object field value, root-level value). + * + * @param i Number value to write + * @throws IOException if there is either an underlying I/O problem or encoding + * issue at format layer + * @since 2.20 + */ + public void writeNumberUnsigned(int i) throws IOException { + _verifyValueWrite("write number unsigned"); + _writeIntMinimal(PREFIX_TYPE_INT_POS, i); + } + @Override public void writeNumber(int i) throws IOException { _verifyValueWrite("write number"); @@ -1183,6 +1198,34 @@ public void writeNumber(int i) throws IOException { _outputBuffer[_outputTail++] = b0; } + /** + * Method for outputting given value as an unsigned integer. + * Can be called in any context where a value is expected + * (Array value, Object field value, root-level value). + * + * @param l Number value to write + * @throws IOException if there is either an underlying I/O problem or encoding + * issue at format layer + * @since 2.20 + */ + public void writeNumberUnsigned(long l) throws IOException { + if (_cfgMinimalInts && l >= 0 && l < 0x100000000L) { + writeNumberUnsigned((int) l); + return; + } + _verifyValueWrite("write number unsigned"); + _ensureRoomForOutput(9); + _outputBuffer[_outputTail++] = (byte) (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS); + _outputBuffer[_outputTail++] = (byte) (l >> 56); + _outputBuffer[_outputTail++] = (byte) (l >> 48); + _outputBuffer[_outputTail++] = (byte) (l >> 40); + _outputBuffer[_outputTail++] = (byte) (l >> 32); + _outputBuffer[_outputTail++] = (byte) (l >> 24); + _outputBuffer[_outputTail++] = (byte) (l >> 16); + _outputBuffer[_outputTail++] = (byte) (l >> 8); + _outputBuffer[_outputTail++] = (byte) l; + } + @Override public void writeNumber(long l) throws IOException { _verifyValueWrite("write number"); diff --git a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/gen/GeneratorSimpleTest.java b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/gen/GeneratorSimpleTest.java index e40f052cf..401dd06b0 100644 --- a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/gen/GeneratorSimpleTest.java +++ b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/gen/GeneratorSimpleTest.java @@ -181,6 +181,98 @@ public void testIntValues() throws Exception (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF); } + @Test + public void testUnsignedIntValues() throws Exception + { + // uint32 max + ByteArrayOutputStream out = new ByteArrayOutputStream(); + CBORGenerator gen = cborGenerator(out); + gen.writeNumberUnsigned(-1); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT32_ELEMENTS), + (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF); + + // Min int + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(Integer.MIN_VALUE); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT32_ELEMENTS), + (byte) 0x80, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00); + + // Truncated to 2 bytes + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(1000); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT16_ELEMENTS), + (byte) 0x03, + (byte) 0xE8); + + // Truncated to 1 byte + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(100); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT8_ELEMENTS), + (byte) 0x64); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(25); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT8_ELEMENTS), + (byte) 0x19); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(24); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT8_ELEMENTS), + (byte) 0x18); + + // Truncated to not take any extra bytes + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(23); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) 0x17); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(10); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) 0x0A); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(1); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) 0x01); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(0); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) 0x00); + } + @Test public void testLongValues() throws Exception { @@ -201,6 +293,119 @@ public void testLongValues() throws Exception assertEquals(0, b[3]); } + @Test + public void testUnsignedLongValues() throws Exception + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + CBORGenerator gen = cborGenerator(out); + + // uint64 max + gen.writeNumberUnsigned(-1L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT64_ELEMENTS), + (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF); + + // Min long + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(Long.MIN_VALUE); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT64_ELEMENTS), + (byte) 0x80, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00); + + // Truncated to 4 bytes + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(1000000L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT32_ELEMENTS), + (byte) 0x00, + (byte) 0x0F, + (byte) 0x42, + (byte) 0x40); + + // Truncated to 2 bytes + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(1000L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT16_ELEMENTS), + (byte) 0x03, + (byte) 0xE8); + + // Truncated to 1 byte + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(100L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT8_ELEMENTS), + (byte) 0x64); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(25L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT8_ELEMENTS), + (byte) 0x19); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(24L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) (CBORConstants.PREFIX_TYPE_INT_POS + CBORConstants.SUFFIX_UINT8_ELEMENTS), + (byte) 0x18); + + // Truncated to not take any extra bytes + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(23L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) 0x17); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(10L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) 0x0A); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(1L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) 0x01); + + out = new ByteArrayOutputStream(); + gen = cborGenerator(out); + gen.writeNumberUnsigned(0L); + gen.close(); + _verifyBytes(out.toByteArray(), + (byte) 0x00); + } + @Test public void testFloatValues() throws Exception {