From 1a79efb9468227fa0d3ef67ca0b98a9139b3d038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Sat, 28 Aug 2021 02:50:19 +0300 Subject: [PATCH 1/3] Implement duplicate support --- config.go | 3 ++ example_test.go | 100 ++++++++++++++++++++++++++++++++++++++ reflect_struct_decoder.go | 68 ++++++++++++++++++-------- 3 files changed, 152 insertions(+), 19 deletions(-) diff --git a/config.go b/config.go index 2adcdc3b..8ec637d5 100644 --- a/config.go +++ b/config.go @@ -20,6 +20,7 @@ type Config struct { SortMapKeys bool UseNumber bool DisallowUnknownFields bool + DisallowDuplicateFields bool TagKey string OnlyTaggedField bool ValidateJsonRawMessage bool @@ -72,6 +73,7 @@ type frozenConfig struct { objectFieldMustBeSimpleString bool onlyTaggedField bool disallowUnknownFields bool + disallowDuplicateFields bool decoderCache *concurrent.Map encoderCache *concurrent.Map encoderExtension Extension @@ -133,6 +135,7 @@ func (cfg Config) Froze() API { objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString, onlyTaggedField: cfg.OnlyTaggedField, disallowUnknownFields: cfg.DisallowUnknownFields, + disallowDuplicateFields: cfg.DisallowDuplicateFields, caseSensitive: cfg.CaseSensitive, } api.streamPool = &sync.Pool{ diff --git a/example_test.go b/example_test.go index 1516b77b..77415186 100644 --- a/example_test.go +++ b/example_test.go @@ -119,3 +119,103 @@ func (m *MyKey) UnmarshalText(text []byte) error { *m = MyKey(text[:3]) return nil } + +type Target struct { + FieldA string `json:"fieldA"` +} + +func Example_duplicateFieldsCaseSensitive() { + api := Config{ + CaseSensitive: true, + DisallowDuplicateFields: true, + }.Froze() + + t := &Target{} + err := api.Unmarshal([]byte(`{"fieldA": "value", "fielda": "val2"}`), t) + fmt.Printf("Case-sensitiveness means no duplicates: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + t = &Target{} + err = api.Unmarshal([]byte(`{"fieldA": "value", "fieldA": "val2"}`), t) + fmt.Printf("Got duplicates in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + t = &Target{} + err = api.Unmarshal([]byte(`{"fielda": "value", "fielda": "val2"}`), t) + fmt.Printf("Got duplicates not in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + // Output: + // Case-sensitiveness means no duplicates: 'fieldA' = "value", err = + // Got duplicates in struct field: 'fieldA' = "value", err = jsoniter.Target.ReadObject: found duplicate field: fieldA, error found in #10 byte of ...|, "fieldA": "val2"}|..., bigger context ...|{"fieldA": "value", "fieldA": "val2"}|... + // Got duplicates not in struct field: 'fieldA' = "", err = jsoniter.Target.ReadObject: found duplicate field: fielda, error found in #10 byte of ...|, "fielda": "val2"}|..., bigger context ...|{"fielda": "value", "fielda": "val2"}|... +} + +func Example_noDuplicateFieldsCaseSensitive() { + api := Config{ + CaseSensitive: true, + DisallowDuplicateFields: false, + }.Froze() + + t := &Target{} + err := api.Unmarshal([]byte(`{"fieldA": "value", "fielda": "val2"}`), t) + fmt.Printf("Case-sensitiveness means no duplicates: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + t = &Target{} + err = api.Unmarshal([]byte(`{"fieldA": "value", "fieldA": "val2"}`), t) + fmt.Printf("Got duplicates in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + t = &Target{} + err = api.Unmarshal([]byte(`{"fielda": "value", "fielda": "val2"}`), t) + fmt.Printf("Got duplicates not in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + // Output: + // Case-sensitiveness means no duplicates: 'fieldA' = "value", err = + // Got duplicates in struct field: 'fieldA' = "val2", err = + // Got duplicates not in struct field: 'fieldA' = "", err = +} + +func Example_duplicateFieldsInCaseSensitive() { + api := Config{ + CaseSensitive: false, + DisallowDuplicateFields: true, + }.Froze() + + t := &Target{} + err := api.Unmarshal([]byte(`{"fieldA": "value", "fielda": "val2"}`), t) + fmt.Printf("In-case-sensitive duplicates: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + t = &Target{} + err = api.Unmarshal([]byte(`{"fieldA": "value", "fieldA": "val2"}`), t) + fmt.Printf("Got duplicates in exact struct field match: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + t = &Target{} + err = api.Unmarshal([]byte(`{"fielda": "value", "fielda": "val2"}`), t) + fmt.Printf("Got duplicates not in notexact struct field match: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + // Output: + // In-case-sensitive duplicates: 'fieldA' = "value", err = jsoniter.Target.ReadObject: found duplicate field: fielda, error found in #10 byte of ...|, "fielda": "val2"}|..., bigger context ...|{"fieldA": "value", "fielda": "val2"}|... + // Got duplicates in exact struct field match: 'fieldA' = "value", err = jsoniter.Target.ReadObject: found duplicate field: fieldA, error found in #10 byte of ...|, "fieldA": "val2"}|..., bigger context ...|{"fieldA": "value", "fieldA": "val2"}|... + // Got duplicates not in notexact struct field match: 'fieldA' = "value", err = jsoniter.Target.ReadObject: found duplicate field: fielda, error found in #10 byte of ...|, "fielda": "val2"}|..., bigger context ...|{"fielda": "value", "fielda": "val2"}|... +} + +func Example_noDuplicateFieldsInCaseSensitive() { + api := Config{ + CaseSensitive: false, + DisallowDuplicateFields: false, + }.Froze() + + t := &Target{} + err := api.Unmarshal([]byte(`{"fieldA": "value", "fielda": "val2"}`), t) + fmt.Printf("Case-sensitiveness means no duplicates: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + t = &Target{} + err = api.Unmarshal([]byte(`{"fieldA": "value", "fieldA": "val2"}`), t) + fmt.Printf("Got duplicates in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + t = &Target{} + err = api.Unmarshal([]byte(`{"fielda": "value", "fielda": "val2"}`), t) + fmt.Printf("Got duplicates not in struct field: 'fieldA' = %q, err = %v\n", t.FieldA, err) + + // Output: + // Case-sensitiveness means no duplicates: 'fieldA' = "val2", err = + // Got duplicates in struct field: 'fieldA' = "val2", err = + // Got duplicates not in struct field: 'fieldA' = "val2", err = +} diff --git a/reflect_struct_decoder.go b/reflect_struct_decoder.go index 92ae912d..3fde5952 100644 --- a/reflect_struct_decoder.go +++ b/reflect_struct_decoder.go @@ -45,9 +45,17 @@ func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder { } func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structFieldDecoder) ValDecoder { - if ctx.disallowUnknownFields { - return &generalStructDecoder{typ: typ, fields: fields, disallowUnknownFields: true} + if ctx.disallowUnknownFields || ctx.disallowDuplicateFields { + return &generalStructDecoder{ + typ: typ, + fields: fields, + disallowUnknownFields: ctx.disallowUnknownFields, + disallowDuplicateFields: ctx.disallowDuplicateFields, + } } + // TODO: Possible optimization could be to directly use the general + // struct decoder in case of in-case-sensitiveness; because of the + // lowercasing in calcHash, known will always be true. knownHash := map[int64]struct{}{ 0: {}, } @@ -60,7 +68,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} return &oneFieldStructDecoder{typ, fieldHash, fieldDecoder} @@ -74,7 +82,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldHash1 == 0 { @@ -97,7 +105,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -128,7 +136,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -165,7 +173,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -208,7 +216,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -257,7 +265,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -312,7 +320,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -373,7 +381,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -440,7 +448,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName, ctx.caseSensitive()) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -487,13 +495,14 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldName9, fieldDecoder9, fieldName10, fieldDecoder10} } - return &generalStructDecoder{typ, fields, false} + return &generalStructDecoder{typ, fields, false, false} } type generalStructDecoder struct { - typ reflect2.Type - fields map[string]*structFieldDecoder - disallowUnknownFields bool + typ reflect2.Type + fields map[string]*structFieldDecoder + disallowUnknownFields bool + disallowDuplicateFields bool } func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { @@ -503,9 +512,13 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) if !iter.incrementDepth() { return } + var seenFields map[string]struct{} + if decoder.disallowDuplicateFields { + seenFields = make(map[string]struct{}, len(decoder.fields)) + } var c byte for c = ','; c == ','; c = iter.nextToken() { - decoder.decodeOneField(ptr, iter) + decoder.decodeOneField(ptr, iter, seenFields) } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) @@ -516,7 +529,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) iter.decrementDepth() } -func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) { +func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator, seenFields map[string]struct{}) { var field string var fieldDecoder *structFieldDecoder if iter.cfg.objectFieldMustBeSimpleString { @@ -533,11 +546,28 @@ func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *It fieldDecoder = decoder.fields[strings.ToLower(field)] } } - if fieldDecoder == nil { + isDuplicate := false + if decoder.disallowDuplicateFields { + seenField := field + if !iter.cfg.caseSensitive { + seenField = strings.ToLower(seenField) + } + _, isDuplicate = seenFields[seenField] + // Always register here so that also unknown fields' duplicates are + // registered. + seenFields[seenField] = struct{}{} + } + + if fieldDecoder == nil || isDuplicate { if decoder.disallowUnknownFields { msg := "found unknown field: " + field iter.ReportError("ReadObject", msg) } + if isDuplicate { + msg := "found duplicate field: " + field + iter.ReportError("ReadObject", msg) + } + // Skip field c := iter.nextToken() if c != ':' { iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) From 2005d889b97e855e946a92e81cabff003af70c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Sat, 28 Aug 2021 02:56:09 +0300 Subject: [PATCH 2/3] Small optimization, always use the geneal struct decoder in in-case-sensitive mode --- iter_object.go | 6 +----- reflect_struct_decoder.go | 25 +++++++++++-------------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/iter_object.go b/iter_object.go index 58ee89c8..80b460f7 100644 --- a/iter_object.go +++ b/iter_object.go @@ -2,7 +2,6 @@ package jsoniter import ( "fmt" - "strings" ) // ReadObject read one field from object. @@ -95,10 +94,7 @@ func (iter *Iterator) readFieldHash() int64 { } } -func calcHash(str string, caseSensitive bool) int64 { - if !caseSensitive { - str = strings.ToLower(str) - } +func calcHash(str string) int64 { hash := int64(0x811c9dc5) for _, b := range []byte(str) { hash ^= int64(b) diff --git a/reflect_struct_decoder.go b/reflect_struct_decoder.go index 3fde5952..6c4570a4 100644 --- a/reflect_struct_decoder.go +++ b/reflect_struct_decoder.go @@ -45,7 +45,7 @@ func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder { } func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structFieldDecoder) ValDecoder { - if ctx.disallowUnknownFields || ctx.disallowDuplicateFields { + if ctx.disallowUnknownFields || ctx.disallowDuplicateFields || !ctx.caseSensitive() { return &generalStructDecoder{ typ: typ, fields: fields, @@ -53,9 +53,6 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF disallowDuplicateFields: ctx.disallowDuplicateFields, } } - // TODO: Possible optimization could be to directly use the general - // struct decoder in case of in-case-sensitiveness; because of the - // lowercasing in calcHash, known will always be true. knownHash := map[int64]struct{}{ 0: {}, } @@ -65,7 +62,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF return &skipObjectDecoder{typ} case 1: for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -79,7 +76,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder1 *structFieldDecoder var fieldDecoder2 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -102,7 +99,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder2 *structFieldDecoder var fieldDecoder3 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -133,7 +130,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder3 *structFieldDecoder var fieldDecoder4 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -170,7 +167,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder4 *structFieldDecoder var fieldDecoder5 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -213,7 +210,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder5 *structFieldDecoder var fieldDecoder6 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -262,7 +259,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder6 *structFieldDecoder var fieldDecoder7 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -317,7 +314,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder7 *structFieldDecoder var fieldDecoder8 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -378,7 +375,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder8 *structFieldDecoder var fieldDecoder9 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} @@ -445,7 +442,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF var fieldDecoder9 *structFieldDecoder var fieldDecoder10 *structFieldDecoder for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) + fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { return &generalStructDecoder{typ, fields, false, false} From ca0e43c61be99d5dfe42df4bb225b167e2bfb89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Sat, 28 Aug 2021 03:11:36 +0300 Subject: [PATCH 3/3] Slight more improvements --- example_test.go | 29 ++++++++++++++++++++ reflect_struct_decoder.go | 56 ++++++++++++++++----------------------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/example_test.go b/example_test.go index 77415186..7abdbcf4 100644 --- a/example_test.go +++ b/example_test.go @@ -4,6 +4,9 @@ import ( "fmt" "os" "strings" + "testing" + + "github.com/stretchr/testify/assert" ) func ExampleMarshal() { @@ -219,3 +222,29 @@ func Example_noDuplicateFieldsInCaseSensitive() { // Got duplicates in struct field: 'fieldA' = "val2", err = // Got duplicates not in struct field: 'fieldA' = "val2", err = } + +func TestEncoder(t *testing.T) { + api := Config{ + CaseSensitive: true, + DisallowDuplicateFields: true, + }.Froze() + + type target2 struct { + B Target `json:"b"` + A Target `json:"a"` + } + + data := `{"a": {"fieldA": "bla"}, "b": {"fieldA": "bar"}}` + data += data + + d := api.NewDecoder(strings.NewReader(data)) + obj := &target2{} + assert.Nil(t, d.Decode(obj)) + assert.Equal(t, "bla", obj.A.FieldA) + assert.Equal(t, "bar", obj.B.FieldA) + + obj = &target2{} + assert.Nil(t, d.Decode(obj)) + assert.Equal(t, "bla", obj.A.FieldA) + assert.Equal(t, "bar", obj.B.FieldA) +} diff --git a/reflect_struct_decoder.go b/reflect_struct_decoder.go index 6c4570a4..e8f7b7f5 100644 --- a/reflect_struct_decoder.go +++ b/reflect_struct_decoder.go @@ -46,12 +46,7 @@ func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder { func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structFieldDecoder) ValDecoder { if ctx.disallowUnknownFields || ctx.disallowDuplicateFields || !ctx.caseSensitive() { - return &generalStructDecoder{ - typ: typ, - fields: fields, - disallowUnknownFields: ctx.disallowUnknownFields, - disallowDuplicateFields: ctx.disallowDuplicateFields, - } + return &generalStructDecoder{typ, fields} } knownHash := map[int64]struct{}{ 0: {}, @@ -65,7 +60,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} return &oneFieldStructDecoder{typ, fieldHash, fieldDecoder} @@ -79,7 +74,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldHash1 == 0 { @@ -102,7 +97,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -133,7 +128,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -170,7 +165,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -213,7 +208,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -262,7 +257,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -317,7 +312,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -378,7 +373,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -445,7 +440,7 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldHash := calcHash(fieldName) _, known := knownHash[fieldHash] if known { - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } knownHash[fieldHash] = struct{}{} if fieldName1 == 0 { @@ -492,14 +487,12 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF fieldName9, fieldDecoder9, fieldName10, fieldDecoder10} } - return &generalStructDecoder{typ, fields, false, false} + return &generalStructDecoder{typ, fields} } type generalStructDecoder struct { - typ reflect2.Type - fields map[string]*structFieldDecoder - disallowUnknownFields bool - disallowDuplicateFields bool + typ reflect2.Type + fields map[string]*structFieldDecoder } func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { @@ -510,7 +503,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) return } var seenFields map[string]struct{} - if decoder.disallowDuplicateFields { + if iter.cfg.disallowDuplicateFields { seenFields = make(map[string]struct{}, len(decoder.fields)) } var c byte @@ -528,23 +521,20 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator, seenFields map[string]struct{}) { var field string - var fieldDecoder *structFieldDecoder if iter.cfg.objectFieldMustBeSimpleString { fieldBytes := iter.ReadStringAsSlice() field = *(*string)(unsafe.Pointer(&fieldBytes)) - fieldDecoder = decoder.fields[field] - if fieldDecoder == nil && !iter.cfg.caseSensitive { - fieldDecoder = decoder.fields[strings.ToLower(field)] - } } else { field = iter.ReadString() - fieldDecoder = decoder.fields[field] - if fieldDecoder == nil && !iter.cfg.caseSensitive { - fieldDecoder = decoder.fields[strings.ToLower(field)] - } } + + fieldDecoder := decoder.fields[field] + if fieldDecoder == nil && !iter.cfg.caseSensitive { + fieldDecoder = decoder.fields[strings.ToLower(field)] + } + isDuplicate := false - if decoder.disallowDuplicateFields { + if iter.cfg.disallowDuplicateFields { seenField := field if !iter.cfg.caseSensitive { seenField = strings.ToLower(seenField) @@ -556,7 +546,7 @@ func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *It } if fieldDecoder == nil || isDuplicate { - if decoder.disallowUnknownFields { + if iter.cfg.disallowUnknownFields { msg := "found unknown field: " + field iter.ReportError("ReadObject", msg) }