From 8f5f28614caaee6df9b9c14972a7f9e3ef7bb8a0 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Mon, 30 Aug 2021 11:39:27 +0900 Subject: [PATCH 1/3] Add test case --- decode_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/decode_test.go b/decode_test.go index f52069b..4b6dcc0 100644 --- a/decode_test.go +++ b/decode_test.go @@ -3732,3 +3732,46 @@ func TestDecodeBinaryTypeWithEscapedChar(t *testing.T) { } }) } + +func TestIssue282(t *testing.T) { + var J = []byte(`{ + "a": {}, + "b": {}, + "c": {}, + "d": {}, + "e": {}, + "f": {}, + "g": {}, + "h": { + "m": "1" + }, + "i": {} +}`) + + type T4 struct { + F0 string + F1 string + F2 string + F3 string + F4 string + F5 string + F6 int + } + type T3 struct { + F0 string + F1 T4 + } + type T2 struct { + F0 string `json:"m"` + F1 T3 + } + type T0 map[string]T2 + + var v T0 + if err := json.Unmarshal(J, &v); err != nil { + t.Fatal(err) + } + if v["h"].F0 != "1" { + t.Fatalf("failed to assign map value") + } +} From 284c1086384c76356b93443fbe75c0573e5ada20 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Mon, 30 Aug 2021 11:40:10 +0900 Subject: [PATCH 2/3] Fix mapassign --- internal/decoder/map.go | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/internal/decoder/map.go b/internal/decoder/map.go index dd480e1..d26a352 100644 --- a/internal/decoder/map.go +++ b/internal/decoder/map.go @@ -9,26 +9,29 @@ import ( ) type mapDecoder struct { - mapType *runtime.Type - keyType *runtime.Type - valueType *runtime.Type - stringKeyType bool - keyDecoder Decoder - valueDecoder Decoder - structName string - fieldName string + mapType *runtime.Type + keyType *runtime.Type + valueType *runtime.Type + canUseAssignFaststrType bool + keyDecoder Decoder + valueDecoder Decoder + structName string + fieldName string } func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder { + isStructIndirect := valueType.Kind() == reflect.Struct && runtime.IfaceIndir(valueType) + stringKeyType := keyType.Kind() == reflect.String + canUseAssignFaststrType := stringKeyType && !isStructIndirect return &mapDecoder{ - mapType: mapType, - keyDecoder: keyDec, - keyType: keyType, - stringKeyType: keyType.Kind() == reflect.String, - valueType: valueType, - valueDecoder: valueDec, - structName: structName, - fieldName: fieldName, + mapType: mapType, + keyDecoder: keyDec, + keyType: keyType, + canUseAssignFaststrType: canUseAssignFaststrType, + valueType: valueType, + valueDecoder: valueDec, + structName: structName, + fieldName: fieldName, } } @@ -45,8 +48,8 @@ func mapassign_faststr(t *runtime.Type, m unsafe.Pointer, s string) unsafe.Point func mapassign(t *runtime.Type, m unsafe.Pointer, k, v unsafe.Pointer) func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) { - if d.stringKeyType { - mapV := mapassign_faststr(d.mapType, m, *(*string)(k)) + if d.canUseAssignFaststrType { + mapV := mapassign_faststr(t, m, *(*string)(k)) typedmemmove(d.valueType, mapV, v) } else { mapassign(t, m, k, v) From d494b03b744a054c45937e8090696704a95d0481 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Tue, 31 Aug 2021 12:21:08 +0900 Subject: [PATCH 3/3] Fix decoding of map type that contains indirect element type --- decode_test.go | 1 + internal/decoder/map.go | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/decode_test.go b/decode_test.go index 4b6dcc0..ea88d76 100644 --- a/decode_test.go +++ b/decode_test.go @@ -3767,6 +3767,7 @@ func TestIssue282(t *testing.T) { } type T0 map[string]T2 + // T2 size is 136 bytes. This is indirect type. var v T0 if err := json.Unmarshal(J, &v); err != nil { t.Fatal(err) diff --git a/internal/decoder/map.go b/internal/decoder/map.go index d26a352..bb18ef9 100644 --- a/internal/decoder/map.go +++ b/internal/decoder/map.go @@ -20,14 +20,11 @@ type mapDecoder struct { } func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder { - isStructIndirect := valueType.Kind() == reflect.Struct && runtime.IfaceIndir(valueType) - stringKeyType := keyType.Kind() == reflect.String - canUseAssignFaststrType := stringKeyType && !isStructIndirect return &mapDecoder{ mapType: mapType, keyDecoder: keyDec, keyType: keyType, - canUseAssignFaststrType: canUseAssignFaststrType, + canUseAssignFaststrType: canUseAssignFaststrType(keyType, valueType), valueType: valueType, valueDecoder: valueDec, structName: structName, @@ -35,6 +32,19 @@ func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, } } +const ( + mapMaxElemSize = 128 +) + +// See detail: https://github.com/goccy/go-json/pull/283 +func canUseAssignFaststrType(key *runtime.Type, value *runtime.Type) bool { + indirectElem := value.Size() > mapMaxElemSize + if indirectElem { + return false + } + return key.Kind() == reflect.String +} + //go:linkname makemap reflect.makemap func makemap(*runtime.Type, int) unsafe.Pointer