Skip to content

Commit 493b949

Browse files
committed
Fixed #228
1 parent e647706 commit 493b949

File tree

5 files changed

+212
-55
lines changed

5 files changed

+212
-55
lines changed

cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java

+88-54
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,28 @@ private Feature(boolean defaultState) {
212212
*/
213213
protected byte[] _binaryValue;
214214

215+
/**
216+
* Helper variables used when dealing with chunked content.
217+
*/
218+
private int _chunkLeft, _chunkEnd;
219+
215220
/**
216221
* We will keep track of tag value for possible future use.
217222
*/
218223
protected int _tagValue = -1;
219224

225+
/**
226+
* Flag that indicates that the current token has not yet
227+
* been fully processed, and needs to be finished for
228+
* some access (or skipped to obtain the next token)
229+
*/
230+
protected boolean _tokenIncomplete = false;
231+
232+
/**
233+
* Type byte of the current token
234+
*/
235+
protected int _typeByte;
236+
220237
/*
221238
/**********************************************************
222239
/* Input source config, state (from ex StreamBasedParserBase)
@@ -246,29 +263,6 @@ private Feature(boolean defaultState) {
246263
*/
247264
protected boolean _bufferRecyclable;
248265

249-
/*
250-
/**********************************************************
251-
/* Additional parsing state
252-
/**********************************************************
253-
*/
254-
255-
/**
256-
* Flag that indicates that the current token has not yet
257-
* been fully processed, and needs to be finished for
258-
* some access (or skipped to obtain the next token)
259-
*/
260-
protected boolean _tokenIncomplete = false;
261-
262-
/**
263-
* Type byte of the current token
264-
*/
265-
protected int _typeByte;
266-
267-
/**
268-
* Helper variables used when dealing with chunked content.
269-
*/
270-
private int _chunkLeft, _chunkEnd;
271-
272266
/*
273267
/**********************************************************
274268
/* Symbol handling, decoding
@@ -379,10 +373,10 @@ public void setCodec(ObjectCodec c) {
379373
_objectCodec = c;
380374
}
381375

382-
/*
383-
/**********************************************************
384-
/* Versioned
385-
/**********************************************************
376+
/*
377+
/**********************************************************
378+
/* Versioned
379+
/**********************************************************
386380
*/
387381

388382
@Override
@@ -619,24 +613,25 @@ public JsonToken nextToken() throws IOException
619613
return _handleCBOREOF();
620614
}
621615
}
622-
int ch = _inputBuffer[_inputPtr++];
623-
int type = (ch >> 5) & 0x7;
616+
int ch = _inputBuffer[_inputPtr++] & 0xFF;
617+
int type = (ch >> 5);
618+
int lowBits = ch & 0x1F;
624619

625620
// One special case: need to consider tag as prefix first:
626621
if (type == 6) {
627-
_tagValue = Integer.valueOf(_decodeTag(ch & 0x1F));
622+
_tagValue = Integer.valueOf(_decodeTag(lowBits));
628623
if (_inputPtr >= _inputEnd) {
629624
if (!loadMore()) {
630625
return _handleCBOREOF();
631626
}
632627
}
633-
ch = _inputBuffer[_inputPtr++];
634-
type = (ch >> 5) & 0x7;
628+
ch = _inputBuffer[_inputPtr++] & 0xFF;
629+
type = (ch >> 5);
630+
lowBits = ch & 0x1F;
635631
} else {
636632
_tagValue = -1;
637633
}
638634

639-
final int lowBits = ch & 0x1F;
640635
switch (type) {
641636
case 0: // positive int
642637
_numTypesValid = NR_INT;
@@ -758,7 +753,8 @@ public JsonToken nextToken() throws IOException
758753

759754
case 6: // another tag; not allowed
760755
_reportError("Multiple tags not allowed per value (first tag: "+_tagValue+")");
761-
756+
757+
case 7:
762758
default: // misc: tokens, floats
763759
switch (lowBits) {
764760
case 20:
@@ -797,9 +793,8 @@ public JsonToken nextToken() throws IOException
797793
// Object end-marker can't occur here
798794
_reportUnexpectedBreak();
799795
}
800-
_invalidToken(ch);
796+
return (_currToken = _decodeSimpleValue(lowBits, ch));
801797
}
802-
return null;
803798
}
804799

805800
protected String _numberToName(int ch, boolean neg) throws IOException
@@ -939,23 +934,24 @@ protected final boolean _checkNextIsIntInArray(final String typeDesc) throws IOE
939934
return false;
940935
}
941936
}
942-
int ch = _inputBuffer[_inputPtr++];
943-
int type = (ch >> 5) & 0x7;
937+
int ch = _inputBuffer[_inputPtr++] & 0xFF;
938+
int type = (ch >> 5);
939+
int lowBits = ch & 0x1F;
944940

945941
// 01-Nov-2019, tatu: We may actually need tag so decode it, but do not assign
946942
// (that'd override tag we already have)
947943
int tagValue = -1;
948944
if (type == 6) {
949-
tagValue = _decodeTag(ch & 0x1F);
945+
tagValue = _decodeTag(lowBits);
950946
if ((_inputPtr >= _inputEnd) && !loadMore()) {
951947
_handleCBOREOF();
952948
return false;
953949
}
954-
ch = _inputBuffer[_inputPtr++];
955-
type = (ch >> 5) & 0x7;
950+
ch = _inputBuffer[_inputPtr++] & 0xFF;
951+
type = (ch >> 5);
952+
lowBits = ch & 0x1F;
956953
}
957-
958-
final int lowBits = ch & 0x1F;
954+
959955
switch (type) {
960956
case 0: // positive int
961957
_numTypesValid = NR_INT;
@@ -1285,25 +1281,26 @@ public String nextTextValue() throws IOException
12851281
return null;
12861282
}
12871283
}
1288-
int ch = _inputBuffer[_inputPtr++];
1289-
int type = (ch >> 5) & 0x7;
1284+
int ch = _inputBuffer[_inputPtr++] & 0xFF;
1285+
int type = (ch >> 5);
1286+
int lowBits = ch & 0x1F;
12901287

12911288
// One special case: need to consider tag as prefix first:
12921289
if (type == 6) {
1293-
_tagValue = Integer.valueOf(_decodeTag(ch & 0x1F));
1290+
_tagValue = Integer.valueOf(_decodeTag(lowBits));
12941291
if (_inputPtr >= _inputEnd) {
12951292
if (!loadMore()) {
12961293
_handleCBOREOF();
12971294
return null;
12981295
}
12991296
}
1300-
ch = _inputBuffer[_inputPtr++];
1301-
type = (ch >> 5) & 0x7;
1297+
ch = _inputBuffer[_inputPtr++] & 0xFF;
1298+
type = (ch >> 5);
1299+
lowBits = ch & 0x1F;
13021300
} else {
13031301
_tagValue = -1;
13041302
}
1305-
1306-
final int lowBits = ch & 0x1F;
1303+
13071304
switch (type) {
13081305
case 0: // positive int
13091306
_numTypesValid = NR_INT;
@@ -1425,6 +1422,7 @@ public String nextTextValue() throws IOException
14251422
case 6: // another tag; not allowed
14261423
_reportError("Multiple tags not allowed per value (first tag: "+_tagValue+")");
14271424

1425+
case 7:
14281426
default: // misc: tokens, floats
14291427
switch (lowBits) {
14301428
case 20:
@@ -1471,10 +1469,9 @@ public String nextTextValue() throws IOException
14711469
// Object end-marker can't occur here
14721470
_reportUnexpectedBreak();
14731471
}
1474-
_invalidToken(ch);
1472+
_currToken = _decodeSimpleValue(lowBits, ch);
1473+
return null;
14751474
}
1476-
// otherwise fall back to generic handling:
1477-
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
14781475
}
14791476

14801477
@Override
@@ -3118,6 +3115,44 @@ protected JsonToken _decodeUndefinedValue() throws IOException {
31183115
return JsonToken.VALUE_NULL;
31193116
}
31203117

3118+
/**
3119+
* Helper method that deals with details of decoding unallocated "simple values"
3120+
* and exposing them as expected token.
3121+
*<p>
3122+
* As of Jackson 2.12, simple values are exposed as
3123+
* {@link JsonToken#VALUE_NUMBER_INT}s,
3124+
* but in later versions this is planned to be changed to separate value type.
3125+
*
3126+
* @since 2.12
3127+
*/
3128+
public JsonToken _decodeSimpleValue(int lowBits, int ch) throws IOException {
3129+
if (lowBits > 24) {
3130+
_invalidToken(ch);
3131+
}
3132+
if (lowBits < 24) {
3133+
_numberInt = lowBits;
3134+
} else { // need another byte
3135+
if (_inputPtr >= _inputEnd) {
3136+
loadMoreGuaranteed();
3137+
}
3138+
_numberInt = _inputBuffer[_inputPtr++] & 0xFF;
3139+
// As per CBOR spec, values below 32 not allowed to avoid
3140+
// confusion (as well as guarantee uniqueness of encoding)
3141+
if (_numberInt < 32) {
3142+
throw _constructError("Invalid second byte for simple value: 0x"
3143+
+Integer.toHexString(_numberInt)+" (only values 0x20 - 0xFF allowed)");
3144+
}
3145+
}
3146+
3147+
// 25-Nov-2020, tatu: Although ideally we should report these
3148+
// as `JsonToken.VALUE_EMBEDDED_OBJECT`, due to late addition
3149+
// of handling in 2.12, simple value in 2.12 will be reported
3150+
// as simple ints.
3151+
3152+
_numTypesValid = NR_INT;
3153+
return (JsonToken.VALUE_NUMBER_INT);
3154+
}
3155+
31213156
/*
31223157
/**********************************************************
31233158
/* Internal methods, UTF8 decoding
@@ -3285,7 +3320,6 @@ protected ByteArrayBuilder _getByteArrayBuilder() {
32853320
return _byteArrayBuilder;
32863321
}
32873322

3288-
@SuppressWarnings("deprecation")
32893323
protected void _closeInput() throws IOException {
32903324
if (_inputStream != null) {
32913325
if (_ioContext.isResourceManaged() || isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.fasterxml.jackson.dataformat.cbor;
2+
3+
/**
4+
* Simple value object to be used for exposing undefined "simple values"
5+
* when encountered during parsing.
6+
* Note that as of Jackson 2.12, this class is <b>not yet</b> used for
7+
* exposing simple values: instead they are report as
8+
* {@link com.fasterxml.jackson.core.JsonToken#VALUE_NUMBER_INT}s.
9+
*<p>
10+
* Simple values left undefined in
11+
* <a href="https://tools.ietf.org/html/rfc7049">CBOR 1.0</a>
12+
* specification contain values {@code [0 - 19], [32, 255]}: other
13+
* values are not used to represent general simple values.
14+
* Specifically, values below {@code 0}, above {@code 255} or
15+
* in range {@code [20, 31] (inclusive)} are never exposed.
16+
*<p>
17+
* Values are not guaranteed to be canonicalized, but being immutable
18+
* may be reused (and in future possible canonicalized if that makes sense).
19+
*<p>
20+
* Note that it is possible that some of above-mentioned values may be
21+
* defined to have specific meaning and get reported using some other
22+
* mechanism.
23+
*
24+
* @since 2.12
25+
*/
26+
public class CBORSimpleValue {
27+
/**
28+
* Actual numeric value represented. Usually should be in range
29+
* of {@code [0-19][32-255]}.
30+
*/
31+
protected final int _value;
32+
33+
public CBORSimpleValue(int value) {
34+
_value = value;
35+
}
36+
37+
/**
38+
* Accessor for the simple integer value represented
39+
*
40+
* @return Simple integer value this instance represents
41+
*/
42+
public int getValue() { return _value; }
43+
44+
@Override
45+
public int hashCode() { return _value; }
46+
47+
@Override
48+
public String toString() {
49+
return Integer.valueOf(_value).toString();
50+
}
51+
52+
@Override
53+
public boolean equals(Object o)
54+
{
55+
if (o == this) return true;
56+
if (o instanceof CBORSimpleValue) {
57+
CBORSimpleValue other = (CBORSimpleValue) o;
58+
return _value == other._value;
59+
}
60+
return false;
61+
}
62+
}

cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/BinaryToStringCoercionTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static class BinaryMapWrapper {
4141
static class StringMapWrapper {
4242
public Map<String, String> data;
4343
}
44-
44+
4545
private final ObjectMapper CBOR_MAPPER = cborMapper();
4646

4747
public void testWithList() throws Exception
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.fasterxml.jackson.dataformat.cbor.parse;
2+
3+
import com.fasterxml.jackson.core.JsonParseException;
4+
import com.fasterxml.jackson.core.JsonParser;
5+
import com.fasterxml.jackson.core.JsonParser.NumberType;
6+
import com.fasterxml.jackson.core.JsonToken;
7+
import com.fasterxml.jackson.dataformat.cbor.CBORConstants;
8+
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
9+
import com.fasterxml.jackson.dataformat.cbor.CBORTestBase;
10+
11+
// @since 2.12
12+
public class SimpleValuesTest extends CBORTestBase
13+
{
14+
private final CBORFactory CBOR_F = new CBORFactory();
15+
16+
public void testTinySimpleValues() throws Exception
17+
{
18+
// Values 0..19 are unassigned, valid to encounter
19+
for (int v = 0; v <= 19; ++v) {
20+
byte[] doc = new byte[1];
21+
doc[0] = (byte) (CBORConstants.PREFIX_TYPE_MISC + v);
22+
try (JsonParser p = CBOR_F.createParser(doc)) {
23+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
24+
// exposes as `int`, fwtw
25+
assertEquals(NumberType.INT, p.getNumberType());
26+
assertEquals(v, p.getIntValue());
27+
}
28+
}
29+
}
30+
31+
public void testValidByteLengthMinimalValues() throws Exception {
32+
// Values 32..255 are unassigned, valid to encounter
33+
for (int v = 32; v <= 255; ++v) {
34+
byte[] doc = { (byte) (CBORConstants.PREFIX_TYPE_MISC + 24), (byte) v };
35+
try (JsonParser p = CBOR_F.createParser(doc)) {
36+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
37+
// exposes as `int`, fwtw
38+
assertEquals(NumberType.INT, p.getNumberType());
39+
assertEquals(v, p.getIntValue());
40+
}
41+
}
42+
}
43+
44+
public void testInvalidByteLengthMinimalValues() throws Exception {
45+
// Values 0..31 are invalid for variant that takes 2 bytes...
46+
for (int v = 0; v <= 31; ++v) {
47+
byte[] doc = { (byte) (CBORConstants.PREFIX_TYPE_MISC + 24), (byte) v };
48+
try (JsonParser p = CBOR_F.createParser(doc)) {
49+
try {
50+
p.nextToken();
51+
fail("Should not pass");
52+
} catch (JsonParseException e) {
53+
verifyException(e, "Invalid second byte for simple value:");
54+
verifyException(e, "0x"+Integer.toHexString(v));
55+
}
56+
}
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)