/usr/share/gocode/src/github.com/goji/param/struct.go is in golang-github-goji-param-dev 0.0~git20160927.d7f49fd-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | package param
import (
"reflect"
"strings"
"sync"
)
// We decode a lot of structs (since it's the top-level thing this library
// decodes) and it takes a fair bit of work to reflect upon the struct to figure
// out what we want to do. Instead of doing this on every invocation, we cache
// metadata about each struct the first time we see it. The upshot is that we
// save some work every time. The downside is we are forced to briefly acquire
// a lock to access the cache in a thread-safe way. If this ever becomes a
// bottleneck, both the lock and the cache can be sharded or something.
type structCache map[string]cacheLine
type cacheLine struct {
offset int
parse func(string, string, []string, reflect.Value)
}
var cacheLock sync.RWMutex
var cache = make(map[reflect.Type]structCache)
func cacheStruct(t reflect.Type) structCache {
cacheLock.RLock()
sc, ok := cache[t]
cacheLock.RUnlock()
if ok {
return sc
}
// It's okay if two people build struct caches simultaneously
sc = make(structCache)
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
// Only unexported fields have a PkgPath; we want to only cache
// exported fields.
if sf.PkgPath != "" && !sf.Anonymous {
continue
}
name := extractName(sf)
if name != "-" {
sc[name] = cacheLine{i, extractHandler(t, sf)}
}
}
cacheLock.Lock()
cache[t] = sc
cacheLock.Unlock()
return sc
}
// Extract the name of the given struct field, looking at struct tags as
// appropriate.
func extractName(sf reflect.StructField) string {
name := sf.Tag.Get("param")
if name == "" {
name = sf.Tag.Get("json")
idx := strings.IndexRune(name, ',')
if idx >= 0 {
name = name[:idx]
}
}
if name == "" {
name = sf.Name
}
return name
}
func extractHandler(s reflect.Type, sf reflect.StructField) func(string, string, []string, reflect.Value) {
if reflect.PtrTo(sf.Type).Implements(textUnmarshalerType) {
return parseTextUnmarshaler
}
switch sf.Type.Kind() {
case reflect.Bool:
return parseBool
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return parseInt
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return parseUint
case reflect.Float32, reflect.Float64:
return parseFloat
case reflect.Map:
return parseMap
case reflect.Ptr:
return parsePtr
case reflect.Slice:
return parseSlice
case reflect.String:
return parseString
case reflect.Struct:
return parseStruct
default:
pebkac("struct %v has illegal field %q (type %v, kind %v).",
s, sf.Name, sf.Type, sf.Type.Kind())
return nil
}
}
// We have to parse two types of structs: ones at the top level, whose keys
// don't have square brackets around them, and nested structs, which do.
func parseStructField(cache structCache, key, sk, keytail string, values []string, target reflect.Value) {
l, ok := cache[sk]
if !ok {
panic(KeyError{
FullKey: key,
Key: kpath(key, keytail),
Type: target.Type(),
Field: sk,
})
}
f := target.Field(l.offset)
l.parse(key, keytail, values, f)
}
|