From ba3857729b5165ec0e7d5e2cdb77434dfc0af359 Mon Sep 17 00:00:00 2001 From: Matt Good Date: Tue, 16 Jan 2018 11:02:03 -0800 Subject: [PATCH 1/2] Fix encoding 0-length arrays The array encoder assumed that arrays had at least one value, so it would serialize them with a zero-value for the array, such as `[0]`. This adds a test to reproduce the issue, and updates the encoder to write an empty array if the length is 0. --- feature_reflect_array.go | 4 ++++ jsoniter_fixed_array_test.go | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/feature_reflect_array.go b/feature_reflect_array.go index f4e211dc..7eb2af92 100644 --- a/feature_reflect_array.go +++ b/feature_reflect_array.go @@ -27,6 +27,10 @@ type arrayEncoder struct { } func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + if encoder.arrayType.Len() == 0 { + stream.WriteEmptyArray() + return + } stream.WriteArrayStart() elemPtr := unsafe.Pointer(ptr) encoder.elemEncoder.Encode(elemPtr, stream) diff --git a/jsoniter_fixed_array_test.go b/jsoniter_fixed_array_test.go index 6824b119..cbe50a0e 100644 --- a/jsoniter_fixed_array_test.go +++ b/jsoniter_fixed_array_test.go @@ -15,6 +15,15 @@ func Test_encode_fixed_array(t *testing.T) { should.Equal("[0.1,1]", output) } +func Test_encode_fixed_array_empty(t *testing.T) { + should := require.New(t) + type FixedArray [0]float64 + fixed := FixedArray{} + output, err := MarshalToString(fixed) + should.Nil(err) + should.Equal("[]", output) +} + func Test_encode_fixed_array_of_map(t *testing.T) { should := require.New(t) type FixedArray [2]map[string]string From 807e4a8b20486bd718a07c9f4194e398b4d67733 Mon Sep 17 00:00:00 2001 From: Matt Good Date: Mon, 22 Jan 2018 14:03:50 -0800 Subject: [PATCH 2/2] Optimize 0-length array case Instead of checking the array length in encode, this can be checked up front in `encoderOfArray` since the array type has a fixed length determined at compile time. So return an `emptyArrayEncoder` that simply writes an empty array to the stream. --- feature_reflect_array.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/feature_reflect_array.go b/feature_reflect_array.go index 7eb2af92..a6dd91ca 100644 --- a/feature_reflect_array.go +++ b/feature_reflect_array.go @@ -13,6 +13,9 @@ func decoderOfArray(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecod } func encoderOfArray(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder { + if typ.Len() == 0 { + return emptyArrayEncoder{} + } encoder := encoderOfType(cfg, prefix+"[array]->", typ.Elem()) if typ.Elem().Kind() == reflect.Map { encoder = &OptionalEncoder{encoder} @@ -20,6 +23,20 @@ func encoderOfArray(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncod return &arrayEncoder{typ, typ.Elem(), encoder} } +type emptyArrayEncoder struct{} + +func (encoder emptyArrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteEmptyArray() +} + +func (encoder emptyArrayEncoder) EncodeInterface(val interface{}, stream *Stream) { + stream.WriteEmptyArray() +} + +func (encoder emptyArrayEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return true +} + type arrayEncoder struct { arrayType reflect.Type elemType reflect.Type @@ -27,10 +44,6 @@ type arrayEncoder struct { } func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - if encoder.arrayType.Len() == 0 { - stream.WriteEmptyArray() - return - } stream.WriteArrayStart() elemPtr := unsafe.Pointer(ptr) encoder.elemEncoder.Encode(elemPtr, stream)