diff --git a/feature_reflect.go b/feature_reflect.go index 78f3d03..63a8ea1 100644 --- a/feature_reflect.go +++ b/feature_reflect.go @@ -348,6 +348,8 @@ func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { } case reflect.Struct: return prefix(fmt.Sprintf("[%s]", typ.String())).addToDecoder(decoderOfStruct(cfg, typ)) + case reflect.Array: + return prefix("[array]").addToDecoder(decoderOfArray(cfg, typ)) case reflect.Slice: return prefix("[slice]").addToDecoder(decoderOfSlice(cfg, typ)) case reflect.Map: @@ -442,6 +444,8 @@ func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { } case reflect.Struct: return prefix(fmt.Sprintf("[%s]", typ.String())).addToEncoder(encoderOfStruct(cfg, typ)) + case reflect.Array: + return prefix("[array]").addToEncoder(encoderOfArray(cfg, typ)) case reflect.Slice: return prefix("[slice]").addToEncoder(encoderOfSlice(cfg, typ)) case reflect.Map: diff --git a/feature_reflect_array.go b/feature_reflect_array.go index abf5127..dd38565 100644 --- a/feature_reflect_array.go +++ b/feature_reflect_array.go @@ -1,21 +1,21 @@ package jsoniter import ( - "fmt" - "io" "reflect" "unsafe" + "io" + "fmt" ) -func decoderOfSlice(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { +func decoderOfArray(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { decoder, err := decoderOfType(cfg, typ.Elem()) if err != nil { return nil, err } - return &sliceDecoder{typ, typ.Elem(), decoder}, nil + return &arrayDecoder{typ, typ.Elem(), decoder}, nil } -func encoderOfSlice(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { +func encoderOfArray(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { encoder, err := encoderOfType(cfg, typ.Elem()) if err != nil { return nil, err @@ -23,140 +23,62 @@ func encoderOfSlice(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { if typ.Elem().Kind() == reflect.Map { encoder = &optionalEncoder{encoder} } - return &sliceEncoder{typ, typ.Elem(), encoder}, nil + return &arrayEncoder{typ, typ.Elem(), encoder}, nil } -type sliceEncoder struct { - sliceType reflect.Type +type arrayEncoder struct { + arrayType reflect.Type elemType reflect.Type elemEncoder Encoder } -func (encoder *sliceEncoder) encode(ptr unsafe.Pointer, stream *Stream) { - slice := (*sliceHeader)(ptr) - if slice.Data == nil { +func (encoder *arrayEncoder) encode(ptr unsafe.Pointer, stream *Stream) { + if ptr == nil { stream.WriteNil() return } - if slice.Len == 0 { - stream.WriteEmptyArray() - return - } stream.WriteArrayStart() - elemPtr := uintptr(slice.Data) + elemPtr := uintptr(ptr) encoder.elemEncoder.encode(unsafe.Pointer(elemPtr), stream) - for i := 1; i < slice.Len; i++ { + for i := 1; i < encoder.arrayType.Len(); i++ { stream.WriteMore() elemPtr += encoder.elemType.Size() encoder.elemEncoder.encode(unsafe.Pointer(elemPtr), stream) } stream.WriteArrayEnd() if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error()) + stream.Error = fmt.Errorf("%v: %s", encoder.arrayType, stream.Error.Error()) } } -func (encoder *sliceEncoder) encodeInterface(val interface{}, stream *Stream) { +func (encoder *arrayEncoder) encodeInterface(val interface{}, stream *Stream) { writeToStream(val, stream, encoder) } -func (encoder *sliceEncoder) isEmpty(ptr unsafe.Pointer) bool { - slice := (*sliceHeader)(ptr) - return slice.Len == 0 +func (encoder *arrayEncoder) isEmpty(ptr unsafe.Pointer) bool { + return false } -type sliceDecoder struct { - sliceType reflect.Type +type arrayDecoder struct { + arrayType reflect.Type elemType reflect.Type elemDecoder Decoder } -// sliceHeader is a safe version of SliceHeader used within this package. -type sliceHeader struct { - Data unsafe.Pointer - Len int - Cap int -} - -func (decoder *sliceDecoder) decode(ptr unsafe.Pointer, iter *Iterator) { +func (decoder *arrayDecoder) decode(ptr unsafe.Pointer, iter *Iterator) { decoder.doDecode(ptr, iter) if iter.Error != nil && iter.Error != io.EOF { - iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error()) + iter.Error = fmt.Errorf("%v: %s", decoder.arrayType, iter.Error.Error()) } } -func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { - slice := (*sliceHeader)(ptr) - reuseSlice(slice, decoder.sliceType, 4) - if !iter.ReadArray() { - return - } +func (decoder *arrayDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { 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) { - newLen := slice.Len + 1 - if newLen <= slice.Cap { - slice.Len = newLen - return - } - newCap := slice.Cap - if newCap == 0 { - newCap = 1 - } else { - for newCap < newLen { - if slice.Len < 1024 { - newCap += newCap - } else { - newCap += newCap / 4 - } + for ; iter.ReadArray(); offset += decoder.elemType.Size() { + if offset < decoder.arrayType.Size() { + decoder.elemDecoder.decode(unsafe.Pointer(uintptr(ptr)+offset), iter) + } else { + iter.Skip() } } - 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) - for i := uintptr(0); i < originalBytesCount; i++ { - dstPtr[i] = srcPtr[i] - } - slice.Len = newLen - slice.Cap = newCap - 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 } diff --git a/feature_reflect_slice.go b/feature_reflect_slice.go new file mode 100644 index 0000000..abf5127 --- /dev/null +++ b/feature_reflect_slice.go @@ -0,0 +1,162 @@ +package jsoniter + +import ( + "fmt" + "io" + "reflect" + "unsafe" +) + +func decoderOfSlice(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { + decoder, err := decoderOfType(cfg, typ.Elem()) + if err != nil { + return nil, err + } + return &sliceDecoder{typ, typ.Elem(), decoder}, nil +} + +func encoderOfSlice(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { + encoder, err := encoderOfType(cfg, typ.Elem()) + if err != nil { + return nil, err + } + if typ.Elem().Kind() == reflect.Map { + encoder = &optionalEncoder{encoder} + } + return &sliceEncoder{typ, typ.Elem(), encoder}, nil +} + +type sliceEncoder struct { + sliceType reflect.Type + elemType reflect.Type + elemEncoder Encoder +} + +func (encoder *sliceEncoder) encode(ptr unsafe.Pointer, stream *Stream) { + slice := (*sliceHeader)(ptr) + if slice.Data == nil { + stream.WriteNil() + return + } + if slice.Len == 0 { + stream.WriteEmptyArray() + return + } + stream.WriteArrayStart() + elemPtr := uintptr(slice.Data) + encoder.elemEncoder.encode(unsafe.Pointer(elemPtr), stream) + for i := 1; i < slice.Len; i++ { + stream.WriteMore() + elemPtr += encoder.elemType.Size() + encoder.elemEncoder.encode(unsafe.Pointer(elemPtr), stream) + } + stream.WriteArrayEnd() + if stream.Error != nil && stream.Error != io.EOF { + stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error()) + } +} + +func (encoder *sliceEncoder) encodeInterface(val interface{}, stream *Stream) { + writeToStream(val, stream, encoder) +} + +func (encoder *sliceEncoder) isEmpty(ptr unsafe.Pointer) bool { + slice := (*sliceHeader)(ptr) + return slice.Len == 0 +} + +type sliceDecoder struct { + sliceType reflect.Type + elemType reflect.Type + elemDecoder Decoder +} + +// sliceHeader is a safe version of SliceHeader used within this package. +type sliceHeader struct { + Data unsafe.Pointer + Len int + Cap int +} + +func (decoder *sliceDecoder) decode(ptr unsafe.Pointer, iter *Iterator) { + 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) { + newLen := slice.Len + 1 + if newLen <= slice.Cap { + slice.Len = newLen + return + } + newCap := slice.Cap + if newCap == 0 { + newCap = 1 + } else { + for newCap < newLen { + if slice.Len < 1024 { + newCap += newCap + } else { + newCap += newCap / 4 + } + } + } + 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) + for i := uintptr(0); i < originalBytesCount; i++ { + dstPtr[i] = srcPtr[i] + } + slice.Len = newLen + slice.Cap = newCap + 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 +} diff --git a/jsoniter_fixed_array_test.go b/jsoniter_fixed_array_test.go new file mode 100644 index 0000000..586bbd7 --- /dev/null +++ b/jsoniter_fixed_array_test.go @@ -0,0 +1,37 @@ +package jsoniter + +import ( + "testing" + "github.com/json-iterator/go/require" + "encoding/json" +) + +func Test_encode_fixed_array(t *testing.T) { + should := require.New(t) + type FixedArray [2]float64 + fixed := FixedArray{0.1, 1.0} + output, err := MarshalToString(fixed) + should.Nil(err) + should.Equal("[0.1,1]", output) +} + +func Test_encode_fixed_array_of_map(t *testing.T) { + should := require.New(t) + type FixedArray [2]map[string]string + fixed := FixedArray{map[string]string{"1": "2"}, map[string]string{"3": "4"}} + output, err := MarshalToString(fixed) + should.Nil(err) + should.Equal(`[{"1":"2"},{"3":"4"}]`, output) +} + +func Test_decode_fixed_array(t *testing.T) { + should := require.New(t) + type FixedArray [2]float64 + var fixed FixedArray + should.Nil(json.Unmarshal([]byte("[1,2,3]"), &fixed)) + should.Equal(float64(1), fixed[0]) + should.Equal(float64(2), fixed[1]) + should.Nil(Unmarshal([]byte("[1,2,3]"), &fixed)) + should.Equal(float64(1), fixed[0]) + should.Equal(float64(2), fixed[1]) +}