-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
2 changed files
with
102 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package jsoniter_test | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"io" | ||
"math/rand" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
jsoniter "github.com/json-iterator/go" | ||
) | ||
|
||
// chunkedData is io.Reader which returns random amount of data in range [1, chunkedData.chunkSize]. | ||
// It simulates chunked data on from HTTP server, which is commonly used by net/http package. | ||
type chunkedData struct { | ||
chunkSize int | ||
data []byte | ||
head int | ||
} | ||
|
||
// Read is implementation of the io.Reader which returns random amount of data in range [1, chunkedData.chunkSize]. | ||
func (c *chunkedData) Read(p []byte) (n int, err error) { | ||
to := c.head + int(rand.Int31n(int32(c.chunkSize))+1) | ||
|
||
// copy does not copy more data then p can consume | ||
n = copy(p, c.data[c.head:to]) | ||
c.head = c.head + n | ||
if c.head >= len(c.data) { | ||
err = io.EOF | ||
} | ||
return n, err | ||
} | ||
|
||
// TestIterator_ReadInt_chunkedInput validates the behaviour of Iterator.ReadInt() method in where: | ||
// - it reads data from io.Reader, | ||
// - expected value is 0 (zero) | ||
// - Iterator.tail == Iterator.head | ||
// - Iterator.tail < len(Iterator.buf) | ||
// - value in buffer after Iterator.tail is presented from previous read and has '.' character. | ||
func TestIterator_ReadInt_chunkedInput(t *testing.T) { | ||
|
||
data := &chunkedData{ | ||
data: jsonFloatIntArray(t, 10), | ||
} | ||
|
||
// because this test is rely on randomness of chunkedData, we are doing multiple iterations to | ||
// be sure, that we can hit a required case. | ||
for data.chunkSize = 3; data.chunkSize <= len(data.data); data.chunkSize++ { | ||
data.head = 0 | ||
|
||
iter := jsoniter.Parse(jsoniter.ConfigDefault, data, data.chunkSize) | ||
i := 0 | ||
for iter.ReadArray() { | ||
// every even item is float, let's just skip it. | ||
if i%2 == 0 { | ||
iter.Skip() | ||
i++ | ||
continue | ||
} | ||
assert.Equal(t, 0, iter.ReadInt()) | ||
require.NoError(t, iter.Error) | ||
i++ | ||
} | ||
|
||
} | ||
} | ||
|
||
// jsonFloatIntArray generates JSON array where every | ||
// - even item is float 0.1 | ||
// - odd item is integer 0 | ||
// | ||
// [0.1, 0, 0.1, 0] | ||
func jsonFloatIntArray(t *testing.T, numberOfItems int) []byte { | ||
t.Helper() | ||
numbers := make([]jsoniter.Any, numberOfItems) | ||
for i := range numbers { | ||
switch i % 2 { | ||
case 0: | ||
numbers[i] = jsoniter.WrapFloat64(0.1) | ||
default: | ||
numbers[i] = jsoniter.WrapInt64(0) | ||
} | ||
} | ||
|
||
fixture, err := jsoniter.ConfigFastest.Marshal(numbers) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
b := &bytes.Buffer{} | ||
|
||
require.NoError( | ||
t, | ||
json.Compact(b, fixture), | ||
"json should be compactable", | ||
) | ||
return b.Bytes() | ||
} |