forked from go-playground/form
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cache.go
133 lines (100 loc) · 2.48 KB
/
cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package form
import (
"reflect"
"sort"
"strings"
"sync"
"sync/atomic"
)
type cacheFields []cachedField
func (s cacheFields) Len() int {
return len(s)
}
func (s cacheFields) Less(i, j int) bool {
return !s[i].isAnonymous
}
func (s cacheFields) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
type cachedField struct {
idx int
name string
isAnonymous bool
isOmitEmpty bool
}
type cachedStruct struct {
fields cacheFields
}
type structCacheMap struct {
m atomic.Value // map[reflect.Type]*cachedStruct
lock sync.Mutex
tagFn TagNameFunc
}
// TagNameFunc allows for adding of a custom tag name parser
type TagNameFunc func(field reflect.StructField) string
func newStructCacheMap() *structCacheMap {
sc := new(structCacheMap)
sc.m.Store(make(map[reflect.Type]*cachedStruct))
return sc
}
func (s *structCacheMap) Get(key reflect.Type) (value *cachedStruct, ok bool) {
value, ok = s.m.Load().(map[reflect.Type]*cachedStruct)[key]
return
}
func (s *structCacheMap) Set(key reflect.Type, value *cachedStruct) {
m := s.m.Load().(map[reflect.Type]*cachedStruct)
nm := make(map[reflect.Type]*cachedStruct, len(m)+1)
for k, v := range m {
nm[k] = v
}
nm[key] = value
s.m.Store(nm)
}
func (s *structCacheMap) parseStruct(mode Mode, current reflect.Value, key reflect.Type, tagName string) *cachedStruct {
s.lock.Lock()
// could have been multiple trying to access, but once first is done this ensures struct
// isn't parsed again.
cs, ok := s.Get(key)
if ok {
s.lock.Unlock()
return cs
}
typ := current.Type()
cs = &cachedStruct{fields: make([]cachedField, 0, 4)} // init 4, betting most structs decoding into have at aleast 4 fields.
numFields := current.NumField()
var fld reflect.StructField
var name string
var idx int
var isOmitEmpty bool
for i := 0; i < numFields; i++ {
isOmitEmpty = false
fld = typ.Field(i)
if fld.PkgPath != blank && !fld.Anonymous {
continue
}
if s.tagFn != nil {
name = s.tagFn(fld)
} else {
name = fld.Tag.Get(tagName)
}
if name == ignore {
continue
}
if mode == ModeExplicit && len(name) == 0 {
continue
}
// check for omitempty
if idx = strings.LastIndexByte(name, ','); idx != -1 {
isOmitEmpty = name[idx+1:] == "omitempty"
name = name[:idx]
}
if len(name) == 0 {
name = fld.Name
}
cs.fields = append(cs.fields, cachedField{idx: i, name: name, isAnonymous: fld.Anonymous, isOmitEmpty: isOmitEmpty})
}
sort.Sort(cs.fields)
s.Set(typ, cs)
s.lock.Unlock()
return cs
}