Skip to content

Commit 8fa7ae6

Browse files
authored
Fix #674: Smile parser does not update StreamReadContext index or e… (#675)
1 parent 48fcd48 commit 8fa7ae6

6 files changed

Lines changed: 333 additions & 8 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* wrt. current index, entry count (binary content won't have
1616
* line/column number).
1717
*
18-
* @since 3.1
18+
* @since 3.2
1919
*/
2020
public class ParserReadContextTest extends CBORTestBase
2121
{

release-notes/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ implementations)
1818

1919
#662: (avro) Remove `IOException` from `AvroMapper.schemaFrom()`
2020
method signatures
21+
#674: Smile parser does not update `StreamReadContext` index or entry count
2122

2223
3.1.1 (not yet released)
2324

smile/src/main/java/tools/jackson/dataformat/smile/SmileParser.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,13 @@ public JsonToken nextToken() throws JacksonException
378378
// also: clear any data retained so far
379379
_binaryValue = null;
380380
// Two main modes: values, and property names.
381-
if ((_currToken != JsonToken.PROPERTY_NAME) && _streamReadContext.inObject()) {
382-
return _updateToken(_handlePropertyName());
381+
final boolean inObject = _streamReadContext.inObject();
382+
if ((_currToken != JsonToken.PROPERTY_NAME) && inObject) {
383+
JsonToken t = _handlePropertyName();
384+
if (t == JsonToken.PROPERTY_NAME) {
385+
_streamReadContext.valueRead();
386+
}
387+
return _updateToken(t);
383388
}
384389
if (_inputPtr >= _inputEnd) {
385390
if (!_loadMore()) {
@@ -388,6 +393,12 @@ public JsonToken nextToken() throws JacksonException
388393
}
389394
final int ch = _inputBuffer[_inputPtr++] & 0xFF;
390395
_typeAsInt = ch;
396+
// 17-Feb-2026, tatu: [dataformats-binary#674] Need to update context index
397+
// for non-Object contexts (Array, root)
398+
// NOTE: should we worry about end markers etc?
399+
if (!inObject) {
400+
_streamReadContext.valueRead();
401+
}
391402
switch (ch >> 5) {
392403
case 0: // short shared string value reference
393404
if (ch != 0) { // 0x0 is invalid
@@ -621,6 +632,11 @@ public String nextName() throws JacksonException
621632
int ch = _inputBuffer[_inputPtr++] & 0xFF;
622633
// is this needed?
623634
_typeAsInt = ch;
635+
// [dataformats-binary#674] Update context index for property entries
636+
// (but not for END_OBJECT marker 0xFB)
637+
if (ch != 0xFB) {
638+
_streamReadContext.valueRead();
639+
}
624640
switch (ch >> 6) {
625641
case 0: // misc, including end marker
626642
switch (ch) {
@@ -738,6 +754,7 @@ public boolean nextName(SerializableString str) throws JacksonException
738754
_updateToken(JsonToken.PROPERTY_NAME);
739755
_inputPtr = ptr;
740756
_streamReadContext.setCurrentName("");
757+
_streamReadContext.valueRead(); // [dataformats-binary#674]
741758
return (byteLen == 0);
742759
case 0x30: // long shared
743760
case 0x31:
@@ -752,6 +769,7 @@ public boolean nextName(SerializableString str) throws JacksonException
752769
_streamReadContext.setCurrentName(name);
753770
_inputPtr = ptr;
754771
_updateToken(JsonToken.PROPERTY_NAME);
772+
_streamReadContext.valueRead(); // [dataformats-binary#674]
755773
return name.equals(str.getValue());
756774
}
757775
//case 0x34: // long ASCII/Unicode name; let's not even try...
@@ -767,6 +785,7 @@ public boolean nextName(SerializableString str) throws JacksonException
767785
_streamReadContext.setCurrentName(name);
768786
_inputPtr = ptr;
769787
_updateToken(JsonToken.PROPERTY_NAME);
788+
_streamReadContext.valueRead(); // [dataformats-binary#674]
770789
return name.equals(str.getValue());
771790
}
772791
case 2: // short ASCII
@@ -789,6 +808,7 @@ public boolean nextName(SerializableString str) throws JacksonException
789808
}
790809
_streamReadContext.setCurrentName(name);
791810
_updateToken(JsonToken.PROPERTY_NAME);
811+
_streamReadContext.valueRead(); // [dataformats-binary#674]
792812
return true;
793813
}
794814
}
@@ -827,6 +847,7 @@ public boolean nextName(SerializableString str) throws JacksonException
827847
}
828848
_streamReadContext.setCurrentName(name);
829849
_updateToken(JsonToken.PROPERTY_NAME);
850+
_streamReadContext.valueRead(); // [dataformats-binary#674]
830851
return true;
831852
}
832853
}
@@ -862,6 +883,11 @@ public int nextNameMatch(PropertyNameMatcher matcher) throws JacksonException
862883
int ch = _inputBuffer[_inputPtr++] & 0xFF;
863884
// is this needed?
864885
_typeAsInt = ch;
886+
// [dataformats-binary#674] Update context index for property entries
887+
// (but not for END_OBJECT marker 0xFB)
888+
if (ch != 0xFB) {
889+
_streamReadContext.valueRead();
890+
}
865891
switch (ch >> 6) {
866892
case 0: // misc, including end marker
867893
switch (ch) {

smile/src/main/java/tools/jackson/dataformat/smile/async/NonBlockingByteArrayParser.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,12 @@ public void endOfInput() {
118118
* implementation
119119
*/
120120

121-
// public boolean nextFieldName(SerializableString str) throws JacksonException
122-
// public String nextTextValue() throws JacksonException
123-
// public int nextIntValue(int defaultValue) throws JacksonException
124-
// public long nextLongValue(long defaultValue) throws JacksonException
125-
// public Boolean nextBooleanValue() throws JacksonException
121+
// public String nextName()
122+
// public boolean nextName(SerializableString str)
123+
// public String nextTextValue()
124+
// public int nextIntValue(int defaultValue)
125+
// public long nextLongValue(long defaultValue)
126+
// public Boolean nextBooleanValue()
126127

127128
@Override
128129
public int releaseBuffered(OutputStream out) throws JacksonException {
@@ -418,6 +419,13 @@ protected JsonToken _finishHeader(int state) throws JacksonException
418419
*/
419420
private final JsonToken _startValue(int ch) throws JacksonException
420421
{
422+
// [dataformats-binary#674] Update context index for non-Object contexts
423+
// (Array, root).
424+
// Q? Structural markers (END_ARRAY 0xF9, EOF 0xFF)?
425+
if (!_streamReadContext.inObject()) {
426+
// && (ch & 0xFF) != 0xF9 && (ch & 0xFF) != 0xFF) {
427+
_streamReadContext.valueRead();
428+
}
421429
main_switch:
422430
switch ((ch >> 5) & 0x7) {
423431
case 0: // short shared string value reference
@@ -531,6 +539,11 @@ private final JsonToken _startValue(int ch) throws JacksonException
531539
*/
532540
protected final JsonToken _startFieldName(int ch) throws JacksonException
533541
{
542+
// [dataformats-binary#674] Update context index for property entries
543+
// (but not for END_OBJECT marker 0xFB)
544+
if ((ch & 0xFF) != 0xFB) {
545+
_streamReadContext.valueRead();
546+
}
534547
switch ((ch >> 6) & 3) {
535548
case 0: // misc, including end marker
536549
switch (ch) {
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package tools.jackson.dataformat.smile.async;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import tools.jackson.core.*;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
public class AsyncParserReadContextTest extends AsyncTestBase
10+
{
11+
// Async version of ParserReadContextTest.keywords()
12+
// [dataformats-binary#674]
13+
@Test
14+
void keywords() throws Exception
15+
{
16+
final String JSON = "{\n"
17+
+"\"key1\" : null,\n"
18+
+"\"key2\" : true,\n"
19+
+"\"key3\" : false,\n"
20+
+"\"key4\" : [ false, null, true ]\n"
21+
+"}"
22+
;
23+
24+
byte[] data = _smileDoc(JSON, true);
25+
26+
// Test with different read sizes to exercise async buffering
27+
_testKeywords(data, 0, 100);
28+
_testKeywords(data, 0, 3);
29+
_testKeywords(data, 0, 1);
30+
31+
_testKeywords(data, 1, 100);
32+
_testKeywords(data, 1, 3);
33+
_testKeywords(data, 1, 1);
34+
}
35+
36+
private void _testKeywords(byte[] data, int offset, int readSize)
37+
{
38+
AsyncReaderWrapper r = asyncForBytes(_smileReader(true), readSize, data, offset);
39+
JsonParser p = r.parser();
40+
41+
TokenStreamContext ctxt = p.streamReadContext();
42+
assertEquals("/", ctxt.toString());
43+
assertTrue(ctxt.inRoot());
44+
assertFalse(ctxt.inArray());
45+
assertFalse(ctxt.inObject());
46+
assertEquals(0, ctxt.getEntryCount());
47+
assertEquals(0, ctxt.getCurrentIndex());
48+
49+
assertToken(JsonToken.START_OBJECT, r.nextToken());
50+
51+
ctxt = p.streamReadContext();
52+
assertFalse(ctxt.inRoot());
53+
assertFalse(ctxt.inArray());
54+
assertTrue(ctxt.inObject());
55+
assertEquals(0, ctxt.getEntryCount());
56+
assertEquals(0, ctxt.getCurrentIndex());
57+
58+
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
59+
assertEquals("key1", p.currentName());
60+
assertEquals("{\"key1\"}", ctxt.toString());
61+
62+
ctxt = p.streamReadContext();
63+
assertFalse(ctxt.inRoot());
64+
assertFalse(ctxt.inArray());
65+
assertTrue(ctxt.inObject());
66+
assertEquals(0, ctxt.getCurrentIndex());
67+
assertEquals(1, ctxt.getEntryCount());
68+
assertEquals("key1", ctxt.currentName());
69+
70+
assertToken(JsonToken.VALUE_NULL, r.nextToken());
71+
assertEquals("key1", ctxt.currentName());
72+
73+
ctxt = p.streamReadContext();
74+
assertEquals(0, ctxt.getCurrentIndex());
75+
assertEquals(1, ctxt.getEntryCount());
76+
77+
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
78+
assertEquals("key2", p.currentName());
79+
ctxt = p.streamReadContext();
80+
assertEquals(1, ctxt.getCurrentIndex());
81+
assertEquals(2, ctxt.getEntryCount());
82+
assertEquals("key2", ctxt.currentName());
83+
84+
assertToken(JsonToken.VALUE_TRUE, r.nextToken());
85+
assertEquals(1, ctxt.getCurrentIndex());
86+
assertEquals(2, ctxt.getEntryCount());
87+
assertEquals("key2", ctxt.currentName());
88+
89+
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
90+
assertEquals("key3", p.currentName());
91+
assertEquals(2, ctxt.getCurrentIndex());
92+
assertEquals(3, ctxt.getEntryCount());
93+
assertToken(JsonToken.VALUE_FALSE, r.nextToken());
94+
assertEquals(2, ctxt.getCurrentIndex());
95+
assertEquals(3, ctxt.getEntryCount());
96+
97+
assertToken(JsonToken.PROPERTY_NAME, r.nextToken());
98+
assertEquals("key4", p.currentName());
99+
assertEquals(3, ctxt.getCurrentIndex());
100+
assertEquals(4, ctxt.getEntryCount());
101+
102+
assertToken(JsonToken.START_ARRAY, r.nextToken());
103+
ctxt = p.streamReadContext();
104+
assertTrue(ctxt.inArray());
105+
assertNull(ctxt.currentName());
106+
assertEquals(0, ctxt.getCurrentIndex());
107+
assertEquals(0, ctxt.getEntryCount());
108+
assertEquals("key4", ctxt.getParent().currentName());
109+
110+
assertToken(JsonToken.VALUE_FALSE, r.nextToken());
111+
assertEquals(0, ctxt.getCurrentIndex());
112+
assertEquals(1, ctxt.getEntryCount());
113+
assertEquals("[0]", ctxt.toString());
114+
115+
assertToken(JsonToken.VALUE_NULL, r.nextToken());
116+
assertEquals(1, ctxt.getCurrentIndex());
117+
assertEquals(2, ctxt.getEntryCount());
118+
119+
assertToken(JsonToken.VALUE_TRUE, r.nextToken());
120+
assertEquals(2, ctxt.getCurrentIndex());
121+
assertEquals(3, ctxt.getEntryCount());
122+
123+
assertToken(JsonToken.END_ARRAY, r.nextToken());
124+
125+
ctxt = p.streamReadContext();
126+
assertTrue(ctxt.inObject());
127+
128+
assertToken(JsonToken.END_OBJECT, r.nextToken());
129+
ctxt = p.streamReadContext();
130+
assertTrue(ctxt.inRoot());
131+
assertNull(ctxt.currentName());
132+
133+
r.close();
134+
}
135+
}

0 commit comments

Comments
 (0)