func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
	var conflicts []int
	for i := range tinfo.fields {
		oldf := &tinfo.fields[i]
		if newf.name == oldf.name {
			conflicts = append(conflicts, i)
		}
	}
	if conflicts == nil {
		tinfo.fields = append(tinfo.fields, *newf)
		return nil
	}
	for _, i := range conflicts {
		if len(tinfo.fields[i].idx) < len(newf.idx) {
			return nil
		}
	}
	for c := len(conflicts) - 1; c >= 0; c-- {
		i := conflicts[c]
		copy(tinfo.fields[i:], tinfo.fields[i+1:])
		tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
	}
	tinfo.fields = append(tinfo.fields, *newf)
	return nil
}
addFieldInfo adds finfo to tinfo.fields if there are no
conflicts, or if conflicts arise from previous fields that were
obtained from deeper embedded structures than finfo. In the latter
case, the conflicting entries are dropped.
A conflict occurs when the path (parent + name) to a field is
itself a prefix of another path, or when two paths match exactly.
It is okay for field paths to share a common, shorter prefix.
package plist
import (
	"reflect"
	"strings"
	"sync"
)
type typeInfo struct {
	fields []fieldInfo
}
type fieldInfo struct {
	idx       []int
	name      string
	omitEmpty bool
}
var tinfoMap = make(map[reflect.Type]*typeInfo)
var tinfoLock sync.RWMutex
func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
	tinfoLock.RLock()
	tinfo, ok := tinfoMap[typ]
	tinfoLock.RUnlock()
	if ok {
		return tinfo, nil
	}
	tinfo = &typeInfo{}
	if typ.Kind() == reflect.Struct {
		n := typ.NumField()
		for i := 0; i < n; i++ {
			f := typ.Field(i)
			if f.PkgPath != "" || f.Tag.Get("plist") == "-" {
				continue  
			}
			if f.Anonymous {
				t := f.Type
				if t.Kind() == reflect.Ptr {
					t = t.Elem()
				}
				if t.Kind() == reflect.Struct {
					inner, err := getTypeInfo(t)
					if err != nil {
						return nil, err
					}
					for _, finfo := range inner.fields {
						finfo.idx = append([]int{i}, finfo.idx...)
						if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
							return nil, err
						}
					}
					continue
				}
			}
			finfo, err := structFieldInfo(typ, &f)
			if err != nil {
				return nil, err
			}
			if err := addFieldInfo(typ, tinfo, finfo); err != nil {
				return nil, err
			}
		}
	}
	tinfoLock.Lock()
	tinfoMap[typ] = tinfo
	tinfoLock.Unlock()
	return tinfo, nil
}
func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
	finfo := &fieldInfo{idx: f.Index}
	tag := f.Tag.Get("plist")
	tokens := strings.Split(tag, ",")
	tag = tokens[0]
	if len(tokens) > 1 {
		tag = tokens[0]
		for _, flag := range tokens[1:] {
			switch flag {
			case "omitempty":
				finfo.omitEmpty = true
			}
		}
	}
	if tag == "" {
		finfo.name = f.Name
		return finfo, nil
	}
	finfo.name = tag
	return finfo, nil
}