// typeFields returns a list of fields that should be recognized for the given
// type. The algorithm is breadth-first search over the set of structs to
// include - the top struct and then any reachable anonymous structs.
typeFields returns a list of fields that should be recognized for the given
type. The algorithm is breadth-first search over the set of structs to
include - the top struct and then any reachable anonymous structs.
// typeFields returns a list of fields that should be recognized for the given // type . The algorithm is breadth - first search over the set of structs to // include - the top struct and then any reachable anonymous structs .
func typeFields(t reflect.Type, cfg *Config) []field {
	current := []field{}
	next := []field{{typ: t}}
	count := map[reflect.Type]int{}
	nextCount := map[reflect.Type]int{}
	visited := map[reflect.Type]bool{}
	var fields []field
	for len(next) > 0 {
		current, next = next, current[:0]
		count, nextCount = nextCount, map[reflect.Type]int{}
		for _, f := range current {
			if visited[f.typ] {
				continue
			}
			visited[f.typ] = true
			for i := 0; i < f.typ.NumField(); i++ {
				sf := f.typ.Field(i)
				isUnexported := sf.PkgPath != ""
				isEmbedded := sf.Anonymous
				if isEmbedded {
					t := sf.Type
					if t.Kind() == reflect.Ptr {
						t = t.Elem()
					}
					if isUnexported && t.Kind() != reflect.Struct {
						continue
					}
				} else if isUnexported {
					continue
				}
				tag := sf.Tag.Get(cfg.TagName)
				tagged := tag != ""
				name, opts := parseTag(tag)
				if name == "-" {
					continue
				}
				if name == "" {
					name = sf.Name
				}
				index := make([]int, len(f.index)+1)
				copy(index, f.index)
				index[len(f.index)] = i
				sft := sf.Type
				if sft.Name() == "" && sft.Kind() == reflect.Ptr {
					sft = sft.Elem()
				}
				if tagged || !isEmbedded || sft.Kind() != reflect.Struct {
					fields = append(fields, fillField(field{
						name:    name,
						tagged:  tagged,
						index:   index,
						typ:     sft,
						options: opts,
					}))
					if count[f.typ] > 1 {
						fields = append(fields, fields[len(fields)-1])
					}
					continue
				}
				nextCount[sft]++
				if nextCount[sft] == 1 {
					next = append(next, fillField(field{
						name:  sft.Name(),
						index: index,
						typ:   sft,
					}))
				}
			}
		}
	}
	sort.Slice(fields, func(i, j int) bool {
		l := fields[i]
		r := fields[j]
		if l.name != r.name {
			return l.name < r.name
		}
		if len(l.index) != len(r.index) {
			return len(l.index) < len(r.index)
		}
		if l.tagged != r.tagged {
			return l.tagged
		}
		for k := 0; k < len(l.index); k++ {
			if k >= len(r.index) {
				return false
			}
			if l.index[k] != r.index[k] {
				return l.index[k] < r.index[k]
			}
		}
		return len(l.index) < len(r.index)
	})
	out := fields[:0]
	for i, count := 0, 0; i < len(fields); i += count {
		for count = 1; i+count < len(fields); count++ {
			if fields[i].name != fields[i+count].name {
				break
			}
		}
		if count == 1 {
			out = append(out, fields[i])
			continue
		}
		contended := fields[i : i+count]
		taggedIndex := -1
		for j, f := range contended {
			if len(f.index) > len(contended[0].index) {
				contended = contended[:j]
				break
			}
			if f.tagged {
				if taggedIndex >= 0 {
					panic(fmt.Errorf("encmap: ambiguous tagged field name '%s' in %s", f.name, t.Name()))
				}
				taggedIndex = j
			}
		}
		if taggedIndex >= 0 {
			out = append(out, contended[taggedIndex])
			continue
		}
		if len(contended) > 1 {
			panic(fmt.Errorf("encmap: ambiguous tagged field name '%s' in %s", contended[0].name, t.Name()))
		}
		out = append(out, contended[0])
	}
	fields = out
	sort.Sort(byIndex(fields))
	return fields
}
func typeFields ( t reflect . Type , cfg * Config ) [ ] field { current := [ ] field { } next := [ ] field { { typ : t } } count := map [ reflect . Type ] int { } nextCount := map [ reflect . Type ] int { } visited := map [ reflect . Type ] bool { } var fields [ ] field for len ( next ) > 0 { current , next = next , current [ : 0 ] count , nextCount = nextCount , map [ reflect . Type ] int { } for _ , f := range current { if visited [ f . typ ] { continue } visited [ f . typ ] = true for i := 0 ; i < f . typ . NumField ( ) ; i ++ { sf := f . typ . Field ( i ) isUnexported := sf . PkgPath != " " isEmbedded := sf . Anonymous if isEmbedded { t := sf . Type if t . Kind ( ) == reflect . Ptr { t = t . Elem ( ) } if isUnexported && t . Kind ( ) != reflect . Struct { continue } } else if isUnexported { continue } tag := sf . Tag . Get ( cfg . TagName ) tagged := tag != " " name , opts := parseTag ( tag ) if name == " " { continue } if name == " " { name = sf . Name } index := make ( [ ] int , len ( f . index ) + 1 ) copy ( index , f . index ) index [ len ( f . index ) ] = i sft := sf . Type if sft . Name ( ) == " " && sft . Kind ( ) == reflect . Ptr { sft = sft . Elem ( ) } if tagged || ! isEmbedded || sft . Kind ( ) != reflect . Struct { fields = append ( fields , fillField ( field { name : name , tagged : tagged , index : index , typ : sft , options : opts , } ) ) if count [ f . typ ] > 1 { fields = append ( fields , fields [ len ( fields ) - 1 ] ) } continue } nextCount [ sft ] ++ if nextCount [ sft ] == 1 { next = append ( next , fillField ( field { name : sft . Name ( ) , index : index , typ : sft , } ) ) } } } } sort . Slice ( fields , func ( i , j int ) bool { l := fields [ i ] r := fields [ j ] if l . name != r . name { return l . name < r . name } if len ( l . index ) != len ( r . index ) { return len ( l . index ) < len ( r . index ) } if l . tagged != r . tagged { return l . tagged } for k := 0 ; k < len ( l . index ) ; k ++ { if k >= len ( r . index ) { return false } if l . index [ k ] != r . index [ k ] { return l . index [ k ] < r . index [ k ] } } return len ( l . index ) < len ( r . index ) } ) out := fields [ : 0 ] for i , count := 0 , 0 ; i < len ( fields ) ; i += count { for count = 1 ; i + count < len ( fields ) ; count ++ { if fields [ i ] . name != fields [ i + count ] . name { break } } if count == 1 { out = append ( out , fields [ i ] ) continue } contended := fields [ i : i + count ] taggedIndex := - 1 for j , f := range contended { if len ( f . index ) > len ( contended [ 0 ] . index ) { contended = contended [ : j ] break } if f . tagged { if taggedIndex >= 0 { panic ( fmt . Errorf ( " " , f . name , t . Name ( ) ) ) } taggedIndex = j } } if taggedIndex >= 0 { out = append ( out , contended [ taggedIndex ] ) continue } if len ( contended ) > 1 { panic ( fmt . Errorf ( " " , contended [ 0 ] . name , t . Name ( ) ) ) } out = append ( out , contended [ 0 ] ) } fields = out sort . Sort ( byIndex ( fields ) ) return fields }
typeFields returns a list of fields that should be recognized for the given
type.
typeFields returns a list of fields that should be recognized for the given type .
// Anonymous fields to explore at the current level and the next. // Count of queued names for current level and the next. // Types already visited at an earlier level. // Fields found. // Scan f.typ for fields to include. // Ignore embedded fields of unexported non-structs. // Would these be the fields of unexported Complex // numbers? I'm not sure when we would hit this. // Do not ignore embedded fields of unexported structs, // because they may have exported fields. // TODO: Consider adding a check to ensure `name` is a valid key // If we're looking at an unnamed pointer type, follow the // pointer to the underlying type. // Record the found field and index sequence ... // If there were multiple instances, add a second, so // that the annihilation code will see a duplicate. // It only cares about the distinction between 1 or 2, // so don't bother generating any more copies. // ... or record a new anonymous struct to be explored in the // next round. // Sort field first by name, then breaking ties with index sequence length, // then breaking ties with "name came from map tag", then breaking ties with // index sequence. // Delete all fields that are hidden based on Go's rules for embedded // fields, modified for the presence of map tags. // Find the number of fields that share a name // If there's only one field named `name`, our job is easy ... // ... otherwise, find the single field that dominates the other // similarly named fields using Go's embedding rules, modified by the // presence of map tags. If there are multiple top-level fields -- which // is an error in Go -- we mirror the compile-time "ambiguous selector" // error as closely as possible at runtime. With a panic. // A shorter index length indicates a more dominant field. The // `contended` slice is sorted in increasing index-length order. We // can therefore drop longer (and less dominant) entries by simply // truncating the slice. // Fields with map tags are given special precedence for the given // index-length level, so we need to keep track of their presence. // If there are multiple tagged fields at the same index level, // we have a genuine conflict. // All remaining contended fields have the same length. If there's more // than one, we have a conflict.