blob: 691f0d88b608f1777ac2297021a8f1d586dd9c53 [file] [log] [blame]
khenaidooefff76e2021-12-15 16:51:30 -05001//go:build !appengine && !gopherjs && !purego
2// +build !appengine,!gopherjs,!purego
3
4// NB: other environments where unsafe is unappropriate should use "purego" build tag
5// https://github.com/golang/go/issues/23172
6
7package desc
8
9import (
10 "sync/atomic"
11 "unsafe"
12)
13
14type jsonNameMap map[string]*FieldDescriptor // loaded/stored atomically via atomic+unsafe
15type memoizedDefault *interface{} // loaded/stored atomically via atomic+unsafe
16
17// FindFieldByJSONName finds the field with the given JSON field name. If no such
18// field exists then nil is returned. Only regular fields are returned, not
19// extensions.
20func (md *MessageDescriptor) FindFieldByJSONName(jsonName string) *FieldDescriptor {
21 // NB: We don't want to eagerly index JSON names because many programs won't use it.
22 // So we want to do it lazily, but also make sure the result is thread-safe. So we
23 // atomically load/store the map as if it were a normal pointer. We don't use other
24 // mechanisms -- like sync.Mutex, sync.RWMutex, sync.Once, or atomic.Value -- to
25 // do this lazily because those types cannot be copied, and we'd rather not induce
26 // 'go vet' errors in programs that use descriptors and try to copy them.
27 // If multiple goroutines try to access the index at the same time, before it is
28 // built, they will all end up computing the index redundantly. Future reads of
29 // the index will use whatever was the "last one stored" by those racing goroutines.
30 // Since building the index is deterministic, this is fine: all indices computed
31 // will be the same.
32 addrOfJsonNames := (*unsafe.Pointer)(unsafe.Pointer(&md.jsonNames))
33 jsonNames := atomic.LoadPointer(addrOfJsonNames)
34 var index map[string]*FieldDescriptor
35 if jsonNames == nil {
36 // slow path: compute the index
37 index = map[string]*FieldDescriptor{}
38 for _, f := range md.fields {
39 jn := f.GetJSONName()
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}