Skip to content

Commit 78de8a1

Browse files
committed
Merge branch '2.x' into 3.x
2 parents 1740dbc + 33a2294 commit 78de8a1

File tree

5 files changed

+104
-25
lines changed

5 files changed

+104
-25
lines changed

cbor/src/main/java/tools/jackson/dataformat/cbor/CBORParser.java

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ public int getFirstTag() {
159159
*/
160160
protected TagList _tagValues = new TagList();
161161

162+
/**
163+
* When major type 7 value is encountered and exposed as {@link JsonToken#VALUE_EMBEDDED_OBJECT},
164+
* the value will be stored here.
165+
*
166+
* @since 2.20
167+
*/
168+
protected CBORSimpleValue _simpleValue;
169+
162170
/**
163171
* Flag that indicates that the current token has not yet
164172
* been fully processed, and needs to be finished for
@@ -506,9 +514,9 @@ public JsonToken nextToken() throws JacksonException
506514
_skipIncomplete();
507515
}
508516
_tokenInputTotal = _currInputProcessed + _inputPtr;
509-
// also: clear any data retained so far
510-
_numTypesValid = NR_UNKNOWN;
511-
_binaryValue = null;
517+
518+
// also: clear any data retained for previous token
519+
clearRetainedValues();
512520

513521
// First: need to keep track of lengths of defined-length Arrays and
514522
// Objects (to materialize END_ARRAY/END_OBJECT as necessary);
@@ -1135,12 +1143,12 @@ public boolean nextName(SerializableString str) throws JacksonException
11351143
{
11361144
// Two parsing modes; can only succeed if expecting field name, so handle that first:
11371145
if (_streamReadContext.inObject() && _currToken != JsonToken.PROPERTY_NAME) {
1138-
_numTypesValid = NR_UNKNOWN;
11391146
if (_tokenIncomplete) {
11401147
_skipIncomplete();
11411148
}
11421149
_tokenInputTotal = _currInputProcessed + _inputPtr;
1143-
_binaryValue = null;
1150+
// need to clear retained values for previous token
1151+
clearRetainedValues();
11441152
_tagValues.clear();
11451153
// completed the whole Object?
11461154
if (!_streamReadContext.expectMoreValues()) {
@@ -1195,12 +1203,12 @@ public boolean nextName(SerializableString str) throws JacksonException
11951203
public String nextName() throws JacksonException
11961204
{
11971205
if (_streamReadContext.inObject() && _currToken != JsonToken.PROPERTY_NAME) {
1198-
_numTypesValid = NR_UNKNOWN;
11991206
if (_tokenIncomplete) {
12001207
_skipIncomplete();
12011208
}
12021209
_tokenInputTotal = _currInputProcessed + _inputPtr;
1203-
_binaryValue = null;
1210+
// need to clear retained values for previous token
1211+
clearRetainedValues();
12041212
_tagValues.clear();
12051213
// completed the whole Object?
12061214
if (!_streamReadContext.expectMoreValues()) {
@@ -1362,9 +1370,8 @@ public int nextNameMatch(PropertyNameMatcher matcher) throws JacksonException
13621370
if (_tokenIncomplete) {
13631371
_skipIncomplete();
13641372
}
1365-
_numTypesValid = NR_UNKNOWN;
13661373
_tokenInputTotal = _currInputProcessed + _inputPtr;
1367-
_binaryValue = null;
1374+
clearRetainedValues();
13681375
_tagValues.clear();
13691376
_sharedString = null;
13701377
// completed the whole Object?
@@ -1859,7 +1866,10 @@ public Object getEmbeddedObject() throws JacksonException
18591866
if (_tokenIncomplete) {
18601867
_finishToken();
18611868
}
1862-
if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) {
1869+
if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
1870+
if (_simpleValue != null) {
1871+
return _simpleValue;
1872+
}
18631873
return _binaryValue;
18641874
}
18651875
return null;
@@ -3690,36 +3700,48 @@ protected JsonToken _decodeUndefinedValue() {
36903700
* Helper method that deals with details of decoding unallocated "simple values"
36913701
* and exposing them as expected token.
36923702
* <p>
3693-
* As of Jackson 2.12, simple values are exposed as
3694-
* {@link JsonToken#VALUE_NUMBER_INT}s,
3695-
* but in later versions this is planned to be changed to separate value type.
3703+
* Starting with Jackson 2.20, this behavior can be changed by enabling the
3704+
* {@link CBORReadFeature#READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT}
3705+
* feature, in which case simple values are returned as {@link JsonToken#VALUE_EMBEDDED_OBJECT} with an
3706+
* embedded {@link CBORSimpleValue} instance.
36963707
*/
36973708
public JsonToken _decodeSimpleValue(int lowBits, int ch) throws JacksonException {
36983709
if (lowBits > 24) {
36993710
_invalidToken(ch);
37003711
}
3712+
final boolean simpleAsEmbedded = CBORReadFeature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures);
37013713
if (lowBits < 24) {
3702-
_numberInt = lowBits;
3714+
if (simpleAsEmbedded) {
3715+
_simpleValue = new CBORSimpleValue(lowBits);
3716+
} else {
3717+
_numberInt = lowBits;
3718+
}
37033719
} else { // need another byte
37043720
if (_inputPtr >= _inputEnd) {
37053721
loadMoreGuaranteed();
37063722
}
3707-
_numberInt = _inputBuffer[_inputPtr++] & 0xFF;
3723+
37083724
// As per CBOR spec, values below 32 not allowed to avoid
37093725
// confusion (as well as guarantee uniqueness of encoding)
3710-
if (_numberInt < 32) {
3726+
int value = _inputBuffer[_inputPtr++] & 0xFF;
3727+
if (value < 32) {
37113728
throw _constructReadException("Invalid second byte for simple value: 0x"
3712-
+Integer.toHexString(_numberInt)+" (only values 0x20 - 0xFF allowed)");
3729+
+Integer.toHexString(value)+" (only values 0x20 - 0xFF allowed)");
3730+
}
3731+
3732+
if (simpleAsEmbedded) {
3733+
_simpleValue = new CBORSimpleValue(value);
3734+
} else {
3735+
_numberInt = value;
37133736
}
37143737
}
37153738

3716-
// 25-Nov-2020, tatu: Although ideally we should report these
3717-
// as `JsonToken.VALUE_EMBEDDED_OBJECT`, due to late addition
3718-
// of handling in 2.12, simple value in 2.12 will be reported
3719-
// as simple ints.
3739+
if (simpleAsEmbedded) {
3740+
return JsonToken.VALUE_EMBEDDED_OBJECT;
3741+
}
37203742

37213743
_numTypesValid = NR_INT;
3722-
return (JsonToken.VALUE_NUMBER_INT);
3744+
return JsonToken.VALUE_NUMBER_INT;
37233745
}
37243746

37253747
/*
@@ -4080,4 +4102,11 @@ private void createChildObjectContext(final int len) throws JacksonException {
40804102
_streamReadContext = _streamReadContext.createChildObjectContext(len);
40814103
_streamReadConstraints.validateNestingDepth(_streamReadContext.getNestingDepth());
40824104
}
4105+
4106+
// @since 2.20
4107+
private void clearRetainedValues() {
4108+
_numTypesValid = NR_UNKNOWN;
4109+
_binaryValue = null;
4110+
_simpleValue = null;
4111+
}
40834112
}

cbor/src/main/java/tools/jackson/dataformat/cbor/CBORReadFeature.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,19 @@ public enum CBORReadFeature implements FormatFeature
3838
*<p>
3939
* The default value is {@code false} for backwards compatibility (with versions prior to 2.20).
4040
*/
41-
READ_UNDEFINED_AS_EMBEDDED_OBJECT(false)
41+
READ_UNDEFINED_AS_EMBEDDED_OBJECT(false),
42+
43+
/**
44+
* Feature that determines how a CBOR "simple value" of major type 7 is exposed by parser.
45+
* <p>
46+
* When enabled, the parser returns {@link JsonToken#VALUE_EMBEDDED_OBJECT} with
47+
* an embedded value of type {@link CBORSimpleValue}, allowing the caller to distinguish
48+
* these values from actual {@link JsonToken#VALUE_NUMBER_INT}s.
49+
* When disabled, simple values are returned as {@link JsonToken#VALUE_NUMBER_INT}.
50+
*<p>
51+
* The default value is {@code false} for backwards compatibility (with versions prior to 2.20).
52+
*/
53+
READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT(false)
4254
;
4355

4456
private final boolean _defaultState;

cbor/src/test/java/tools/jackson/dataformat/cbor/parse/SimpleValuesTest.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
import tools.jackson.core.JsonToken;
88
import tools.jackson.core.exc.StreamReadException;
99

10-
import tools.jackson.dataformat.cbor.CBORConstants;
11-
import tools.jackson.dataformat.cbor.CBORTestBase;
10+
import tools.jackson.dataformat.cbor.*;
1211

1312
import static org.junit.jupiter.api.Assertions.assertEquals;
1413
import static org.junit.jupiter.api.Assertions.fail;
@@ -31,6 +30,23 @@ public void testTinySimpleValues() throws Exception
3130
}
3231
}
3332

33+
@Test
34+
public void testTinySimpleValuesAsEmbeddedObjectWhenEnabled() throws Exception
35+
{
36+
CBORFactory f = CBORFactory.builder()
37+
.enable(CBORReadFeature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT)
38+
.build();
39+
// Values 0..19 are unassigned, valid to encounter
40+
for (int v = 0; v <= 19; ++v) {
41+
byte[] doc = new byte[1];
42+
doc[0] = (byte) (CBORConstants.PREFIX_TYPE_MISC + v);
43+
try (CBORParser p = cborParser(f, doc)) {
44+
assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
45+
assertEquals(new CBORSimpleValue(v), p.getEmbeddedObject());
46+
}
47+
}
48+
}
49+
3450
@Test
3551
public void testValidByteLengthMinimalValues() throws Exception {
3652
// Values 32..255 are unassigned, valid to encounter
@@ -45,6 +61,21 @@ public void testValidByteLengthMinimalValues() throws Exception {
4561
}
4662
}
4763

64+
@Test
65+
public void testValidByteLengthMinimalValuesAsEmbeddedObjectWhenEnabled() throws Exception {
66+
// Values 32..255 are unassigned, valid to encounter
67+
CBORFactory f = CBORFactory.builder()
68+
.enable(CBORReadFeature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT)
69+
.build();
70+
for (int v = 32; v <= 255; ++v) {
71+
byte[] doc = { (byte) (CBORConstants.PREFIX_TYPE_MISC + 24), (byte) v };
72+
try (CBORParser p = cborParser(f, doc)) {
73+
assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
74+
assertEquals(new CBORSimpleValue(v), p.getEmbeddedObject());
75+
}
76+
}
77+
}
78+
4879
@Test
4980
public void testInvalidByteLengthMinimalValues() throws Exception {
5081
// Values 0..31 are invalid for variant that takes 2 bytes...

release-notes/CREDITS-2.x

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,3 +397,7 @@ Fawzi Essam (@iifawzi)
397397
* Contributed fix for #431: (cbor) Negative `BigInteger` values not encoded/decoded
398398
correctly
399399
(2.20.0)
400+
* Contributed implementation of #587: (cbor) Allow exposing CBOR Simple values as
401+
`JsonToken.VALUE_EMBEDDED_OBJECT` with a feature flag
402+
(2.20.0)
403+

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Active maintainers:
2222
#431: (cbor) Negative `BigInteger` values not encoded/decoded correctly
2323
(reported by Brian G)
2424
(fix contributed by Fawzi E)
25+
#587: (cbor) Allow exposing CBOR Simple values as `JsonToken.VALUE_EMBEDDED_OBJECT`
26+
with a feature flag
27+
(implementation contributed by Fawzi E)
2528
- Generate SBOMs [JSTEP-14]
2629

2730
2.19.0 (24-Apr-2025)

0 commit comments

Comments
 (0)