diff --git a/jsoniter_reflect.go b/jsoniter_reflect.go index 9bd32ef..5ad0300 100644 --- a/jsoniter_reflect.go +++ b/jsoniter_reflect.go @@ -308,19 +308,46 @@ type sliceHeader struct { } func (decoder *sliceDecoder) decode(ptr unsafe.Pointer, iter *Iterator) { - slice := (*sliceHeader)(ptr) - slice.Len = 0 - for iter.ReadArray() { - offset := uintptr(slice.Len) * decoder.elemType.Size() - growOne(slice, decoder.sliceType, decoder.elemType) - dataPtr := uintptr(slice.Data) + offset - decoder.elemDecoder.decode(unsafe.Pointer(dataPtr), iter) - } + decoder.doDecode(ptr, iter) if iter.Error != nil && iter.Error != io.EOF { iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error()) } } +func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { + slice := (*sliceHeader)(ptr) + reuseSlice(slice, decoder.sliceType, 4) + if !iter.ReadArray() { + return + } + offset := uintptr(0) + decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter) + if !iter.ReadArray() { + slice.Len = 1 + return + } + offset += decoder.elemType.Size() + decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter) + if !iter.ReadArray() { + slice.Len = 2 + return + } + offset += decoder.elemType.Size() + decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter) + if !iter.ReadArray() { + slice.Len = 3 + return + } + offset += decoder.elemType.Size() + decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter) + slice.Len = 4 + for iter.ReadArray() { + growOne(slice, decoder.sliceType, decoder.elemType) + offset += decoder.elemType.Size() + decoder.elemDecoder.decode(unsafe.Pointer(uintptr(slice.Data) + offset), iter) + } +} + // grow grows the slice s so that it can hold extra more values, allocating // more capacity if needed. It also returns the old and new slice lengths. func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Type) { @@ -353,6 +380,15 @@ func growOne(slice *sliceHeader, sliceType reflect.Type, elementType reflect.Typ slice.Data = dst } +func reuseSlice(slice *sliceHeader, sliceType reflect.Type, expectedCap int) { + if expectedCap <= slice.Cap { + return + } + dst := unsafe.Pointer(reflect.MakeSlice(sliceType, 0, expectedCap).Pointer()) + slice.Cap = expectedCap + slice.Data = dst +} + var DECODERS unsafe.Pointer func addDecoderToCache(cacheKey string, decoder Decoder) { diff --git a/jsoniter_reflect_test.go b/jsoniter_reflect_test.go index 54cdd4a..6e031d5 100644 --- a/jsoniter_reflect_test.go +++ b/jsoniter_reflect_test.go @@ -218,7 +218,7 @@ func Test_reflect_struct_tag_field(t *testing.T) { func Test_reflect_slice(t *testing.T) { iter := ParseString(`["hello", "world"]`) - slice := make([]string, 0, 1) + slice := make([]string, 0, 5) iter.Read(&slice) if len(slice) != 2 { fmt.Println(iter.Error) @@ -234,6 +234,24 @@ func Test_reflect_slice(t *testing.T) { } } +func Test_reflect_large_slice(t *testing.T) { + iter := ParseString(`[1,2,3,4,5,6,7,8,9]`) + slice := make([]int, 0, 1) + iter.Read(&slice) + if len(slice) != 9 { + fmt.Println(iter.Error) + t.Fatal(len(slice)) + } + if slice[0] != 1 { + fmt.Println(iter.Error) + t.Fatal(slice[0]) + } + if slice[8] != 9 { + fmt.Println(iter.Error) + t.Fatal(slice[1]) + } +} + func Test_reflect_nested(t *testing.T) { iter := ParseString(`[{"field1": "hello"}, null, {"field2": "world"}]`) slice := []*StructOfString{} @@ -267,12 +285,12 @@ type StructOfTagOne struct { func Benchmark_jsoniter_reflect(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { - iter := ParseString(`{"field3": "100"}`) - struct_ := StructOfTagOne{} - iter.Read(&struct_) - //iter := ParseString(`["hello", "world"]`) - //array := make([]string, 0, 1) - //iter.Read(&array) + //iter := ParseString(`{"field3": "100"}`) + //struct_ := StructOfTagOne{} + //iter.Read(&struct_) + iter := ParseString(`[1,2,3]`) + var array []int + iter.Read(&array) } }