mirror of
https://github.com/json-iterator/go.git
synced 2025-03-23 21:09:11 +02:00
support struct
This commit is contained in:
parent
18a2587df6
commit
dd431da523
@ -3,24 +3,59 @@ package jsoniter
|
||||
import (
|
||||
"reflect"
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Decoder interface {
|
||||
decode(iter *Iterator, obj interface{})
|
||||
decode(ptr unsafe.Pointer, iter *Iterator)
|
||||
}
|
||||
|
||||
type stringDecoder struct {
|
||||
}
|
||||
|
||||
func (decoder *stringDecoder) decode(iter *Iterator, obj interface{}) {
|
||||
ptr := obj.(*string)
|
||||
*ptr = iter.ReadString()
|
||||
func (decoder *stringDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
*((*string)(ptr)) = iter.ReadString()
|
||||
}
|
||||
|
||||
type structDecoder struct {
|
||||
fields map[string]Decoder
|
||||
}
|
||||
|
||||
func (decoder *structDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
|
||||
fieldDecoder := decoder.fields[field]
|
||||
if fieldDecoder == nil {
|
||||
iter.Skip()
|
||||
} else {
|
||||
fieldDecoder.decode(ptr, iter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type structFieldDecoder struct {
|
||||
offset uintptr
|
||||
fieldDecoder Decoder
|
||||
}
|
||||
|
||||
func (decoder *structFieldDecoder) decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||
fieldPtr := uintptr(ptr) + decoder.offset
|
||||
decoder.fieldDecoder.decode(unsafe.Pointer(fieldPtr), iter)
|
||||
}
|
||||
|
||||
var DECODER_STRING *stringDecoder
|
||||
var DECODERS_STRUCT unsafe.Pointer
|
||||
|
||||
func init() {
|
||||
DECODER_STRING = &stringDecoder{}
|
||||
atomic.StorePointer(&DECODERS_STRUCT, unsafe.Pointer(&map[string]*structDecoder{}))
|
||||
}
|
||||
|
||||
// emptyInterface is the header for an interface{} value.
|
||||
type emptyInterface struct {
|
||||
typ *struct{}
|
||||
word unsafe.Pointer
|
||||
}
|
||||
|
||||
func (iter *Iterator) Read(obj interface{}) {
|
||||
@ -30,13 +65,23 @@ func (iter *Iterator) Read(obj interface{}) {
|
||||
iter.Error = err
|
||||
return
|
||||
}
|
||||
decoder.decode(iter, obj)
|
||||
e := (*emptyInterface)(unsafe.Pointer(&obj))
|
||||
decoder.decode(e.word, iter)
|
||||
}
|
||||
|
||||
type prefix string
|
||||
|
||||
func (p prefix) addTo(decoder Decoder, err error) (Decoder, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", p, err.Error())
|
||||
}
|
||||
return decoder, err
|
||||
}
|
||||
|
||||
func decoderOfType(type_ reflect.Type) (Decoder, error) {
|
||||
switch type_.Kind() {
|
||||
case reflect.Ptr:
|
||||
return decoderOfPtr(type_.Elem())
|
||||
return prefix("ptr").addTo(decoderOfPtr(type_.Elem()))
|
||||
default:
|
||||
return nil, errors.New("expect ptr")
|
||||
}
|
||||
@ -46,8 +91,49 @@ func decoderOfPtr(type_ reflect.Type) (Decoder, error) {
|
||||
switch type_.Kind() {
|
||||
case reflect.String:
|
||||
return DECODER_STRING, nil
|
||||
case reflect.Struct:
|
||||
return decoderOfStruct(type_)
|
||||
default:
|
||||
return nil, errors.New("expect string")
|
||||
}
|
||||
}
|
||||
|
||||
func decoderOfStruct(type_ reflect.Type) (Decoder, error) {
|
||||
cacheKey := type_.String()
|
||||
cachedDecoder := getStructDecoderFromCache(cacheKey)
|
||||
if cachedDecoder == nil {
|
||||
fields := map[string]Decoder{}
|
||||
for i := 0; i < type_.NumField(); i++ {
|
||||
field := type_.Field(i)
|
||||
decoder, err := decoderOfPtr(field.Type)
|
||||
if err != nil {
|
||||
return prefix(fmt.Sprintf("[%s]", field.Name)).addTo(decoder, err)
|
||||
}
|
||||
fields[field.Name] = &structFieldDecoder{field.Offset, decoder}
|
||||
}
|
||||
cachedDecoder = &structDecoder{fields}
|
||||
addStructDecoderToCache(cacheKey, cachedDecoder)
|
||||
}
|
||||
return cachedDecoder, nil
|
||||
}
|
||||
|
||||
func addStructDecoderToCache(cacheKey string, decoder *structDecoder) {
|
||||
retry := true
|
||||
for retry {
|
||||
ptr := atomic.LoadPointer(&DECODERS_STRUCT)
|
||||
cache := *(*map[string]*structDecoder)(ptr)
|
||||
copy := map[string]*structDecoder{}
|
||||
for k, v := range cache {
|
||||
copy[k] = v
|
||||
}
|
||||
copy[cacheKey] = decoder
|
||||
retry = !atomic.CompareAndSwapPointer(&DECODERS_STRUCT, ptr, unsafe.Pointer(©))
|
||||
}
|
||||
}
|
||||
|
||||
func getStructDecoderFromCache(cacheKey string) *structDecoder {
|
||||
ptr := atomic.LoadPointer(&DECODERS_STRUCT)
|
||||
cache := *(*map[string]*structDecoder)(ptr)
|
||||
return cache[cacheKey]
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@ package jsoniter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func Test_reflect_str(t *testing.T) {
|
||||
@ -9,6 +11,60 @@ func Test_reflect_str(t *testing.T) {
|
||||
str := ""
|
||||
iter.Read(&str)
|
||||
if str != "hello" {
|
||||
t.FailNow()
|
||||
t.Fatal(str)
|
||||
}
|
||||
}
|
||||
|
||||
type StructOfString struct {
|
||||
field1 string
|
||||
field2 string
|
||||
}
|
||||
|
||||
func Test_reflect_struct(t *testing.T) {
|
||||
iter := ParseString(`{"field1": "hello", "field2": "world"}`)
|
||||
struct_ := StructOfString{}
|
||||
iter.Read(&struct_)
|
||||
if struct_.field1 != "hello" {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(struct_.field1)
|
||||
}
|
||||
if struct_.field2 != "world" {
|
||||
fmt.Println(iter.Error)
|
||||
t.Fatal(struct_.field1)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_reflect(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter := ParseString(`{"field1": "hello", "field2": "world"}`)
|
||||
struct_ := StructOfString{}
|
||||
iter.Read(&struct_)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_jsoniter_direct(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
iter := ParseString(`{"field1": "hello", "field2": "world"}`)
|
||||
struct_ := StructOfString{}
|
||||
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
|
||||
switch field {
|
||||
case "field1":
|
||||
struct_.field1 = iter.ReadString()
|
||||
case "field2":
|
||||
struct_.field2 = iter.ReadString()
|
||||
default:
|
||||
iter.Skip()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_json_reflect(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
struct_ := StructOfString{}
|
||||
json.Unmarshal([]byte(`{"field1": "hello", "field2": "world"}`), &struct_)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user