From c3f5a2c536711bb648f66aa94264a551464a4825 Mon Sep 17 00:00:00 2001
From: Tao Wen <taowen@gmail.com>
Date: Mon, 19 Jun 2017 23:43:28 +0800
Subject: [PATCH] #64 support fixed array

---
 feature_reflect.go           |   4 +
 feature_reflect_array.go     | 130 ++++++----------------------
 feature_reflect_slice.go     | 162 +++++++++++++++++++++++++++++++++++
 jsoniter_fixed_array_test.go |  37 ++++++++
 4 files changed, 229 insertions(+), 104 deletions(-)
 create mode 100644 feature_reflect_slice.go
 create mode 100644 jsoniter_fixed_array_test.go

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])
+}