gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

cache.go (2541B)


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