Merge pull request #65034 from caesarxuchao/json-case-sensitive

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Make kubernetes json serializer case sensitive

This PR imported the latest jsoniterator library so that case sensitivity during unmarhsaling is optional. The PR also set Kubernetes json serializer to be case sensitive.

Kubernetes json serializer had been case sensitive for 1.1-1.7 as we were using ugorji. This PR restores the behavior.

Fix #64612.

```release-notes
Kubernetes json deserializer is now case-sensitive as it was before 1.8.
If your config files contains fields with wrong case, the config files will be now invalid.
```
pull/8/head
Kubernetes Submit Queue 2018-06-14 15:41:26 -07:00 committed by GitHub
commit a2de1398f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 151 additions and 73 deletions

4
Godeps/Godeps.json generated
View File

@ -2029,8 +2029,8 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Comment": "1.1.3-16-g2ddf6d7",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Comment": "1.1.3-22-gf2b4162",
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/jteeuwen/go-bindata",

View File

@ -58,7 +58,6 @@ go_library(
"//pkg/proxy/apis/kubeproxyconfig/scheme:go_default_library",
"//pkg/proxy/apis/kubeproxyconfig/v1alpha1:go_default_library",
"//pkg/util/pointer:go_default_library",
"//vendor/github.com/json-iterator/go:go_default_library",
"//vendor/github.com/ugorji/go/codec:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -23,7 +23,6 @@ import (
"strconv"
"strings"
jsoniter "github.com/json-iterator/go"
"github.com/ugorji/go/codec"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -31,8 +30,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
type configMutationFunc func(map[string]interface{}) error
// These migrations are a stop-gap until we get a properly-versioned configuration file for MasterConfiguration.

View File

@ -64,7 +64,7 @@ KubeProxy:
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
ExcludeCIDRs: null
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s

View File

@ -53,7 +53,7 @@ kubeProxy:
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
ExcludeCIDRs: null
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s

View File

@ -50,7 +50,7 @@ kubeProxy:
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
ExcludeCIDRs: null
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s

View File

@ -54,7 +54,7 @@ kubeProxy:
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
ExcludeCIDRs: null
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s

View File

@ -49,7 +49,7 @@ kubeProxy:
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
ExcludeCIDRs: null
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s

View File

@ -49,7 +49,7 @@ kubeProxy:
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
ExcludeCIDRs: null
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s

View File

@ -96,6 +96,7 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/protobuf:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",

View File

@ -37,6 +37,7 @@ import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
k8s_json "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/sets"
@ -560,9 +561,10 @@ func BenchmarkDecodeIntoJSONCodecGenConfigFast(b *testing.B) {
b.StopTimer()
}
// BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary
// provides a baseline for JSON decode performance
// with jsoniter.ConfigCompatibleWithStandardLibrary
// BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary provides a
// baseline for JSON decode performance with
// jsoniter.ConfigCompatibleWithStandardLibrary, but with case sensitivity set
// to true
func BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary(b *testing.B) {
kcodec := testapi.Default.Codec()
items := benchmarkItems(b)
@ -577,9 +579,10 @@ func BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary(b *testi
}
b.ResetTimer()
iter := k8s_json.CaseSensitiveJsonIterator()
for i := 0; i < b.N; i++ {
obj := v1.Pod{}
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(encoded[i%width], &obj); err != nil {
if err := iter.Unmarshal(encoded[i%width], &obj); err != nil {
b.Fatal(err)
}
}

View File

@ -65,7 +65,7 @@ type KubeProxyIPVSConfiguration struct {
Scheduler string `json:"scheduler"`
// excludeCIDRs is a list of CIDR's which the ipvs proxier should not touch
// when cleaning up ipvs services.
ExcludeCIDRs []string
ExcludeCIDRs []string `json:"excludeCIDRs"`
}
// KubeProxyConntrackConfiguration contains conntrack settings for

View File

@ -36,7 +36,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/modern-go/concurrent",

View File

@ -532,7 +532,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/mailru/easyjson/buffer",

View File

@ -92,7 +92,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/modern-go/concurrent",

View File

@ -21,9 +21,9 @@ go_test(
embed = [":go_default_library"],
deps = [
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/json-iterator/go:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
],
)

View File

@ -21,7 +21,7 @@ import (
"reflect"
"testing"
jsoniter "github.com/json-iterator/go"
k8s_json "k8s.io/apimachinery/pkg/runtime/serializer/json"
)
type GroupVersionHolder struct {
@ -47,7 +47,8 @@ func TestGroupVersionUnmarshalJSON(t *testing.T) {
t.Errorf("JSON codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV)
}
// test the json-iterator codec
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(c.input, &result); err != nil {
iter := k8s_json.CaseSensitiveJsonIterator()
if err := iter.Unmarshal(c.input, &result); err != nil {
t.Errorf("json-iterator codec failed to unmarshal input '%v': %v", c.input, err)
}
if !reflect.DeepEqual(result.GV, c.expect) {

View File

@ -21,7 +21,7 @@ import (
"reflect"
"testing"
jsoniter "github.com/json-iterator/go"
k8s_json "k8s.io/apimachinery/pkg/runtime/serializer/json"
)
func TestVerbsUgorjiMarshalJSON(t *testing.T) {
@ -45,7 +45,7 @@ func TestVerbsUgorjiMarshalJSON(t *testing.T) {
}
}
func TestVerbsUgorjiUnmarshalJSON(t *testing.T) {
func TestVerbsUJsonIterUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result APIResource
@ -56,9 +56,10 @@ func TestVerbsUgorjiUnmarshalJSON(t *testing.T) {
{`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}},
}
iter := k8s_json.CaseSensitiveJsonIterator()
for i, c := range cases {
var result APIResource
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(c.input), &result); err != nil {
if err := iter.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err)
}
if !reflect.DeepEqual(result, c.result) {

View File

@ -93,6 +93,20 @@ func init() {
jsoniter.RegisterTypeDecoderFunc("interface {}", decodeNumberAsInt64IfPossible)
}
// CaseSensitiveJsonIterator returns a jsoniterator API that's configured to be
// case-sensitive when unmarshalling, and otherwise compatible with
// the encoding/json standard library.
func CaseSensitiveJsonIterator() jsoniter.API {
return jsoniter.Config{
EscapeHTML: true,
SortMapKeys: true,
ValidateJsonRawMessage: true,
CaseSensitive: true,
}.Froze()
}
var caseSensitiveJsonIterator = CaseSensitiveJsonIterator()
// gvkWithDefaults returns group kind and version defaulting from provided default
func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind {
if len(actual.Kind) == 0 {
@ -157,7 +171,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
types, _, err := s.typer.ObjectKinds(into)
switch {
case runtime.IsNotRegisteredError(err), isUnstructured:
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(data, into); err != nil {
if err := caseSensitiveJsonIterator.Unmarshal(data, into); err != nil {
return nil, actual, err
}
return into, actual, nil
@ -181,7 +195,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
return nil, actual, err
}
if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(data, obj); err != nil {
if err := caseSensitiveJsonIterator.Unmarshal(data, obj); err != nil {
return nil, actual, err
}
return obj, actual, nil
@ -190,7 +204,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, i
// Encode serializes the provided object to the given writer.
func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
if s.yaml {
json, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(obj)
json, err := caseSensitiveJsonIterator.Marshal(obj)
if err != nil {
return err
}
@ -203,7 +217,7 @@ func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
}
if s.pretty {
data, err := jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ")
data, err := caseSensitiveJsonIterator.MarshalIndent(obj, "", " ")
if err != nil {
return err
}

View File

@ -31,9 +31,30 @@ import (
type testDecodable struct {
Other string
Value int `json:"value"`
Spec DecodableSpec `json:"spec"`
gvk schema.GroupVersionKind
}
// DecodableSpec has 15 fields. json-iterator treats struct with more than 10
// fields differently from struct that has less than 10 fields.
type DecodableSpec struct {
A int `json:"A"`
B int `json:"B"`
C int `json:"C"`
D int `json:"D"`
E int `json:"E"`
F int `json:"F"`
G int `json:"G"`
H int `json:"h"`
I int `json:"i"`
J int `json:"j"`
K int `json:"k"`
L int `json:"l"`
M int `json:"m"`
N int `json:"n"`
O int `json:"o"`
}
func (d *testDecodable) GetObjectKind() schema.ObjectKind { return d }
func (d *testDecodable) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk }
func (d *testDecodable) GroupVersionKind() schema.GroupVersionKind { return d.gvk }
@ -221,6 +242,28 @@ func TestDecode(t *testing.T) {
},
},
},
// Unmarshalling is case-sensitive
{
// "VaLue" should have been "value"
data: []byte(`{"kind":"Test","apiVersion":"other/blah","VaLue":1,"Other":"test"}`),
into: &testDecodable{},
typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind(schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &testDecodable{
Other: "test",
},
},
// Unmarshalling is case-sensitive for big struct.
{
// "b" should have been "B", "I" should have been "i"
data: []byte(`{"kind":"Test","apiVersion":"other/blah","spec": {"A": 1, "b": 2, "h": 3, "I": 4}}`),
into: &testDecodable{},
typer: &mockTyper{err: runtime.NewNotRegisteredErrForKind(schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"})},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &testDecodable{
Spec: DecodableSpec{A: 1, H: 3},
},
},
}
for i, test := range testCases {

View File

@ -504,7 +504,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/mailru/easyjson/buffer",

View File

@ -156,7 +156,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/modern-go/concurrent",

View File

@ -216,7 +216,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/mailru/easyjson/buffer",

View File

@ -76,7 +76,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/modern-go/concurrent",

View File

@ -208,7 +208,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/mailru/easyjson/buffer",

View File

@ -92,7 +92,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/modern-go/concurrent",

View File

@ -1,6 +1,12 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/json-iterator/go"
packages = ["."]
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
version = "1.1.3"
[[projects]]
name = "github.com/modern-go/concurrent"
packages = ["."]
@ -16,6 +22,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "ac7003b5a981716353a43055ab7d4c5357403cb30a60de2dbdeb446c1544beaa"
inputs-digest = "56a0b9e9e61d2bc8af5e1b68537401b7f4d60805eda3d107058f3171aa5cf793"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -2,12 +2,13 @@ package jsoniter
import (
"encoding/json"
"github.com/modern-go/concurrent"
"github.com/modern-go/reflect2"
"io"
"reflect"
"sync"
"unsafe"
"github.com/modern-go/concurrent"
"github.com/modern-go/reflect2"
)
// Config customize how the API should behave.
@ -23,6 +24,7 @@ type Config struct {
OnlyTaggedField bool
ValidateJsonRawMessage bool
ObjectFieldMustBeSimpleString bool
CaseSensitive bool
}
// API the public interface of this package.
@ -75,6 +77,7 @@ type frozenConfig struct {
extensions []Extension
streamPool *sync.Pool
iteratorPool *sync.Pool
caseSensitive bool
}
func (cfg *frozenConfig) initCache() {
@ -128,6 +131,7 @@ func (cfg Config) Froze() API {
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
onlyTaggedField: cfg.OnlyTaggedField,
disallowUnknownFields: cfg.DisallowUnknownFields,
caseSensitive: cfg.CaseSensitive,
}
api.streamPool = &sync.Pool{
New: func() interface{} {

View File

@ -60,7 +60,7 @@ func (iter *Iterator) readFieldHash() int64 {
if b == '\\' {
iter.head = i
for _, b := range iter.readStringSlowPath() {
if 'A' <= b && b <= 'Z' {
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
b += 'a' - 'A'
}
hash ^= int64(b)
@ -82,7 +82,7 @@ func (iter *Iterator) readFieldHash() int64 {
}
return hash
}
if 'A' <= b && b <= 'Z' {
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
b += 'a' - 'A'
}
hash ^= int64(b)
@ -95,10 +95,14 @@ func (iter *Iterator) readFieldHash() int64 {
}
}
func calcHash(str string) int64 {
func calcHash(str string, caseSensitive bool) int64 {
hash := int64(0x811c9dc5)
for _, b := range str {
if caseSensitive {
hash ^= int64(b)
} else {
hash ^= int64(unicode.ToLower(b))
}
hash *= 0x1000193
}
return int64(hash)

View File

@ -2,9 +2,10 @@ package jsoniter
import (
"fmt"
"github.com/modern-go/reflect2"
"reflect"
"unsafe"
"github.com/modern-go/reflect2"
)
// ValDecoder is an internal type registered to cache as needed.
@ -40,6 +41,14 @@ type ctx struct {
decoders map[reflect2.Type]ValDecoder
}
func (b *ctx) caseSensitive() bool {
if b.frozenConfig == nil {
// default is case-insensitive
return false
}
return b.frozenConfig.caseSensitive
}
func (b *ctx) append(prefix string) *ctx {
return &ctx{
frozenConfig: b.frozenConfig,

View File

@ -1,7 +1,6 @@
package jsoniter
import (
"bytes"
"encoding/base64"
"reflect"
"strconv"
@ -418,20 +417,11 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
}
switch iter.WhatIsNext() {
case StringValue:
encoding := base64.StdEncoding
src := iter.SkipAndReturnBytes()
// New line characters (\r and \n) are ignored.
// Refer to https://golang.org/pkg/encoding/base64/#Encoding.Decode
src = bytes.Replace(src, []byte(`\r`), []byte{}, -1)
src = bytes.Replace(src, []byte(`\n`), []byte{}, -1)
src = src[1 : len(src)-1]
decodedLen := encoding.DecodedLen(len(src))
dst := make([]byte, decodedLen)
len, err := encoding.Decode(dst, src)
src := iter.ReadString()
dst, err := base64.StdEncoding.DecodeString(src)
if err != nil {
iter.ReportError("decode base64", err.Error())
} else {
dst = dst[:len]
codec.sliceType.UnsafeSet(ptr, unsafe.Pointer(&dst))
}
case ArrayValue:

View File

@ -2,10 +2,11 @@ package jsoniter
import (
"fmt"
"github.com/modern-go/reflect2"
"io"
"strings"
"unsafe"
"github.com/modern-go/reflect2"
)
func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder {
@ -31,11 +32,15 @@ func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder {
for k, binding := range bindings {
fields[k] = binding.Decoder.(*structFieldDecoder)
}
if !ctx.caseSensitive() {
for k, binding := range bindings {
if _, found := fields[strings.ToLower(k)]; !found {
fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder)
}
}
}
return createStructDecoder(ctx, typ, fields)
}
@ -46,12 +51,13 @@ func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structF
knownHash := map[int64]struct{}{
0: {},
}
switch len(fields) {
case 0:
return &skipObjectDecoder{typ}
case 1:
for fieldName, fieldDecoder := range fields {
fieldHash := calcHash(fieldName)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -65,7 +71,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -88,7 +94,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -119,7 +125,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -156,7 +162,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -199,7 +205,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -248,7 +254,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -303,7 +309,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -364,7 +370,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -431,7 +437,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)
fieldHash := calcHash(fieldName, ctx.caseSensitive())
_, known := knownHash[fieldHash]
if known {
return &generalStructDecoder{typ, fields, false}
@ -513,13 +519,13 @@ func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *It
fieldBytes := iter.ReadStringAsSlice()
field = *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder = decoder.fields[field]
if fieldDecoder == nil {
if fieldDecoder == nil && !iter.cfg.caseSensitive {
fieldDecoder = decoder.fields[strings.ToLower(field)]
}
} else {
field = iter.ReadString()
fieldDecoder = decoder.fields[field]
if fieldDecoder == nil {
if fieldDecoder == nil && !iter.cfg.caseSensitive {
fieldDecoder = decoder.fields[strings.ToLower(field)]
}
}