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