gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/encoding/cache.go (about)

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