type_fields.go (6475B)
1 package toml 2 3 // Struct field handling is adapted from code in encoding/json: 4 // 5 // Copyright 2010 The Go Authors. All rights reserved. 6 // Use of this source code is governed by a BSD-style 7 // license that can be found in the Go distribution. 8 9 import ( 10 "reflect" 11 "sort" 12 "sync" 13 ) 14 15 // A field represents a single field found in a struct. 16 type field struct { 17 name string // the name of the field (`toml` tag included) 18 tag bool // whether field has a `toml` tag 19 index []int // represents the depth of an anonymous field 20 typ reflect.Type // the type of the field 21 } 22 23 // byName sorts field by name, breaking ties with depth, 24 // then breaking ties with "name came from toml tag", then 25 // breaking ties with index sequence. 26 type byName []field 27 28 func (x byName) Len() int { return len(x) } 29 func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 30 func (x byName) Less(i, j int) bool { 31 if x[i].name != x[j].name { 32 return x[i].name < x[j].name 33 } 34 if len(x[i].index) != len(x[j].index) { 35 return len(x[i].index) < len(x[j].index) 36 } 37 if x[i].tag != x[j].tag { 38 return x[i].tag 39 } 40 return byIndex(x).Less(i, j) 41 } 42 43 // byIndex sorts field by index sequence. 44 type byIndex []field 45 46 func (x byIndex) Len() int { return len(x) } 47 func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 48 func (x byIndex) Less(i, j int) bool { 49 for k, xik := range x[i].index { 50 if k >= len(x[j].index) { 51 return false 52 } 53 if xik != x[j].index[k] { 54 return xik < x[j].index[k] 55 } 56 } 57 return len(x[i].index) < len(x[j].index) 58 } 59 60 // typeFields returns a list of fields that TOML should recognize for the given 61 // type. The algorithm is breadth-first search over the set of structs to 62 // include - the top struct and then any reachable anonymous structs. 63 func typeFields(t reflect.Type) []field { 64 // Anonymous fields to explore at the current level and the next. 65 current := []field{} 66 next := []field{{typ: t}} 67 68 // Count of queued names for current level and the next. 69 var count map[reflect.Type]int 70 var nextCount map[reflect.Type]int 71 72 // Types already visited at an earlier level. 73 visited := map[reflect.Type]bool{} 74 75 // Fields found. 76 var fields []field 77 78 for len(next) > 0 { 79 current, next = next, current[:0] 80 count, nextCount = nextCount, map[reflect.Type]int{} 81 82 for _, f := range current { 83 if visited[f.typ] { 84 continue 85 } 86 visited[f.typ] = true 87 88 // Scan f.typ for fields to include. 89 for i := 0; i < f.typ.NumField(); i++ { 90 sf := f.typ.Field(i) 91 if sf.PkgPath != "" && !sf.Anonymous { // unexported 92 continue 93 } 94 opts := getOptions(sf.Tag) 95 if opts.skip { 96 continue 97 } 98 index := make([]int, len(f.index)+1) 99 copy(index, f.index) 100 index[len(f.index)] = i 101 102 ft := sf.Type 103 if ft.Name() == "" && ft.Kind() == reflect.Ptr { 104 // Follow pointer. 105 ft = ft.Elem() 106 } 107 108 // Record found field and index sequence. 109 if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { 110 tagged := opts.name != "" 111 name := opts.name 112 if name == "" { 113 name = sf.Name 114 } 115 fields = append(fields, field{name, tagged, index, ft}) 116 if count[f.typ] > 1 { 117 // If there were multiple instances, add a second, 118 // so that the annihilation code will see a duplicate. 119 // It only cares about the distinction between 1 or 2, 120 // so don't bother generating any more copies. 121 fields = append(fields, fields[len(fields)-1]) 122 } 123 continue 124 } 125 126 // Record new anonymous struct to explore in next round. 127 nextCount[ft]++ 128 if nextCount[ft] == 1 { 129 f := field{name: ft.Name(), index: index, typ: ft} 130 next = append(next, f) 131 } 132 } 133 } 134 } 135 136 sort.Sort(byName(fields)) 137 138 // Delete all fields that are hidden by the Go rules for embedded fields, 139 // except that fields with TOML tags are promoted. 140 141 // The fields are sorted in primary order of name, secondary order 142 // of field index length. Loop over names; for each name, delete 143 // hidden fields by choosing the one dominant field that survives. 144 out := fields[:0] 145 for advance, i := 0, 0; i < len(fields); i += advance { 146 // One iteration per name. 147 // Find the sequence of fields with the name of this first field. 148 fi := fields[i] 149 name := fi.name 150 for advance = 1; i+advance < len(fields); advance++ { 151 fj := fields[i+advance] 152 if fj.name != name { 153 break 154 } 155 } 156 if advance == 1 { // Only one field with this name 157 out = append(out, fi) 158 continue 159 } 160 dominant, ok := dominantField(fields[i : i+advance]) 161 if ok { 162 out = append(out, dominant) 163 } 164 } 165 166 fields = out 167 sort.Sort(byIndex(fields)) 168 169 return fields 170 } 171 172 // dominantField looks through the fields, all of which are known to 173 // have the same name, to find the single field that dominates the 174 // others using Go's embedding rules, modified by the presence of 175 // TOML tags. If there are multiple top-level fields, the boolean 176 // will be false: This condition is an error in Go and we skip all 177 // the fields. 178 func dominantField(fields []field) (field, bool) { 179 // The fields are sorted in increasing index-length order. The winner 180 // must therefore be one with the shortest index length. Drop all 181 // longer entries, which is easy: just truncate the slice. 182 length := len(fields[0].index) 183 tagged := -1 // Index of first tagged field. 184 for i, f := range fields { 185 if len(f.index) > length { 186 fields = fields[:i] 187 break 188 } 189 if f.tag { 190 if tagged >= 0 { 191 // Multiple tagged fields at the same level: conflict. 192 // Return no field. 193 return field{}, false 194 } 195 tagged = i 196 } 197 } 198 if tagged >= 0 { 199 return fields[tagged], true 200 } 201 // All remaining fields have the same length. If there's more than one, 202 // we have a conflict (two fields named "X" at the same level) and we 203 // return no field. 204 if len(fields) > 1 { 205 return field{}, false 206 } 207 return fields[0], true 208 } 209 210 var fieldCache struct { 211 sync.RWMutex 212 m map[reflect.Type][]field 213 } 214 215 // cachedTypeFields is like typeFields but uses a cache to avoid repeated work. 216 func cachedTypeFields(t reflect.Type) []field { 217 fieldCache.RLock() 218 f := fieldCache.m[t] 219 fieldCache.RUnlock() 220 if f != nil { 221 return f 222 } 223 224 // Compute fields without lock. 225 // Might duplicate effort but won't hold other computations back. 226 f = typeFields(t) 227 if f == nil { 228 f = []field{} 229 } 230 231 fieldCache.Lock() 232 if fieldCache.m == nil { 233 fieldCache.m = map[reflect.Type][]field{} 234 } 235 fieldCache.m[t] = f 236 fieldCache.Unlock() 237 return f 238 }