From f58c19cb4246c867e5f7c2248f43fc497cb1d5c5 Mon Sep 17 00:00:00 2001 From: Fawzi Essam Date: Sat, 10 May 2025 22:45:54 +0200 Subject: [PATCH 1/6] Allow exposing cbor simple values as VALUE_EMBEDDED_OBJECT Signed-off-by: Fawzi Essam --- .../jackson/dataformat/cbor/CBORParser.java | 64 +++++++++++++++---- .../cbor/parse/SimpleValuesTest.java | 32 ++++++++++ 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java index 351dbd1a4..59f76e5ac 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java @@ -59,7 +59,21 @@ public enum Feature implements FormatFeature * * @since 2.20 */ - HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT(false) + HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT(false), + + + /** + * Feature that determines how a CBOR "simple value" of major type 7 is decoded. + *

+ * When enabled, the parser returns {@link JsonToken#VALUE_EMBEDDED_OBJECT} with an embedded value + * of type {@link CBORSimpleValue}, allowing the caller to distinguish these values from actual {@link JsonToken#VALUE_NUMBER_INT}. + *

+ * When disabled (the default, for backwards compatibility), simple values are returned as {@link JsonToken#VALUE_NUMBER_INT}, + * from Jackson 2.10 to 2.19. + * + * @since 2.20 + */ + HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT(false) ; final boolean _defaultState; @@ -206,7 +220,7 @@ public int getFirstTag() { * @since 2.20 */ protected int _formatFeatures; - + /** * Codec used for data binding when (if) requested. */ @@ -540,6 +554,9 @@ public boolean empty() { protected float _numberFloat; protected double _numberDouble; + protected CBORSimpleValue _simpleValue; + + // And then object types protected BigInteger _numberBigInt; @@ -823,9 +840,9 @@ public JsonToken nextToken() throws IOException _skipIncomplete(); } _tokenInputTotal = _currInputProcessed + _inputPtr; - // also: clear any data retained so far - _numTypesValid = NR_UNKNOWN; - _binaryValue = null; + + // also: clear any data retained so far. + clearRetainedValues(); // First: need to keep track of lengths of defined-length Arrays and // Objects (to materialize END_ARRAY/END_OBJECT as necessary); @@ -1843,6 +1860,9 @@ public Object getEmbeddedObject() throws IOException _finishToken(); } if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) { + if (_simpleValue != null) { + return _simpleValue; + } return _binaryValue; } return null; @@ -3723,27 +3743,37 @@ public JsonToken _decodeSimpleValue(int lowBits, int ch) throws IOException { _invalidToken(ch); } if (lowBits < 24) { - _numberInt = lowBits; + if (Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + _simpleValue = new CBORSimpleValue(lowBits); + } else { + _numberInt = lowBits; + } } else { // need another byte if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } - _numberInt = _inputBuffer[_inputPtr++] & 0xFF; + // As per CBOR spec, values below 32 not allowed to avoid // confusion (as well as guarantee uniqueness of encoding) - if (_numberInt < 32) { + int value = _inputBuffer[_inputPtr++] & 0xFF; + if (value < 32) { throw _constructError("Invalid second byte for simple value: 0x" - +Integer.toHexString(_numberInt)+" (only values 0x20 - 0xFF allowed)"); + +Integer.toHexString(value)+" (only values 0x20 - 0xFF allowed)"); + } + + if (Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + _simpleValue = new CBORSimpleValue(value); + } else { + _numberInt = value; } } - // 25-Nov-2020, tatu: Although ideally we should report these - // as `JsonToken.VALUE_EMBEDDED_OBJECT`, due to late addition - // of handling in 2.12, simple value in 2.12 will be reported - // as simple ints. + if (Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + return JsonToken.VALUE_EMBEDDED_OBJECT; + } _numTypesValid = NR_INT; - return (JsonToken.VALUE_NUMBER_INT); + return JsonToken.VALUE_NUMBER_INT; } /* @@ -4100,4 +4130,10 @@ private void createChildObjectContext(final int len) throws IOException { _streamReadContext = _streamReadContext.createChildObjectContext(len); _streamReadConstraints.validateNestingDepth(_streamReadContext.getNestingDepth()); } + + private void clearRetainedValues() { + _numTypesValid = NR_UNKNOWN; + _binaryValue = null; + _simpleValue = null; + } } diff --git a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/SimpleValuesTest.java b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/SimpleValuesTest.java index d72f0175c..e9a145240 100644 --- a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/SimpleValuesTest.java +++ b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/SimpleValuesTest.java @@ -30,6 +30,23 @@ public void testTinySimpleValues() throws Exception } } + @Test + public void testTinySimpleValuesAsEmbeddedObjectWhenEnabled() throws Exception + { + CBORFactory f = CBORFactory.builder() + .enable(CBORParser.Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT) + .build(); + // Values 0..19 are unassigned, valid to encounter + for (int v = 0; v <= 19; ++v) { + byte[] doc = new byte[1]; + doc[0] = (byte) (CBORConstants.PREFIX_TYPE_MISC + v); + try (CBORParser p = cborParser(f, doc)) { + assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken()); + assertEquals(new CBORSimpleValue(v), p.getEmbeddedObject()); + } + } + } + @Test public void testValidByteLengthMinimalValues() throws Exception { // Values 32..255 are unassigned, valid to encounter @@ -44,6 +61,21 @@ public void testValidByteLengthMinimalValues() throws Exception { } } + @Test + public void testValidByteLengthMinimalValuesAsEmbeddedObjectWhenEnabled() throws Exception { + // Values 32..255 are unassigned, valid to encounter + CBORFactory f = CBORFactory.builder() + .enable(CBORParser.Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT) + .build(); + for (int v = 32; v <= 255; ++v) { + byte[] doc = { (byte) (CBORConstants.PREFIX_TYPE_MISC + 24), (byte) v }; + try (CBORParser p = cborParser(f, doc)) { + assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken()); + assertEquals(new CBORSimpleValue(v), p.getEmbeddedObject()); + } + } + } + @Test public void testInvalidByteLengthMinimalValues() throws Exception { // Values 0..31 are invalid for variant that takes 2 bytes... From 69a3099eb580c7b6eb070ba59d7ed5df3abedb6a Mon Sep 17 00:00:00 2001 From: Fawzi Essam Date: Sat, 10 May 2025 22:50:55 +0200 Subject: [PATCH 2/6] update README Signed-off-by: Fawzi Essam --- .../com/fasterxml/jackson/dataformat/cbor/CBORParser.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java index 59f76e5ac..dbf728550 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java @@ -3732,9 +3732,10 @@ protected JsonToken _decodeUndefinedValue() { * Helper method that deals with details of decoding unallocated "simple values" * and exposing them as expected token. *

- * As of Jackson 2.12, simple values are exposed as - * {@link JsonToken#VALUE_NUMBER_INT}s, - * but in later versions this is planned to be changed to separate value type. + * Starting with Jackson 2.20, this behavior can be changed by enabling the + * {@link com.fasterxml.jackson.dataformat.cbor.CBORParser.Feature#HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT} + * feature, in which case simple values are returned as {@link JsonToken#VALUE_EMBEDDED_OBJECT} with an + * embedded {@link CBORSimpleValue} instance. * * @since 2.12 */ From f02755de96bb9280675ec04869a27fb41923090e Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sat, 10 May 2025 15:30:12 -0700 Subject: [PATCH 3/6] Renaming, add release notes --- .../jackson/dataformat/cbor/CBORParser.java | 49 ++++++++++--------- .../cbor/parse/SimpleValuesTest.java | 4 +- .../cbor/parse/UndefinedValueTest.java | 6 +-- release-notes/CREDITS-2.x | 4 ++ release-notes/VERSION-2.x | 6 ++- 5 files changed, 38 insertions(+), 31 deletions(-) diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java index fc264a717..dae16c714 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java @@ -48,36 +48,37 @@ public enum Feature implements FormatFeature DECODE_USING_STANDARD_NEGATIVE_BIGINT_ENCODING(false), /** - * Feature that determines how an {@code undefined} value ({@code 0xF7}) is decoded. + * Feature that determines how an {@code undefined} value ({@code 0xF7}) is exposed + * by parser. *

- * When enabled, the parser returns {@link JsonToken#VALUE_EMBEDDED_OBJECT} with a - * value of {@code null}, allowing the caller to distinguish {@code undefined} from actual + * When enabled, the parser returns {@link JsonToken#VALUE_EMBEDDED_OBJECT} with + * a value of {@code null}, allowing the caller to distinguish {@code undefined} from actual * {@link JsonToken#VALUE_NULL}. - * When disabled (default, for backwards compatibility), {@code undefined} value is - * reported as {@link JsonToken#VALUE_NULL}, maintaining legacy behavior - * in use up to Jackson 2.19. + * When disabled {@code undefined} value is reported as {@link JsonToken#VALUE_NULL}. + *

+ * The default value is {@code false} for backwards compatibility (with versions prior to 2.20). * * @since 2.20 */ - HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT(false), - + READ_UNDEFINED_AS_EMBEDDED_OBJECT(false), /** - * Feature that determines how a CBOR "simple value" of major type 7 is decoded. + * Feature that determines how a CBOR "simple value" of major type 7 is exposed by parser. *

- * When enabled, the parser returns {@link JsonToken#VALUE_EMBEDDED_OBJECT} with an embedded value - * of type {@link CBORSimpleValue}, allowing the caller to distinguish these values from actual {@link JsonToken#VALUE_NUMBER_INT}. - *

- * When disabled (the default, for backwards compatibility), simple values are returned as {@link JsonToken#VALUE_NUMBER_INT}, - * from Jackson 2.10 to 2.19. + * When enabled, the parser returns {@link JsonToken#VALUE_EMBEDDED_OBJECT} with + * an embedded value of type {@link CBORSimpleValue}, allowing the caller to distinguish + * these values from actual {@link JsonToken#VALUE_NUMBER_INT}s. + * When disabled, simple values are returned as {@link JsonToken#VALUE_NUMBER_INT}. + *

+ * The default value is {@code false} for backwards compatibility (with versions prior to 2.20). * * @since 2.20 */ - HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT(false) + READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT(false) ; - final boolean _defaultState; - final int _mask; + private final boolean _defaultState; + private final int _mask; /** * Method that calculates bit set (flags) of all features that @@ -1953,7 +1954,7 @@ private final byte[] _getBinaryFromString(Base64Variant variant) throws IOExcept * Checking whether the current token represents an `undefined` value (0xF7). *

* This method allows distinguishing between real {@code null} and `undefined`, - * even if {@link CBORParser.Feature#HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT} is disabled + * even if {@link CBORParser.Feature#READ_UNDEFINED_AS_EMBEDDED_OBJECT} is disabled * and the token is reported as {@link JsonToken#VALUE_NULL}. * * @return {@code true} if current token is an `undefined`, {@code false} otherwise @@ -3714,14 +3715,14 @@ private final static long _long(int i1, int i2) * as {@link JsonToken#VALUE_NULL} by default. *

* - * since 2.20 If {@link CBORParser.Feature#HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT} is enabled, + * since 2.20 If {@link CBORParser.Feature#READ_UNDEFINED_AS_EMBEDDED_OBJECT} is enabled, * the value will instead be decoded as {@link JsonToken#VALUE_EMBEDDED_OBJECT} * with an embedded value of {@code null}. * * @since 2.10 */ protected JsonToken _decodeUndefinedValue() { - if (Feature.HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + if (Feature.READ_UNDEFINED_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { _binaryValue = null; // should be clear but just in case return JsonToken.VALUE_EMBEDDED_OBJECT; } @@ -3733,7 +3734,7 @@ protected JsonToken _decodeUndefinedValue() { * and exposing them as expected token. *

* Starting with Jackson 2.20, this behavior can be changed by enabling the - * {@link com.fasterxml.jackson.dataformat.cbor.CBORParser.Feature#HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT} + * {@link com.fasterxml.jackson.dataformat.cbor.CBORParser.Feature#READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT} * feature, in which case simple values are returned as {@link JsonToken#VALUE_EMBEDDED_OBJECT} with an * embedded {@link CBORSimpleValue} instance. * @@ -3744,7 +3745,7 @@ public JsonToken _decodeSimpleValue(int lowBits, int ch) throws IOException { _invalidToken(ch); } if (lowBits < 24) { - if (Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + if (Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { _simpleValue = new CBORSimpleValue(lowBits); } else { _numberInt = lowBits; @@ -3762,14 +3763,14 @@ public JsonToken _decodeSimpleValue(int lowBits, int ch) throws IOException { +Integer.toHexString(value)+" (only values 0x20 - 0xFF allowed)"); } - if (Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + if (Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { _simpleValue = new CBORSimpleValue(value); } else { _numberInt = value; } } - if (Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + if (Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { return JsonToken.VALUE_EMBEDDED_OBJECT; } diff --git a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/SimpleValuesTest.java b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/SimpleValuesTest.java index e9a145240..e1eed4ea2 100644 --- a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/SimpleValuesTest.java +++ b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/SimpleValuesTest.java @@ -34,7 +34,7 @@ public void testTinySimpleValues() throws Exception public void testTinySimpleValuesAsEmbeddedObjectWhenEnabled() throws Exception { CBORFactory f = CBORFactory.builder() - .enable(CBORParser.Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT) + .enable(CBORParser.Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT) .build(); // Values 0..19 are unassigned, valid to encounter for (int v = 0; v <= 19; ++v) { @@ -65,7 +65,7 @@ public void testValidByteLengthMinimalValues() throws Exception { public void testValidByteLengthMinimalValuesAsEmbeddedObjectWhenEnabled() throws Exception { // Values 32..255 are unassigned, valid to encounter CBORFactory f = CBORFactory.builder() - .enable(CBORParser.Feature.HANDLE_SIMPLE_VALUES_AS_EMBEDDED_OBJECT) + .enable(CBORParser.Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT) .build(); for (int v = 32; v <= 255; ++v) { byte[] doc = { (byte) (CBORConstants.PREFIX_TYPE_MISC + 24), (byte) v }; diff --git a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/UndefinedValueTest.java b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/UndefinedValueTest.java index 4b1454188..956c732bd 100644 --- a/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/UndefinedValueTest.java +++ b/cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/UndefinedValueTest.java @@ -32,7 +32,7 @@ public void testUndefinedLiteralStreaming() throws Exception @Test public void testUndefinedLiteralAsEmbeddedObject() throws Exception { CBORFactory f = CBORFactory.builder() - .enable(CBORParser.Feature.HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT) + .enable(CBORParser.Feature.READ_UNDEFINED_AS_EMBEDDED_OBJECT) .build(); CBORParser p = cborParser(f, new byte[] { BYTE_UNDEFINED }); @@ -62,7 +62,7 @@ public void testUndefinedInArray() throws Exception @Test public void testUndefinedInArrayAsEmbeddedObject() throws Exception { CBORFactory f = CBORFactory.builder() - .enable(CBORParser.Feature.HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT) + .enable(CBORParser.Feature.READ_UNDEFINED_AS_EMBEDDED_OBJECT) .build(); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -108,7 +108,7 @@ public void testUndefinedInObject() throws Exception @Test public void testUndefinedInObjectAsEmbeddedObject() throws Exception { CBORFactory f = CBORFactory.builder() - .enable(CBORParser.Feature.HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT) + .enable(CBORParser.Feature.READ_UNDEFINED_AS_EMBEDDED_OBJECT) .build(); ByteArrayOutputStream out = new ByteArrayOutputStream(); diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index f407d82b0..4214e8634 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -397,3 +397,7 @@ Fawzi Essam (@iifawzi) * Contributed fix for #431: (cbor) Negative `BigInteger` values not encoded/decoded correctly (2.20.0) + * Contributed implementation of #587: (cbor) Allow exposing CBOR Simple values as + `JsonToken.VALUE_EMBEDDED_OBJECT` with a feature flag + (2.20.0) + diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 0da77538a..4d09889f9 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -18,11 +18,13 @@ Active maintainers: #137: (cbor) Allow exposing CBOR "undefined" value as `JsonToken.VALUE_EMBEDDED_OBJECT`; with embedded value of `null` - (implementation contributed by Fawzi E) - + (implementation contributed by Fawzi E) #431: (cbor) Negative `BigInteger` values not encoded/decoded correctly (reported by Brian G) (fix contributed by Fawzi E) +#587: (cbor) Allow exposing CBOR Simple values as `JsonToken.VALUE_EMBEDDED_OBJECT` + with a feature flag + (implementation contributed by Fawzi E) - Generate SBOMs [JSTEP-14] 2.19.0 (24-Apr-2025) From 2871cfe90958750ce39357fa07630c4316d76d07 Mon Sep 17 00:00:00 2001 From: Fawzi Essam Date: Sun, 11 May 2025 01:27:08 +0200 Subject: [PATCH 4/6] clearing retained values for nextFieldName Signed-off-by: Fawzi Essam --- .../com/fasterxml/jackson/dataformat/cbor/CBORParser.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java index dae16c714..21e381307 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java @@ -1470,12 +1470,11 @@ public boolean nextFieldName(SerializableString str) throws IOException { // Two parsing modes; can only succeed if expecting field name, so handle that first: if (_streamReadContext.inObject() && _currToken != JsonToken.FIELD_NAME) { - _numTypesValid = NR_UNKNOWN; + clearRetainedValues(); if (_tokenIncomplete) { _skipIncomplete(); } _tokenInputTotal = _currInputProcessed + _inputPtr; - _binaryValue = null; _tagValues.clear(); // completed the whole Object? if (!_streamReadContext.expectMoreValues()) { @@ -1530,12 +1529,11 @@ public boolean nextFieldName(SerializableString str) throws IOException public String nextFieldName() throws IOException { if (_streamReadContext.inObject() && _currToken != JsonToken.FIELD_NAME) { - _numTypesValid = NR_UNKNOWN; + clearRetainedValues(); if (_tokenIncomplete) { _skipIncomplete(); } _tokenInputTotal = _currInputProcessed + _inputPtr; - _binaryValue = null; _tagValues.clear(); // completed the whole Object? if (!_streamReadContext.expectMoreValues()) { From 704d7ec7245f9ceef443cdfafce8c39cda429b7d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sat, 10 May 2025 19:31:42 -0700 Subject: [PATCH 5/6] minor reordering --- .../jackson/dataformat/cbor/CBORParser.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java index 21e381307..731f8e7b5 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java @@ -377,6 +377,14 @@ public int getFirstTag() { */ protected TagList _tagValues = new TagList(); + /** + * When major type 7 value is encountered and exposed as {@link JsonToken#VALUE_EMBEDDED_OBJECT}, + * the value will be stored here. + * + * @since 2.20 + */ + protected CBORSimpleValue _simpleValue; + /** * Flag that indicates that the current token has not yet * been fully processed, and needs to be finished for @@ -555,9 +563,6 @@ public boolean empty() { protected float _numberFloat; protected double _numberDouble; - protected CBORSimpleValue _simpleValue; - - // And then object types protected BigInteger _numberBigInt; @@ -842,7 +847,7 @@ public JsonToken nextToken() throws IOException } _tokenInputTotal = _currInputProcessed + _inputPtr; - // also: clear any data retained so far. + // also: clear any data retained for previous token clearRetainedValues(); // First: need to keep track of lengths of defined-length Arrays and @@ -1470,11 +1475,12 @@ public boolean nextFieldName(SerializableString str) throws IOException { // Two parsing modes; can only succeed if expecting field name, so handle that first: if (_streamReadContext.inObject() && _currToken != JsonToken.FIELD_NAME) { - clearRetainedValues(); if (_tokenIncomplete) { _skipIncomplete(); } _tokenInputTotal = _currInputProcessed + _inputPtr; + // need to clear retained values for previous token + clearRetainedValues(); _tagValues.clear(); // completed the whole Object? if (!_streamReadContext.expectMoreValues()) { @@ -1522,18 +1528,19 @@ public boolean nextFieldName(SerializableString str) throws IOException } } // otherwise just fall back to default handling; should occur rarely - return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName()); + return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(currentName()); } @Override public String nextFieldName() throws IOException { if (_streamReadContext.inObject() && _currToken != JsonToken.FIELD_NAME) { - clearRetainedValues(); if (_tokenIncomplete) { _skipIncomplete(); } _tokenInputTotal = _currInputProcessed + _inputPtr; + // need to clear retained values for previous token + clearRetainedValues(); _tagValues.clear(); // completed the whole Object? if (!_streamReadContext.expectMoreValues()) { @@ -4131,6 +4138,7 @@ private void createChildObjectContext(final int len) throws IOException { _streamReadConstraints.validateNestingDepth(_streamReadContext.getNestingDepth()); } + // @since 2.20 private void clearRetainedValues() { _numTypesValid = NR_UNKNOWN; _binaryValue = null; From c9cbd52c1bc124ecdff6b48dca6a3a88c70d4e74 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sat, 10 May 2025 19:38:53 -0700 Subject: [PATCH 6/6] minor tweaking, commenting --- .../jackson/dataformat/cbor/CBORParser.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java index 731f8e7b5..ff4cfe114 100644 --- a/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java @@ -1865,7 +1865,7 @@ public Object getEmbeddedObject() throws IOException if (_tokenIncomplete) { _finishToken(); } - if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) { + if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) { if (_simpleValue != null) { return _simpleValue; } @@ -1958,11 +1958,11 @@ private final byte[] _getBinaryFromString(Base64Variant variant) throws IOExcept /** * Checking whether the current token represents an `undefined` value (0xF7). *

- * This method allows distinguishing between real {@code null} and `undefined`, + * This method allows distinguishing between real {@code null} and {@code undefined}, * even if {@link CBORParser.Feature#READ_UNDEFINED_AS_EMBEDDED_OBJECT} is disabled * and the token is reported as {@link JsonToken#VALUE_NULL}. * - * @return {@code true} if current token is an `undefined`, {@code false} otherwise + * @return {@code true} if current token is an {@code undefined}, {@code false} otherwise * * @since 2.20 */ @@ -3739,7 +3739,7 @@ protected JsonToken _decodeUndefinedValue() { * and exposing them as expected token. *

* Starting with Jackson 2.20, this behavior can be changed by enabling the - * {@link com.fasterxml.jackson.dataformat.cbor.CBORParser.Feature#READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT} + * {@link CBORParser.Feature#READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT} * feature, in which case simple values are returned as {@link JsonToken#VALUE_EMBEDDED_OBJECT} with an * embedded {@link CBORSimpleValue} instance. * @@ -3749,8 +3749,9 @@ public JsonToken _decodeSimpleValue(int lowBits, int ch) throws IOException { if (lowBits > 24) { _invalidToken(ch); } + final boolean simpleAsEmbedded = Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures); if (lowBits < 24) { - if (Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + if (simpleAsEmbedded) { _simpleValue = new CBORSimpleValue(lowBits); } else { _numberInt = lowBits; @@ -3768,14 +3769,14 @@ public JsonToken _decodeSimpleValue(int lowBits, int ch) throws IOException { +Integer.toHexString(value)+" (only values 0x20 - 0xFF allowed)"); } - if (Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + if (simpleAsEmbedded) { _simpleValue = new CBORSimpleValue(value); } else { _numberInt = value; } } - if (Feature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) { + if (simpleAsEmbedded) { return JsonToken.VALUE_EMBEDDED_OBJECT; }