summaryrefslogtreecommitdiff
path: root/struct.go
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@datawire.io>2022-08-14 20:52:06 -0600
committerLuke Shumaker <lukeshu@datawire.io>2022-08-16 00:05:24 -0600
commit54bbd1e59317a6e9658eb8098657078cc8e81979 (patch)
tree0d7033a0644945dedfe0fca158e0c40864f759f6 /struct.go
parent2ae2ebe2a5ac712db6f9221cb1ad8cfa76aad180 (diff)
wip: Reduce test differences [ci-skip]
- Handle UTF-16 surrogate pairs - Handle cycles in values - Handle cycles in types - Better errors - Handle case-folding of struct field names - Allow []byteTypeWithMethods - Fix struct field-order - Fix handling of interfaces storing pointers - Enforce a maximum decode depth - Validate struct tags
Diffstat (limited to 'struct.go')
-rw-r--r--struct.go61
1 files changed, 38 insertions, 23 deletions
diff --git a/struct.go b/struct.go
index ad142d6..ee2bbf3 100644
--- a/struct.go
+++ b/struct.go
@@ -22,24 +22,24 @@ type structIndex struct {
}
func indexStruct(typ reflect.Type) structIndex {
- byName := make(map[string][]structField)
- var byPos []string
+ var byPos []structField
+ byName := make(map[string][]int)
- indexStructInner(typ, nil, byName, &byPos)
+ indexStructInner(typ, nil, &byPos, byName, map[reflect.Type]struct{}{})
ret := structIndex{
byName: make(map[string]int),
}
- for _, name := range byPos {
- fields := byName[name]
- delete(byName, name)
- switch len(fields) {
+ for curPos, _field := range byPos {
+ name := _field.Name
+ fieldPoss := byName[name]
+ switch len(fieldPoss) {
case 0:
// do nothing
case 1:
ret.byName[name] = len(ret.byPos)
- ret.byPos = append(ret.byPos, fields[0])
+ ret.byPos = append(ret.byPos, _field)
default:
// To quote the encoding/json docs (version 1.18.4):
//
@@ -56,27 +56,29 @@ func indexStruct(typ reflect.Type) structIndex {
//
// 3) Otherwise there are multiple fields, and all are ignored; no error
// occurs.
- leastLevel := len(fields[0].Path)
- for _, field := range fields[1:] {
+ leastLevel := len(byPos[fieldPoss[0]].Path)
+ for _, fieldPos := range fieldPoss[1:] {
+ field := byPos[fieldPos]
if len(field.Path) < leastLevel {
leastLevel = len(field.Path)
}
}
var numUntagged, numTagged int
- var untaggedIdx, taggedIdx int
- for i, field := range fields {
+ var untaggedPos, taggedPos int
+ for _, fieldPos := range fieldPoss {
+ field := byPos[fieldPos]
if len(field.Path) != leastLevel {
continue
}
if field.Tagged {
numTagged++
- taggedIdx = i
+ taggedPos = fieldPos
if numTagged > 1 {
break // optimization
}
} else {
numUntagged++
- untaggedIdx = i
+ untaggedPos = fieldPos
}
}
switch numTagged {
@@ -85,12 +87,16 @@ func indexStruct(typ reflect.Type) structIndex {
case 0:
// do nothing
case 1:
- ret.byName[name] = len(ret.byPos)
- ret.byPos = append(ret.byPos, fields[untaggedIdx])
+ if curPos == untaggedPos {
+ ret.byName[name] = len(ret.byPos)
+ ret.byPos = append(ret.byPos, byPos[curPos])
+ }
}
case 1:
- ret.byName[name] = len(ret.byPos)
- ret.byPos = append(ret.byPos, fields[taggedIdx])
+ if curPos == taggedPos {
+ ret.byName[name] = len(ret.byPos)
+ ret.byPos = append(ret.byPos, byPos[curPos])
+ }
}
}
}
@@ -98,7 +104,13 @@ func indexStruct(typ reflect.Type) structIndex {
return ret
}
-func indexStructInner(typ reflect.Type, prefix []int, byName map[string][]structField, byPos *[]string) {
+func indexStructInner(typ reflect.Type, prefix []int, byPos *[]structField, byName map[string][]int, seen map[reflect.Type]struct{}) {
+ if _, ok := seen[typ]; ok {
+ return
+ }
+ seen[typ] = struct{}{}
+ defer delete(seen, typ)
+
n := typ.NumField()
for i := 0; i < n; i++ {
path := append(append([]int(nil), prefix...), i)
@@ -123,25 +135,28 @@ func indexStructInner(typ reflect.Type, prefix []int, byName map[string][]struct
}
tagName, opts := parseTag(tag)
name := tagName
+ if !isValidTag(name) {
+ name = ""
+ }
if name == "" {
name = fTyp.Name
}
- if embed {
+ if embed && tagName == "" {
t := fTyp.Type
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
- indexStructInner(t, path, byName, byPos)
+ indexStructInner(t, path, byPos, byName, seen)
} else {
- byName[name] = append(byName[name], structField{
+ byName[name] = append(byName[name], len(*byPos))
+ *byPos = append(*byPos, structField{
Name: name,
Path: path,
Tagged: tagName != "",
OmitEmpty: opts.Contains("omitempty"),
Quote: opts.Contains("string") && isQuotable(fTyp.Type),
})
- *byPos = append(*byPos, name)
}
}
}