blob: 6ff872fd5c29304f1c702a00bd7a38daecfafcf1 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001//+build !appengine
2// TODO: exclude other build tags for environments where unsafe package is inappropriate
3
4package desc
5
6import (
7 "sync/atomic"
8 "unsafe"
9)
10
11type jsonNameMap map[string]*FieldDescriptor // loaded/stored atomically via atomic+unsafe
12type memoizedDefault *interface{} // loaded/stored atomically via atomic+unsafe
13
14// FindFieldByJSONName finds the field with the given JSON field name. If no such
15// field exists then nil is returned. Only regular fields are returned, not
16// extensions.
17func (md *MessageDescriptor) FindFieldByJSONName(jsonName string) *FieldDescriptor {
18 // NB: We don't want to eagerly index JSON names because many programs won't use it.
19 // So we want to do it lazily, but also make sure the result is thread-safe. So we
20 // atomically load/store the map as if it were a normal pointer. We don't use other
21 // mechanisms -- like sync.Mutex, sync.RWMutex, sync.Once, or atomic.Value -- to
22 // do this lazily because those types cannot be copied, and we'd rather not induce
23 // 'go vet' errors in programs that use descriptors and try to copy them.
24 // If multiple goroutines try to access the index at the same time, before it is
25 // built, they will all end up computing the index redundantly. Future reads of
26 // the index will use whatever was the "last one stored" by those racing goroutines.
27 // Since building the index is deterministic, this is fine: all indices computed
28 // will be the same.
29 addrOfJsonNames := (*unsafe.Pointer)(unsafe.Pointer(&md.jsonNames))
30 jsonNames := atomic.LoadPointer(addrOfJsonNames)
31 var index map[string]*FieldDescriptor
32 if jsonNames == nil {
33 // slow path: compute the index
34 index = map[string]*FieldDescriptor{}
35 for _, f := range md.fields {
36 jn := f.proto.GetJsonName()
37 if jn == "" {
38 jn = f.proto.GetName()
39 }
40 index[jn] = f
41 }
42 atomic.StorePointer(addrOfJsonNames, *(*unsafe.Pointer)(unsafe.Pointer(&index)))
43 } else {
44 *(*unsafe.Pointer)(unsafe.Pointer(&index)) = jsonNames
45 }
46 return index[jsonName]
47}
48
49func (fd *FieldDescriptor) getDefaultValue() interface{} {
50 addrOfDef := (*unsafe.Pointer)(unsafe.Pointer(&fd.def))
51 def := atomic.LoadPointer(addrOfDef)
52 if def != nil {
53 return *(*interface{})(def)
54 }
55 // slow path: compute the default, potentially involves decoding value
56 d := fd.determineDefault()
57 atomic.StorePointer(addrOfDef, (unsafe.Pointer(&d)))
58 return d
59}