mirror of
https://github.com/json-iterator/go.git
synced 2024-11-27 08:30:57 +02:00
fix read any; reuse existing obj for optional
This commit is contained in:
parent
247a23a637
commit
97849e019f
85
feature_iter_object.go
Normal file
85
feature_iter_object.go
Normal file
@ -0,0 +1,85 @@
|
||||
package jsoniter
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (iter *Iterator) ReadObject() (ret string) {
|
||||
c := iter.nextToken()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
switch c {
|
||||
case 'n': {
|
||||
iter.skipUntilBreak()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
return "" // null
|
||||
}
|
||||
case '{': {
|
||||
c = iter.nextToken()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
switch c {
|
||||
case '}':
|
||||
return "" // end of object
|
||||
case '"':
|
||||
iter.unreadByte()
|
||||
return iter.readObjectField()
|
||||
default:
|
||||
iter.ReportError("ReadObject", `expect " after {`)
|
||||
return
|
||||
}
|
||||
}
|
||||
case ',':
|
||||
return iter.readObjectField()
|
||||
case '}':
|
||||
return "" // end of object
|
||||
default:
|
||||
iter.ReportError("ReadObject", `expect { or , or } or n`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectStart() bool {
|
||||
c := iter.nextToken()
|
||||
if c == '{' {
|
||||
c = iter.nextToken()
|
||||
if c == '}' {
|
||||
return false
|
||||
}
|
||||
iter.unreadByte()
|
||||
return true
|
||||
}
|
||||
iter.ReportError("readObjectStart", "expect { ")
|
||||
return false
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectField() (ret string) {
|
||||
str := iter.readStringAsBytes()
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == "" {
|
||||
ret = string(str);
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if iter.buf[iter.head] != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field")
|
||||
return
|
||||
}
|
||||
iter.head++
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == "" {
|
||||
ret = string(str);
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ret == "" {
|
||||
return *(*string)(unsafe.Pointer(&str))
|
||||
}
|
||||
return ret
|
||||
}
|
@ -11,6 +11,15 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
/*
|
||||
Reflection on type to create decoders, which is then cached
|
||||
Reflection on value is avoided as we can, as the reflect.Value itself will allocate, with following exceptions
|
||||
1. create instance of new value, for example *int will need a int to be allocated
|
||||
2. append to slice, if the existing cap is not enough, allocate will be done using Reflect.New
|
||||
3. assignment to map, both key and value will be reflect.Value
|
||||
For a simple struct binding, it will be reflect.Value free and allocation free
|
||||
*/
|
||||
|
||||
type Decoder interface {
|
||||
decode(ptr unsafe.Pointer, iter *Iterator)
|
||||
}
|
||||
@ -133,7 +142,7 @@ type stringNumberDecoder struct {
|
||||
}
|
||||
|
||||
func (decoder *stringNumberDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
c := iter.readByte()
|
||||
c := iter.nextToken()
|
||||
if c != '"' {
|
||||
iter.ReportError("stringNumberDecoder", `expect "`)
|
||||
return
|
||||
@ -158,9 +167,15 @@ func (decoder *optionalDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
if iter.ReadNull() {
|
||||
*((*unsafe.Pointer)(ptr)) = nil
|
||||
} else {
|
||||
value := reflect.New(decoder.valueType)
|
||||
decoder.valueDecoder.decode(unsafe.Pointer(value.Pointer()), iter)
|
||||
*((*uintptr)(ptr)) = value.Pointer()
|
||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||
// pointer to null, we have to allocate memory to hold the value
|
||||
value := reflect.New(decoder.valueType)
|
||||
decoder.valueDecoder.decode(unsafe.Pointer(value.Pointer()), iter)
|
||||
*((*uintptr)(ptr)) = value.Pointer()
|
||||
} else {
|
||||
// reuse existing instance
|
||||
decoder.valueDecoder.decode(*((*unsafe.Pointer)(ptr)), iter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,7 +293,24 @@ type fourFieldsStructDecoder struct {
|
||||
}
|
||||
|
||||
func (decoder *fourFieldsStructDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
|
||||
if !iter.readObjectStart() {
|
||||
return
|
||||
}
|
||||
field := iter.readObjectField()
|
||||
switch field {
|
||||
case decoder.fieldName1:
|
||||
decoder.fieldDecoder1.decode(ptr, iter)
|
||||
case decoder.fieldName2:
|
||||
decoder.fieldDecoder2.decode(ptr, iter)
|
||||
case decoder.fieldName3:
|
||||
decoder.fieldDecoder3.decode(ptr, iter)
|
||||
case decoder.fieldName4:
|
||||
decoder.fieldDecoder4.decode(ptr, iter)
|
||||
default:
|
||||
iter.Skip()
|
||||
}
|
||||
for iter.nextToken() != ',' {
|
||||
field := iter.readObjectField()
|
||||
switch field {
|
||||
case decoder.fieldName1:
|
||||
decoder.fieldDecoder1.decode(ptr, iter)
|
||||
@ -327,6 +359,7 @@ func (decoder *mapDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
|
||||
elem := reflect.New(decoder.elemType)
|
||||
decoder.elemDecoder.decode(unsafe.Pointer(elem.Pointer()), iter)
|
||||
// to put into map, we have to use reflection
|
||||
realVal.SetMapIndex(reflect.ValueOf(string([]byte(field))), elem.Elem())
|
||||
}
|
||||
}
|
||||
@ -406,6 +439,7 @@ func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Typ
|
||||
}
|
||||
}
|
||||
dst := unsafe.Pointer(reflect.MakeSlice(sliceType, newLen, newCap).Pointer())
|
||||
// copy old array into new array
|
||||
originalBytesCount := uintptr(slice.Len) * elementType.Size()
|
||||
srcPtr := (*[1 << 30]byte)(slice.Data)
|
||||
dstPtr := (*[1 << 30]byte)(dst)
|
||||
@ -554,9 +588,13 @@ func (iter *Iterator) readNumber() (ret *Any) {
|
||||
str = append(str, c)
|
||||
continue
|
||||
default:
|
||||
iter.head = i
|
||||
hasMore = false
|
||||
break
|
||||
}
|
||||
if !hasMore {
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasMore {
|
||||
if !iter.loadMore() {
|
||||
@ -632,14 +670,6 @@ func decoderOfPtr(type_ reflect.Type) (Decoder, error) {
|
||||
if typeName == "jsoniter.Any" {
|
||||
return &anyDecoder{}, nil
|
||||
}
|
||||
|
||||
for _, extension := range extensions {
|
||||
alternativeFieldNames, func_ := extension(type_, nil)
|
||||
if alternativeFieldNames != nil {
|
||||
return nil, fmt.Errorf("%v should not return alternative field names when only type is being passed", extension)
|
||||
}
|
||||
typeDecoders[typeName] = &funcDecoder{func_}
|
||||
}
|
||||
typeDecoder := typeDecoders[typeName]
|
||||
if typeDecoder != nil {
|
||||
return typeDecoder, nil
|
||||
|
125
jsoniter.go
125
jsoniter.go
@ -69,24 +69,31 @@ type Iterator struct {
|
||||
Error error
|
||||
}
|
||||
|
||||
func Create() *Iterator {
|
||||
return &Iterator{
|
||||
reader: nil,
|
||||
buf: nil,
|
||||
head: 0,
|
||||
tail: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func Parse(reader io.Reader, bufSize int) *Iterator {
|
||||
iter := &Iterator{
|
||||
return &Iterator{
|
||||
reader: reader,
|
||||
buf: make([]byte, bufSize),
|
||||
head: 0,
|
||||
tail: 0,
|
||||
}
|
||||
return iter
|
||||
}
|
||||
|
||||
func ParseBytes(input []byte) *Iterator {
|
||||
iter := &Iterator{
|
||||
return &Iterator{
|
||||
reader: nil,
|
||||
buf: input,
|
||||
head: 0,
|
||||
tail: len(input),
|
||||
}
|
||||
return iter
|
||||
}
|
||||
|
||||
func ParseString(input string) *Iterator {
|
||||
@ -101,7 +108,6 @@ func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
||||
}
|
||||
|
||||
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
||||
// only for benchmarking
|
||||
iter.reader = nil
|
||||
iter.Error = nil
|
||||
iter.buf = input
|
||||
@ -111,7 +117,7 @@ func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
||||
}
|
||||
|
||||
func (iter *Iterator) WhatIsNext() ValueType {
|
||||
valueType := valueTypes[iter.readByte()];
|
||||
valueType := valueTypes[iter.nextToken()];
|
||||
iter.unreadByte();
|
||||
return valueType;
|
||||
}
|
||||
@ -261,7 +267,7 @@ func (iter *Iterator) ReadUint32() (ret uint32) {
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadUint64() (ret uint64) {
|
||||
c := iter.readByte()
|
||||
c := iter.nextToken()
|
||||
v := digits[c]
|
||||
if v == 0 {
|
||||
return 0 // single zero
|
||||
@ -327,7 +333,7 @@ func (iter *Iterator) ReadInt32() (ret int32) {
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadInt64() (ret int64) {
|
||||
c := iter.readByte()
|
||||
c := iter.nextToken()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
@ -350,22 +356,27 @@ func (iter *Iterator) ReadString() (ret string) {
|
||||
|
||||
func (iter *Iterator) readStringAsBytes() (ret []byte) {
|
||||
c := iter.nextToken()
|
||||
if c == '"' {
|
||||
end := iter.findStringEndWithoutEscape()
|
||||
if end != -1 {
|
||||
// fast path: reuse the underlying buffer
|
||||
ret = iter.buf[iter.head:end-1]
|
||||
iter.head = end
|
||||
return ret
|
||||
}
|
||||
return iter.readStringAsBytesSlowPath()
|
||||
}
|
||||
if c == 'n' {
|
||||
iter.skipUntilBreak()
|
||||
return
|
||||
}
|
||||
if c != '"' {
|
||||
iter.ReportError("ReadString", `expects " or n`)
|
||||
return
|
||||
}
|
||||
end := iter.findStringEndWithoutEscape()
|
||||
if end != -1 {
|
||||
// fast path: reuse the underlying buffer
|
||||
ret = iter.buf[iter.head:end-1]
|
||||
iter.head = end
|
||||
return ret
|
||||
}
|
||||
iter.ReportError("ReadString", `expects " or n`)
|
||||
return
|
||||
}
|
||||
|
||||
func (iter *Iterator) readStringAsBytesSlowPath() (ret []byte) {
|
||||
str := make([]byte, 0, 8)
|
||||
var c byte
|
||||
for iter.Error == nil {
|
||||
c = iter.readByte()
|
||||
if c == '"' {
|
||||
@ -541,79 +552,11 @@ func (iter *Iterator) ReadArray() (ret bool) {
|
||||
case ',':
|
||||
return true
|
||||
default:
|
||||
iter.ReportError("ReadArray", "expect [ or , or ] or n")
|
||||
iter.ReportError("ReadArray", "expect [ or , or ] or n, but found: " + string([]byte{c}))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadObject() (ret string) {
|
||||
c := iter.nextToken()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
switch c {
|
||||
case 'n': {
|
||||
iter.skipUntilBreak()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
return "" // null
|
||||
}
|
||||
case '{': {
|
||||
c = iter.nextToken()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
switch c {
|
||||
case '}':
|
||||
return "" // end of object
|
||||
case '"':
|
||||
iter.unreadByte()
|
||||
return iter.readObjectField()
|
||||
default:
|
||||
iter.ReportError("ReadObject", `expect " after {`)
|
||||
return
|
||||
}
|
||||
}
|
||||
case ',':
|
||||
return iter.readObjectField()
|
||||
case '}':
|
||||
return "" // end of object
|
||||
default:
|
||||
iter.ReportError("ReadObject", `expect { or , or } or n`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readObjectField() (ret string) {
|
||||
str := iter.readStringAsBytes()
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == "" {
|
||||
ret = string(str);
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if iter.buf[iter.head] != ':' {
|
||||
iter.ReportError("ReadObject", "expect : after object field")
|
||||
return
|
||||
}
|
||||
iter.head++
|
||||
if iter.skipWhitespacesWithoutLoadMore() {
|
||||
if ret == "" {
|
||||
ret = string(str);
|
||||
}
|
||||
if !iter.loadMore() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ret == "" {
|
||||
return *(*string)(unsafe.Pointer(&str))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadFloat32() (ret float32) {
|
||||
strBuf := [8]byte{}
|
||||
str := strBuf[0:0]
|
||||
@ -681,7 +624,7 @@ func (iter *Iterator) ReadFloat64() (ret float64) {
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadBool() (ret bool) {
|
||||
c := iter.readByte()
|
||||
c := iter.nextToken()
|
||||
if iter.Error != nil {
|
||||
return
|
||||
}
|
||||
@ -714,7 +657,7 @@ func (iter *Iterator) ReadBase64() (ret []byte) {
|
||||
}
|
||||
|
||||
func (iter *Iterator) ReadNull() (ret bool) {
|
||||
c := iter.readByte()
|
||||
c := iter.nextToken()
|
||||
if c == 'n' {
|
||||
iter.skipUntilBreak()
|
||||
return true
|
||||
@ -724,7 +667,7 @@ func (iter *Iterator) ReadNull() (ret bool) {
|
||||
}
|
||||
|
||||
func (iter *Iterator) Skip() {
|
||||
c := iter.readByte()
|
||||
c := iter.nextToken()
|
||||
switch c {
|
||||
case '"':
|
||||
iter.skipString()
|
||||
|
@ -8,6 +8,9 @@ import (
|
||||
func Test_read_string_as_any(t *testing.T) {
|
||||
iter := ParseString(`[1, {"hello": "world"}, 2]`)
|
||||
any := iter.ReadAny()
|
||||
if iter.Error != nil {
|
||||
t.Fatal(iter.Error)
|
||||
}
|
||||
if any.ToString(1, "hello") != "world" {
|
||||
t.FailNow()
|
||||
}
|
||||
|
@ -45,45 +45,25 @@ func Test_customize_field_decoder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type TestObject1 struct {
|
||||
field1 string
|
||||
}
|
||||
|
||||
func Test_customize_field_by_extension(t *testing.T) {
|
||||
RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc) {
|
||||
if (type_.String() == "jsoniter.Tom" && field.Name == "field1") {
|
||||
if (type_.String() == "jsoniter.TestObject1" && field.Name == "field1") {
|
||||
return []string{"field-1"}, func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
tom := Tom{}
|
||||
err := Unmarshal([]byte(`{"field-1": 100}`), &tom)
|
||||
obj := TestObject1{}
|
||||
err := Unmarshal([]byte(`{"field-1": 100}`), &obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tom.field1 != "100" {
|
||||
t.Fatal(tom.field1)
|
||||
}
|
||||
}
|
||||
|
||||
type Jerry struct {
|
||||
field1 string
|
||||
}
|
||||
|
||||
func Test_customize_type_by_extension(t *testing.T) {
|
||||
RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, DecoderFunc) {
|
||||
if (type_.String() == "jsoniter.Jerry" && field == nil) {
|
||||
return nil, func(ptr unsafe.Pointer, iter *Iterator) {
|
||||
obj := (*Jerry)(ptr)
|
||||
obj.field1 = iter.ReadString()
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
jerry := Jerry{}
|
||||
err := Unmarshal([]byte(`"100"`), &jerry)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if jerry.field1 != "100" {
|
||||
t.Fatal(jerry.field1)
|
||||
if obj.field1 != "100" {
|
||||
t.Fatal(obj.field1)
|
||||
}
|
||||
}
|
@ -7,21 +7,21 @@ import (
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func Test_large_file(t *testing.T) {
|
||||
file, err := os.Open("/tmp/large-file.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
iter := Parse(file, 4096)
|
||||
count := 0
|
||||
for iter.ReadArray() {
|
||||
iter.Skip()
|
||||
count++
|
||||
}
|
||||
if count != 11351 {
|
||||
t.Fatal(count)
|
||||
}
|
||||
}
|
||||
//func Test_large_file(t *testing.T) {
|
||||
// file, err := os.Open("/tmp/large-file.json")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// iter := Parse(file, 4096)
|
||||
// count := 0
|
||||
// for iter.ReadArray() {
|
||||
// iter.Skip()
|
||||
// count++
|
||||
// }
|
||||
// if count != 11351 {
|
||||
// t.Fatal(count)
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
func Benchmark_jsoniter_large_file(b *testing.B) {
|
||||
|
@ -297,13 +297,14 @@ type StructOfTagOne struct {
|
||||
|
||||
func Benchmark_jsoniter_reflect(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
iter := Create()
|
||||
struct_ := &StructOfTagOne{}
|
||||
//var struct_ *StructOfTagOne
|
||||
input := []byte(`{"field3": "100", "field4": "100"}`)
|
||||
//input := []byte(`null`)
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter := ParseString(`{"field3": "100"}`)
|
||||
struct_ := StructOfTagOne{}
|
||||
iter.ResetBytes(input)
|
||||
iter.Read(&struct_)
|
||||
//iter := ParseString(`[1,2,3]`)
|
||||
//var array []int
|
||||
//iter.Read(&array)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user