You've already forked json-iterator
mirror of
https://github.com/json-iterator/go.git
synced 2025-06-15 22:50:24 +02:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
e6b9536d36 | |||
a3465d79a9 | |||
9b79a3e192 | |||
1779031cda | |||
6821bec9fa | |||
9461257643 | |||
5bce16d299 |
1
go.sum
1
go.sum
@ -9,6 +9,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
|
@ -288,6 +288,9 @@ non_decimal_loop:
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
value = (value << 3) + (value << 1) + uint64(ind)
|
||||
if value > maxFloat64 {
|
||||
return iter.readFloat64SlowPath()
|
||||
}
|
||||
}
|
||||
}
|
||||
return iter.readFloat64SlowPath()
|
||||
|
@ -9,6 +9,7 @@ var intDigits []int8
|
||||
|
||||
const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1
|
||||
const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1
|
||||
const maxFloat64 = 1<<53 - 1
|
||||
|
||||
func init() {
|
||||
intDigits = make([]int8, 256)
|
||||
@ -339,7 +340,7 @@ func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
||||
}
|
||||
|
||||
func (iter *Iterator) assertInteger() {
|
||||
if iter.head < len(iter.buf) && iter.buf[iter.head] == '.' {
|
||||
if iter.head < iter.tail && iter.buf[iter.head] == '.' {
|
||||
iter.ReportError("assertInteger", "can not decode float as int")
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,9 @@ package misc_tests
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@ -70,6 +72,95 @@ func Test_float_as_int(t *testing.T) {
|
||||
should.NotNil(jsoniter.Unmarshal([]byte(`1.1`), &i))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
should := require.New(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
|
||||
}
|
||||
|
||||
should.Zero(iter.ReadInt())
|
||||
should.NoError(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()
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_encode_int(b *testing.B) {
|
||||
stream := jsoniter.NewStream(jsoniter.ConfigDefault, ioutil.Discard, 64)
|
||||
for n := 0; n < b.N; n++ {
|
||||
|
@ -42,6 +42,25 @@ func Test_marshal_invalid_json_raw_message(t *testing.T) {
|
||||
should.Nil(aouterr)
|
||||
}
|
||||
|
||||
func Test_marshal_nil_json_raw_message(t *testing.T) {
|
||||
type A struct {
|
||||
Nil1 jsoniter.RawMessage `json:"raw1"`
|
||||
Nil2 json.RawMessage `json:"raw2"`
|
||||
}
|
||||
|
||||
a := A{}
|
||||
should := require.New(t)
|
||||
aout, aouterr := jsoniter.Marshal(&a)
|
||||
should.Equal(`{"raw1":null,"raw2":null}`, string(aout))
|
||||
should.Nil(aouterr)
|
||||
|
||||
a.Nil1 = []byte(`Any`)
|
||||
a.Nil2 = []byte(`Any`)
|
||||
should.Nil(jsoniter.Unmarshal(aout, &a))
|
||||
should.Nil(a.Nil1)
|
||||
should.Nil(a.Nil2)
|
||||
}
|
||||
|
||||
func Test_raw_message_memory_not_copied_issue(t *testing.T) {
|
||||
jsonStream := `{"name":"xxxxx","bundle_id":"com.zonst.majiang","app_platform":"ios","app_category":"100103", "budget_day":1000,"bidding_min":1,"bidding_max":2,"bidding_type":"CPM", "freq":{"open":true,"type":"day","num":100},"speed":1, "targeting":{"vendor":{"open":true,"list":["zonst"]}, "geo_code":{"open":true,"list":["156110100"]},"app_category":{"open":true,"list":["100101"]}, "day_parting":{"open":true,"list":["100409","100410"]},"device_type":{"open":true,"list":["ipad"]}, "os_version":{"open":true,"list":[10]},"carrier":{"open":true,"list":["mobile"]}, "network":{"open":true,"list":["4G"]}},"url":{"tracking_imp_url":"http://www.baidu.com", "tracking_clk_url":"http://www.baidu.com","jump_url":"http://www.baidu.com","deep_link_url":"http://www.baidu.com"}}`
|
||||
type IteratorObject struct {
|
||||
|
@ -65,7 +65,7 @@ func (iter *Iterator) ReadVal(obj interface{}) {
|
||||
decoder := iter.cfg.getDecoderFromCache(cacheKey)
|
||||
if decoder == nil {
|
||||
typ := reflect2.TypeOf(obj)
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
if typ == nil || typ.Kind() != reflect.Ptr {
|
||||
iter.ReportError("ReadVal", "can only unmarshal into pointer")
|
||||
return
|
||||
}
|
||||
|
@ -33,11 +33,19 @@ type jsonRawMessageCodec struct {
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*json.RawMessage)(ptr)) = json.RawMessage(iter.SkipAndReturnBytes())
|
||||
if iter.ReadNil() {
|
||||
*((*json.RawMessage)(ptr)) = nil
|
||||
} else {
|
||||
*((*json.RawMessage)(ptr)) = iter.SkipAndReturnBytes()
|
||||
}
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
if *((*json.RawMessage)(ptr)) == nil {
|
||||
stream.WriteNil()
|
||||
} else {
|
||||
stream.WriteRaw(string(*((*json.RawMessage)(ptr))))
|
||||
}
|
||||
}
|
||||
|
||||
func (codec *jsonRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
@ -48,11 +56,19 @@ type jsoniterRawMessageCodec struct {
|
||||
}
|
||||
|
||||
func (codec *jsoniterRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*RawMessage)(ptr)) = RawMessage(iter.SkipAndReturnBytes())
|
||||
if iter.ReadNil() {
|
||||
*((*RawMessage)(ptr)) = nil
|
||||
} else {
|
||||
*((*RawMessage)(ptr)) = iter.SkipAndReturnBytes()
|
||||
}
|
||||
}
|
||||
|
||||
func (codec *jsoniterRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
||||
if *((*RawMessage)(ptr)) == nil {
|
||||
stream.WriteNil()
|
||||
} else {
|
||||
stream.WriteRaw(string(*((*RawMessage)(ptr))))
|
||||
}
|
||||
}
|
||||
|
||||
func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
|
@ -1075,6 +1075,11 @@ type stringModeNumberDecoder struct {
|
||||
}
|
||||
|
||||
func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
if iter.WhatIsNext() == NilValue {
|
||||
decoder.elemDecoder.Decode(ptr, iter)
|
||||
return
|
||||
}
|
||||
|
||||
c := iter.nextToken()
|
||||
if c != '"' {
|
||||
iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c}))
|
||||
|
@ -26,6 +26,11 @@ func init() {
|
||||
Field int `json:"field"`
|
||||
})(nil),
|
||||
input: `{"field": null}`,
|
||||
}, unmarshalCase{
|
||||
ptr: (*struct {
|
||||
Field int `json:"field,string"`
|
||||
})(nil),
|
||||
input: `{"field": null}`,
|
||||
}, unmarshalCase{
|
||||
ptr: (*struct {
|
||||
ID int `json:"id"`
|
||||
|
Reference in New Issue
Block a user