Skip to content

Commit ae689be

Browse files
committed
jsoniter: Fix errors during reading integers from chunked io.Reader
This commit fixes bug in Iterator.assertInteger method if the next conditions are met: - Iterator reads data from `io.Reader`, - expected value is `0` (zero) - `Iterator.tail == Iterator.head + 1` - `Iterator.tail < len(Iterator.buf)` - value in the buffer after `Iterator.tail` is presented from the previous read and has '.' character. Typical error which user cal see is: - assertInteger: can not decode float as int, error found in #X byte of ... Regression test added for checking the correct behaviour. Fixes #476
1 parent a1ca083 commit ae689be

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

iter_int.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ func (iter *Iterator) readUint64(c byte) (ret uint64) {
339339
}
340340

341341
func (iter *Iterator) assertInteger() {
342-
if iter.head < len(iter.buf) && iter.buf[iter.head] == '.' {
342+
if iter.head < iter.tail && iter.buf[iter.head] == '.' {
343343
iter.ReportError("assertInteger", "can not decode float as int")
344344
}
345345
}

iter_int_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package jsoniter_test
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"io"
7+
"math/rand"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
13+
jsoniter "github.com/json-iterator/go"
14+
)
15+
16+
// chunkedData is io.Reader which returns random amount of data in range [1, chunkedData.chunkSize].
17+
// It simulates chunked data on from HTTP server, which is commonly used by net/http package.
18+
type chunkedData struct {
19+
chunkSize int
20+
data []byte
21+
head int
22+
}
23+
24+
// Read is implementation of the io.Reader which returns random amount of data in range [1, chunkedData.chunkSize].
25+
func (c *chunkedData) Read(p []byte) (n int, err error) {
26+
to := c.head + int(rand.Int31n(int32(c.chunkSize))+1)
27+
28+
// copy does not copy more data then p can consume
29+
n = copy(p, c.data[c.head:to])
30+
c.head = c.head + n
31+
if c.head >= len(c.data) {
32+
err = io.EOF
33+
}
34+
return n, err
35+
}
36+
37+
// TestIterator_ReadInt_chunkedInput validates the behaviour of Iterator.ReadInt() method in where:
38+
// - it reads data from io.Reader,
39+
// - expected value is 0 (zero)
40+
// - Iterator.tail == Iterator.head
41+
// - Iterator.tail < len(Iterator.buf)
42+
// - value in buffer after Iterator.tail is presented from previous read and has '.' character.
43+
func TestIterator_ReadInt_chunkedInput(t *testing.T) {
44+
45+
data := &chunkedData{
46+
data: jsonFloatIntArray(t, 10),
47+
}
48+
49+
// because this test is rely on randomness of chunkedData, we are doing multiple iterations to
50+
// be sure, that we can hit a required case.
51+
for data.chunkSize = 3; data.chunkSize <= len(data.data); data.chunkSize++ {
52+
data.head = 0
53+
54+
iter := jsoniter.Parse(jsoniter.ConfigDefault, data, data.chunkSize)
55+
i := 0
56+
for iter.ReadArray() {
57+
// every even item is float, let's just skip it.
58+
if i%2 == 0 {
59+
iter.Skip()
60+
i++
61+
continue
62+
}
63+
assert.Equal(t, 0, iter.ReadInt())
64+
require.NoError(t, iter.Error)
65+
i++
66+
}
67+
68+
}
69+
}
70+
71+
// jsonFloatIntArray generates JSON array where every
72+
// - even item is float 0.1
73+
// - odd item is integer 0
74+
//
75+
// [0.1, 0, 0.1, 0]
76+
func jsonFloatIntArray(t *testing.T, numberOfItems int) []byte {
77+
t.Helper()
78+
numbers := make([]jsoniter.Any, numberOfItems)
79+
for i := range numbers {
80+
switch i % 2 {
81+
case 0:
82+
numbers[i] = jsoniter.WrapFloat64(0.1)
83+
default:
84+
numbers[i] = jsoniter.WrapInt64(0)
85+
}
86+
}
87+
88+
fixture, err := jsoniter.ConfigFastest.Marshal(numbers)
89+
if err != nil {
90+
panic(err)
91+
}
92+
93+
b := &bytes.Buffer{}
94+
95+
require.NoError(
96+
t,
97+
json.Compact(b, fixture),
98+
"json should be compactable",
99+
)
100+
return b.Bytes()
101+
}

0 commit comments

Comments
 (0)