1 // Copyright 2024 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 package impl
6 7 import (
8 "strconv"
9 "sync/atomic"
10 "unsafe"
11 12 "google.golang.org/protobuf/reflect/protoreflect"
13 )
14 15 func (Export) UnmarshalField(msg any, fieldNum int32) {
16 UnmarshalField(msg.(protoreflect.ProtoMessage).ProtoReflect(), protoreflect.FieldNumber(fieldNum))
17 }
18 19 // Present checks the presence set for a certain field number (zero
20 // based, ordered by appearance in original proto file). part is
21 // a pointer to the correct element in the bitmask array, num is the
22 // field number unaltered. Example (field number 70 -> part =
23 // &m.XXX_presence[1], num = 70)
24 func (Export) Present(part *uint32, num uint32) bool {
25 // This hook will read an unprotected shadow presence set if
26 // we're unning under the race detector
27 raceDetectHookPresent(part, num)
28 return atomic.LoadUint32(part)&(1<<(num%32)) > 0
29 }
30 31 // SetPresent adds a field to the presence set. part is a pointer to
32 // the relevant element in the array and num is the field number
33 // unaltered. size is the number of fields in the protocol
34 // buffer.
35 func (Export) SetPresent(part *uint32, num uint32, size uint32) {
36 // This hook will mutate an unprotected shadow presence set if
37 // we're running under the race detector
38 raceDetectHookSetPresent(part, num, presenceSize(size))
39 for {
40 old := atomic.LoadUint32(part)
41 if atomic.CompareAndSwapUint32(part, old, old|(1<<(num%32))) {
42 return
43 }
44 }
45 }
46 47 // SetPresentNonAtomic is like SetPresent, but operates non-atomically.
48 // It is meant for use by builder methods, where the message is known not
49 // to be accessible yet by other goroutines.
50 func (Export) SetPresentNonAtomic(part *uint32, num uint32, size uint32) {
51 // This hook will mutate an unprotected shadow presence set if
52 // we're running under the race detector
53 raceDetectHookSetPresent(part, num, presenceSize(size))
54 *part |= 1 << (num % 32)
55 }
56 57 // ClearPresence removes a field from the presence set. part is a
58 // pointer to the relevant element in the presence array and num is
59 // the field number unaltered.
60 func (Export) ClearPresent(part *uint32, num uint32) {
61 // This hook will mutate an unprotected shadow presence set if
62 // we're running under the race detector
63 raceDetectHookClearPresent(part, num)
64 for {
65 old := atomic.LoadUint32(part)
66 if atomic.CompareAndSwapUint32(part, old, old&^(1<<(num%32))) {
67 return
68 }
69 }
70 }
71 72 // interfaceToPointer takes a pointer to an empty interface whose value is a
73 // pointer type, and converts it into a "pointer" that points to the same
74 // target
75 func interfaceToPointer(i *any) pointer {
76 return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
77 }
78 79 func (p pointer) atomicGetPointer() pointer {
80 return pointer{p: atomic.LoadPointer((*unsafe.Pointer)(p.p))}
81 }
82 83 func (p pointer) atomicSetPointer(q pointer) {
84 atomic.StorePointer((*unsafe.Pointer)(p.p), q.p)
85 }
86 87 // AtomicCheckPointerIsNil takes an interface (which is a pointer to a
88 // pointer) and returns true if the pointed-to pointer is nil (using an
89 // atomic load). This function is inlineable and, on x86, just becomes a
90 // simple load and compare.
91 func (Export) AtomicCheckPointerIsNil(ptr any) bool {
92 return interfaceToPointer(&ptr).atomicGetPointer().IsNil()
93 }
94 95 // AtomicSetPointer takes two interfaces (first is a pointer to a pointer,
96 // second is a pointer) and atomically sets the second pointer into location
97 // referenced by first pointer. Unfortunately, atomicSetPointer() does not inline
98 // (even on x86), so this does not become a simple store on x86.
99 func (Export) AtomicSetPointer(dstPtr, valPtr any) {
100 interfaceToPointer(&dstPtr).atomicSetPointer(interfaceToPointer(&valPtr))
101 }
102 103 // AtomicLoadPointer loads the pointer at the location pointed at by src,
104 // and stores that pointer value into the location pointed at by dst.
105 func (Export) AtomicLoadPointer(ptr Pointer, dst Pointer) {
106 *(*unsafe.Pointer)(unsafe.Pointer(dst)) = atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(ptr)))
107 }
108 109 // AtomicInitializePointer makes ptr and dst point to the same value.
110 //
111 // If *ptr is a nil pointer, it sets *ptr = *dst.
112 //
113 // If *ptr is a non-nil pointer, it sets *dst = *ptr.
114 func (Export) AtomicInitializePointer(ptr Pointer, dst Pointer) {
115 if !atomic.CompareAndSwapPointer((*unsafe.Pointer)(ptr), unsafe.Pointer(nil), *(*unsafe.Pointer)(dst)) {
116 *(*unsafe.Pointer)(unsafe.Pointer(dst)) = atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(ptr)))
117 }
118 }
119 120 // MessageFieldStringOf returns the field formatted as a string,
121 // either as the field name if resolvable otherwise as a decimal string.
122 func (Export) MessageFieldStringOf(md protoreflect.MessageDescriptor, n protoreflect.FieldNumber) string {
123 fd := md.Fields().ByNumber(n)
124 if fd != nil {
125 return string(fd.Name())
126 }
127 return strconv.Itoa(int(n))
128 }
129