Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 53 additions & 7 deletions decode_map_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,72 @@ func findStructFieldByKey(
return fldIdx, true
}
if caseInsensitive {
return findFieldCaseInsensitive(structType.fields, string(keyBytes))
return findFieldCaseInsensitiveBytes(structType.fields, keyBytes)
}
return -1, false
}

// findFieldCaseInsensitive returns the index of the first field whose name
// case-insensitively matches key, or -1 and false if no field matches.
func findFieldCaseInsensitive(flds decodingFields, key string) (int, bool) {
keyLen := len(key)
// findFieldCaseInsensitiveBytes returns the index of the first field whose name
// case-insensitively matches keyBytes, or -1 and false if no field matches.
//
// It keeps the common ASCII path allocation-free and avoids strings.EqualFold's
// Unicode machinery unless either side contains non-ASCII bytes.
func findFieldCaseInsensitiveBytes(flds decodingFields, keyBytes []byte) (int, bool) {
keyLen := len(keyBytes)
var key string
var keySet bool

for i, f := range flds {
if f.keyAsInt {
if f.keyAsInt || len(f.name) != keyLen {
continue
}

if match, ascii := equalFoldASCIIStringBytes(f.name, keyBytes); ascii {
if match {
return i, true
}
continue
}
if len(f.name) == keyLen && strings.EqualFold(f.name, key) {

if !keySet {
key = string(keyBytes)
keySet = true
}
if strings.EqualFold(f.name, key) {
return i, true
}
}
return -1, false
}

// equalFoldASCIIStringBytes compares s and b using ASCII case folding.
// ascii is false if either input contains non-ASCII bytes, in which case the
// caller must use strings.EqualFold to preserve Unicode case-folding semantics.
func equalFoldASCIIStringBytes(s string, b []byte) (match bool, ascii bool) {
if len(s) != len(b) {
return false, true
}

for i := range b {
c := s[i]
d := b[i]
if c|d >= 0x80 {
return false, false
}

if 'A' <= c && c <= 'Z' {
c += 'a' - 'A'
}
if 'A' <= d && d <= 'Z' {
d += 'a' - 'A'
}
if c != d {
return false, true
}
}
return true, true
}

// handleUnmatchedMapKey handles a map entry whose key does not match any struct
// field. It can return UnknownFieldError or DupMapKeyError.
// handleUnmatchedMapKey consumes the CBOR value, so the caller doesn't need to skip any values.
Expand Down
Loading