forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/ghodss/yaml/fields.go

897 lines
13 KiB
Go
Raw Normal View History

2024-05-14 13:07:09 +00:00
// Copyright 2013 The Go Authors. All rights reserved.
2024-05-14 13:07:09 +00:00
// Use of this source code is governed by a BSD-style
2024-05-14 13:07:09 +00:00
// license that can be found in the LICENSE file.
2024-05-14 13:07:09 +00:00
package yaml
import (
"bytes"
"encoding"
"encoding/json"
"reflect"
"sort"
"strings"
"sync"
"unicode"
"unicode/utf8"
)
// indirect walks down v allocating pointers as needed,
2024-05-14 13:07:09 +00:00
// until it gets to a non-pointer.
2024-05-14 13:07:09 +00:00
// if it encounters an Unmarshaler, indirect stops and returns that.
2024-05-14 13:07:09 +00:00
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
2024-05-14 13:07:09 +00:00
func indirect(v reflect.Value, decodingNull bool) (json.Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
2024-05-14 13:07:09 +00:00
// If v is a named type and is addressable,
2024-05-14 13:07:09 +00:00
// start with its address, so that if the type has pointer methods,
2024-05-14 13:07:09 +00:00
// we find them.
2024-05-14 13:07:09 +00:00
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
2024-05-14 13:07:09 +00:00
v = v.Addr()
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
for {
2024-05-14 13:07:09 +00:00
// Load value from interface, but only if the result will be
2024-05-14 13:07:09 +00:00
// usefully addressable.
2024-05-14 13:07:09 +00:00
if v.Kind() == reflect.Interface && !v.IsNil() {
2024-05-14 13:07:09 +00:00
e := v.Elem()
2024-05-14 13:07:09 +00:00
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
2024-05-14 13:07:09 +00:00
v = e
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
if v.Kind() != reflect.Ptr {
2024-05-14 13:07:09 +00:00
break
2024-05-14 13:07:09 +00:00
}
if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() {
2024-05-14 13:07:09 +00:00
break
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if v.IsNil() {
2024-05-14 13:07:09 +00:00
if v.CanSet() {
2024-05-14 13:07:09 +00:00
v.Set(reflect.New(v.Type().Elem()))
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
v = reflect.New(v.Type().Elem())
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if v.Type().NumMethod() > 0 {
2024-05-14 13:07:09 +00:00
if u, ok := v.Interface().(json.Unmarshaler); ok {
2024-05-14 13:07:09 +00:00
return u, nil, reflect.Value{}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
2024-05-14 13:07:09 +00:00
return nil, u, reflect.Value{}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
v = v.Elem()
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return nil, nil, v
2024-05-14 13:07:09 +00:00
}
// A field represents a single field found in a struct.
2024-05-14 13:07:09 +00:00
type field struct {
name string
nameBytes []byte // []byte(name)
2024-05-14 13:07:09 +00:00
equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent
tag bool
index []int
typ reflect.Type
2024-05-14 13:07:09 +00:00
omitEmpty bool
quoted bool
2024-05-14 13:07:09 +00:00
}
func fillField(f field) field {
2024-05-14 13:07:09 +00:00
f.nameBytes = []byte(f.name)
2024-05-14 13:07:09 +00:00
f.equalFold = foldFunc(f.nameBytes)
2024-05-14 13:07:09 +00:00
return f
2024-05-14 13:07:09 +00:00
}
// byName sorts field by name, breaking ties with depth,
2024-05-14 13:07:09 +00:00
// then breaking ties with "name came from json tag", then
2024-05-14 13:07:09 +00:00
// breaking ties with index sequence.
2024-05-14 13:07:09 +00:00
type byName []field
func (x byName) Len() int { return len(x) }
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byName) Less(i, j int) bool {
2024-05-14 13:07:09 +00:00
if x[i].name != x[j].name {
2024-05-14 13:07:09 +00:00
return x[i].name < x[j].name
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if len(x[i].index) != len(x[j].index) {
2024-05-14 13:07:09 +00:00
return len(x[i].index) < len(x[j].index)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if x[i].tag != x[j].tag {
2024-05-14 13:07:09 +00:00
return x[i].tag
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return byIndex(x).Less(i, j)
2024-05-14 13:07:09 +00:00
}
// byIndex sorts field by index sequence.
2024-05-14 13:07:09 +00:00
type byIndex []field
func (x byIndex) Len() int { return len(x) }
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byIndex) Less(i, j int) bool {
2024-05-14 13:07:09 +00:00
for k, xik := range x[i].index {
2024-05-14 13:07:09 +00:00
if k >= len(x[j].index) {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if xik != x[j].index[k] {
2024-05-14 13:07:09 +00:00
return xik < x[j].index[k]
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return len(x[i].index) < len(x[j].index)
2024-05-14 13:07:09 +00:00
}
// typeFields returns a list of fields that JSON should recognize for the given type.
2024-05-14 13:07:09 +00:00
// The algorithm is breadth-first search over the set of structs to include - the top struct
2024-05-14 13:07:09 +00:00
// and then any reachable anonymous structs.
2024-05-14 13:07:09 +00:00
func typeFields(t reflect.Type) []field {
2024-05-14 13:07:09 +00:00
// Anonymous fields to explore at the current level and the next.
2024-05-14 13:07:09 +00:00
current := []field{}
2024-05-14 13:07:09 +00:00
next := []field{{typ: t}}
// Count of queued names for current level and the next.
2024-05-14 13:07:09 +00:00
count := map[reflect.Type]int{}
2024-05-14 13:07:09 +00:00
nextCount := map[reflect.Type]int{}
// Types already visited at an earlier level.
2024-05-14 13:07:09 +00:00
visited := map[reflect.Type]bool{}
// Fields found.
2024-05-14 13:07:09 +00:00
var fields []field
for len(next) > 0 {
2024-05-14 13:07:09 +00:00
current, next = next, current[:0]
2024-05-14 13:07:09 +00:00
count, nextCount = nextCount, map[reflect.Type]int{}
for _, f := range current {
2024-05-14 13:07:09 +00:00
if visited[f.typ] {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
visited[f.typ] = true
// Scan f.typ for fields to include.
2024-05-14 13:07:09 +00:00
for i := 0; i < f.typ.NumField(); i++ {
2024-05-14 13:07:09 +00:00
sf := f.typ.Field(i)
2024-05-14 13:07:09 +00:00
if sf.PkgPath != "" { // unexported
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
tag := sf.Tag.Get("json")
2024-05-14 13:07:09 +00:00
if tag == "-" {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
name, opts := parseTag(tag)
2024-05-14 13:07:09 +00:00
if !isValidTag(name) {
2024-05-14 13:07:09 +00:00
name = ""
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
index := make([]int, len(f.index)+1)
2024-05-14 13:07:09 +00:00
copy(index, f.index)
2024-05-14 13:07:09 +00:00
index[len(f.index)] = i
ft := sf.Type
2024-05-14 13:07:09 +00:00
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
2024-05-14 13:07:09 +00:00
// Follow pointer.
2024-05-14 13:07:09 +00:00
ft = ft.Elem()
2024-05-14 13:07:09 +00:00
}
// Record found field and index sequence.
2024-05-14 13:07:09 +00:00
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
2024-05-14 13:07:09 +00:00
tagged := name != ""
2024-05-14 13:07:09 +00:00
if name == "" {
2024-05-14 13:07:09 +00:00
name = sf.Name
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
fields = append(fields, fillField(field{
name: name,
tag: tagged,
index: index,
typ: ft,
2024-05-14 13:07:09 +00:00
omitEmpty: opts.Contains("omitempty"),
quoted: opts.Contains("string"),
2024-05-14 13:07:09 +00:00
}))
2024-05-14 13:07:09 +00:00
if count[f.typ] > 1 {
2024-05-14 13:07:09 +00:00
// If there were multiple instances, add a second,
2024-05-14 13:07:09 +00:00
// so that the annihilation code will see a duplicate.
2024-05-14 13:07:09 +00:00
// It only cares about the distinction between 1 or 2,
2024-05-14 13:07:09 +00:00
// so don't bother generating any more copies.
2024-05-14 13:07:09 +00:00
fields = append(fields, fields[len(fields)-1])
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
// Record new anonymous struct to explore in next round.
2024-05-14 13:07:09 +00:00
nextCount[ft]++
2024-05-14 13:07:09 +00:00
if nextCount[ft] == 1 {
2024-05-14 13:07:09 +00:00
next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft}))
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
sort.Sort(byName(fields))
// Delete all fields that are hidden by the Go rules for embedded fields,
2024-05-14 13:07:09 +00:00
// except that fields with JSON tags are promoted.
// The fields are sorted in primary order of name, secondary order
2024-05-14 13:07:09 +00:00
// of field index length. Loop over names; for each name, delete
2024-05-14 13:07:09 +00:00
// hidden fields by choosing the one dominant field that survives.
2024-05-14 13:07:09 +00:00
out := fields[:0]
2024-05-14 13:07:09 +00:00
for advance, i := 0, 0; i < len(fields); i += advance {
2024-05-14 13:07:09 +00:00
// One iteration per name.
2024-05-14 13:07:09 +00:00
// Find the sequence of fields with the name of this first field.
2024-05-14 13:07:09 +00:00
fi := fields[i]
2024-05-14 13:07:09 +00:00
name := fi.name
2024-05-14 13:07:09 +00:00
for advance = 1; i+advance < len(fields); advance++ {
2024-05-14 13:07:09 +00:00
fj := fields[i+advance]
2024-05-14 13:07:09 +00:00
if fj.name != name {
2024-05-14 13:07:09 +00:00
break
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if advance == 1 { // Only one field with this name
2024-05-14 13:07:09 +00:00
out = append(out, fi)
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
dominant, ok := dominantField(fields[i : i+advance])
2024-05-14 13:07:09 +00:00
if ok {
2024-05-14 13:07:09 +00:00
out = append(out, dominant)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
fields = out
2024-05-14 13:07:09 +00:00
sort.Sort(byIndex(fields))
return fields
2024-05-14 13:07:09 +00:00
}
// dominantField looks through the fields, all of which are known to
2024-05-14 13:07:09 +00:00
// have the same name, to find the single field that dominates the
2024-05-14 13:07:09 +00:00
// others using Go's embedding rules, modified by the presence of
2024-05-14 13:07:09 +00:00
// JSON tags. If there are multiple top-level fields, the boolean
2024-05-14 13:07:09 +00:00
// will be false: This condition is an error in Go and we skip all
2024-05-14 13:07:09 +00:00
// the fields.
2024-05-14 13:07:09 +00:00
func dominantField(fields []field) (field, bool) {
2024-05-14 13:07:09 +00:00
// The fields are sorted in increasing index-length order. The winner
2024-05-14 13:07:09 +00:00
// must therefore be one with the shortest index length. Drop all
2024-05-14 13:07:09 +00:00
// longer entries, which is easy: just truncate the slice.
2024-05-14 13:07:09 +00:00
length := len(fields[0].index)
2024-05-14 13:07:09 +00:00
tagged := -1 // Index of first tagged field.
2024-05-14 13:07:09 +00:00
for i, f := range fields {
2024-05-14 13:07:09 +00:00
if len(f.index) > length {
2024-05-14 13:07:09 +00:00
fields = fields[:i]
2024-05-14 13:07:09 +00:00
break
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if f.tag {
2024-05-14 13:07:09 +00:00
if tagged >= 0 {
2024-05-14 13:07:09 +00:00
// Multiple tagged fields at the same level: conflict.
2024-05-14 13:07:09 +00:00
// Return no field.
2024-05-14 13:07:09 +00:00
return field{}, false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
tagged = i
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if tagged >= 0 {
2024-05-14 13:07:09 +00:00
return fields[tagged], true
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
// All remaining fields have the same length. If there's more than one,
2024-05-14 13:07:09 +00:00
// we have a conflict (two fields named "X" at the same level) and we
2024-05-14 13:07:09 +00:00
// return no field.
2024-05-14 13:07:09 +00:00
if len(fields) > 1 {
2024-05-14 13:07:09 +00:00
return field{}, false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return fields[0], true
2024-05-14 13:07:09 +00:00
}
var fieldCache struct {
sync.RWMutex
2024-05-14 13:07:09 +00:00
m map[reflect.Type][]field
}
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
2024-05-14 13:07:09 +00:00
func cachedTypeFields(t reflect.Type) []field {
2024-05-14 13:07:09 +00:00
fieldCache.RLock()
2024-05-14 13:07:09 +00:00
f := fieldCache.m[t]
2024-05-14 13:07:09 +00:00
fieldCache.RUnlock()
2024-05-14 13:07:09 +00:00
if f != nil {
2024-05-14 13:07:09 +00:00
return f
2024-05-14 13:07:09 +00:00
}
// Compute fields without lock.
2024-05-14 13:07:09 +00:00
// Might duplicate effort but won't hold other computations back.
2024-05-14 13:07:09 +00:00
f = typeFields(t)
2024-05-14 13:07:09 +00:00
if f == nil {
2024-05-14 13:07:09 +00:00
f = []field{}
2024-05-14 13:07:09 +00:00
}
fieldCache.Lock()
2024-05-14 13:07:09 +00:00
if fieldCache.m == nil {
2024-05-14 13:07:09 +00:00
fieldCache.m = map[reflect.Type][]field{}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
fieldCache.m[t] = f
2024-05-14 13:07:09 +00:00
fieldCache.Unlock()
2024-05-14 13:07:09 +00:00
return f
2024-05-14 13:07:09 +00:00
}
func isValidTag(s string) bool {
2024-05-14 13:07:09 +00:00
if s == "" {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
for _, c := range s {
2024-05-14 13:07:09 +00:00
switch {
2024-05-14 13:07:09 +00:00
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
2024-05-14 13:07:09 +00:00
// Backslash and quote chars are reserved, but
2024-05-14 13:07:09 +00:00
// otherwise any punctuation chars are allowed
2024-05-14 13:07:09 +00:00
// in a tag name.
2024-05-14 13:07:09 +00:00
default:
2024-05-14 13:07:09 +00:00
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
const (
caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
kelvin = '\u212a'
2024-05-14 13:07:09 +00:00
smallLongEss = '\u017f'
)
// foldFunc returns one of four different case folding equivalence
2024-05-14 13:07:09 +00:00
// functions, from most general (and slow) to fastest:
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8
2024-05-14 13:07:09 +00:00
// 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S')
2024-05-14 13:07:09 +00:00
// 3) asciiEqualFold, no special, but includes non-letters (including _)
2024-05-14 13:07:09 +00:00
// 4) simpleLetterEqualFold, no specials, no non-letters.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// The letters S and K are special because they map to 3 runes, not just 2:
2024-06-14 08:41:36 +00:00
// - S maps to s and to U+017F 'ſ' Latin small letter long s
2024-06-14 08:41:36 +00:00
// - k maps to K and to U+212A '' Kelvin sign
2024-06-14 08:41:36 +00:00
//
2024-05-14 13:07:09 +00:00
// See http://play.golang.org/p/tTxjOc0OGo
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// The returned function is specialized for matching against s and
2024-05-14 13:07:09 +00:00
// should only be given s. It's not curried for performance reasons.
2024-05-14 13:07:09 +00:00
func foldFunc(s []byte) func(s, t []byte) bool {
2024-05-14 13:07:09 +00:00
nonLetter := false
2024-05-14 13:07:09 +00:00
special := false // special letter
2024-05-14 13:07:09 +00:00
for _, b := range s {
2024-05-14 13:07:09 +00:00
if b >= utf8.RuneSelf {
2024-05-14 13:07:09 +00:00
return bytes.EqualFold
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
upper := b & caseMask
2024-05-14 13:07:09 +00:00
if upper < 'A' || upper > 'Z' {
2024-05-14 13:07:09 +00:00
nonLetter = true
2024-05-14 13:07:09 +00:00
} else if upper == 'K' || upper == 'S' {
2024-05-14 13:07:09 +00:00
// See above for why these letters are special.
2024-05-14 13:07:09 +00:00
special = true
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if special {
2024-05-14 13:07:09 +00:00
return equalFoldRight
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if nonLetter {
2024-05-14 13:07:09 +00:00
return asciiEqualFold
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return simpleLetterEqualFold
2024-05-14 13:07:09 +00:00
}
// equalFoldRight is a specialization of bytes.EqualFold when s is
2024-05-14 13:07:09 +00:00
// known to be all ASCII (including punctuation), but contains an 's',
2024-05-14 13:07:09 +00:00
// 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t.
2024-05-14 13:07:09 +00:00
// See comments on foldFunc.
2024-05-14 13:07:09 +00:00
func equalFoldRight(s, t []byte) bool {
2024-05-14 13:07:09 +00:00
for _, sb := range s {
2024-05-14 13:07:09 +00:00
if len(t) == 0 {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
tb := t[0]
2024-05-14 13:07:09 +00:00
if tb < utf8.RuneSelf {
2024-05-14 13:07:09 +00:00
if sb != tb {
2024-05-14 13:07:09 +00:00
sbUpper := sb & caseMask
2024-05-14 13:07:09 +00:00
if 'A' <= sbUpper && sbUpper <= 'Z' {
2024-05-14 13:07:09 +00:00
if sbUpper != tb&caseMask {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
t = t[1:]
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
// sb is ASCII and t is not. t must be either kelvin
2024-05-14 13:07:09 +00:00
// sign or long s; sb must be s, S, k, or K.
2024-05-14 13:07:09 +00:00
tr, size := utf8.DecodeRune(t)
2024-05-14 13:07:09 +00:00
switch sb {
2024-05-14 13:07:09 +00:00
case 's', 'S':
2024-05-14 13:07:09 +00:00
if tr != smallLongEss {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
case 'k', 'K':
2024-05-14 13:07:09 +00:00
if tr != kelvin {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
default:
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
t = t[size:]
}
2024-05-14 13:07:09 +00:00
if len(t) > 0 {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
// asciiEqualFold is a specialization of bytes.EqualFold for use when
2024-05-14 13:07:09 +00:00
// s is all ASCII (but may contain non-letters) and contains no
2024-05-14 13:07:09 +00:00
// special-folding letters.
2024-05-14 13:07:09 +00:00
// See comments on foldFunc.
2024-05-14 13:07:09 +00:00
func asciiEqualFold(s, t []byte) bool {
2024-05-14 13:07:09 +00:00
if len(s) != len(t) {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
for i, sb := range s {
2024-05-14 13:07:09 +00:00
tb := t[i]
2024-05-14 13:07:09 +00:00
if sb == tb {
2024-05-14 13:07:09 +00:00
continue
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') {
2024-05-14 13:07:09 +00:00
if sb&caseMask != tb&caseMask {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
// simpleLetterEqualFold is a specialization of bytes.EqualFold for
2024-05-14 13:07:09 +00:00
// use when s is all ASCII letters (no underscores, etc) and also
2024-05-14 13:07:09 +00:00
// doesn't contain 'k', 'K', 's', or 'S'.
2024-05-14 13:07:09 +00:00
// See comments on foldFunc.
2024-05-14 13:07:09 +00:00
func simpleLetterEqualFold(s, t []byte) bool {
2024-05-14 13:07:09 +00:00
if len(s) != len(t) {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
for i, b := range s {
2024-05-14 13:07:09 +00:00
if b&caseMask != t[i]&caseMask {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
// tagOptions is the string following a comma in a struct field's "json"
2024-05-14 13:07:09 +00:00
// tag, or the empty string. It does not include the leading comma.
2024-05-14 13:07:09 +00:00
type tagOptions string
// parseTag splits a struct field's json tag into its name and
2024-05-14 13:07:09 +00:00
// comma-separated options.
2024-05-14 13:07:09 +00:00
func parseTag(tag string) (string, tagOptions) {
2024-05-14 13:07:09 +00:00
if idx := strings.Index(tag, ","); idx != -1 {
2024-05-14 13:07:09 +00:00
return tag[:idx], tagOptions(tag[idx+1:])
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return tag, tagOptions("")
2024-05-14 13:07:09 +00:00
}
// Contains reports whether a comma-separated list of options
2024-05-14 13:07:09 +00:00
// contains a particular substr flag. substr must be surrounded by a
2024-05-14 13:07:09 +00:00
// string boundary or commas.
2024-05-14 13:07:09 +00:00
func (o tagOptions) Contains(optionName string) bool {
2024-05-14 13:07:09 +00:00
if len(o) == 0 {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
s := string(o)
2024-05-14 13:07:09 +00:00
for s != "" {
2024-05-14 13:07:09 +00:00
var next string
2024-05-14 13:07:09 +00:00
i := strings.Index(s, ",")
2024-05-14 13:07:09 +00:00
if i >= 0 {
2024-05-14 13:07:09 +00:00
s, next = s[:i], s[i+1:]
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if s == optionName {
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
s = next
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}