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 "sync/atomic"
9 "unsafe"
10 )
11 12 // presenceSize represents the size of a presence set, which should be the largest index of the set+1
13 type presenceSize uint32
14 15 // presence is the internal representation of the bitmap array in a generated protobuf
16 type presence struct {
17 // This is a pointer to the beginning of an array of uint32
18 P unsafe.Pointer
19 }
20 21 func (p presence) toElem(num uint32) (ret *uint32) {
22 const (
23 bitsPerByte = 8
24 siz = unsafe.Sizeof(*ret)
25 )
26 // p.P points to an array of uint32, num is the bit in this array that the
27 // caller wants to check/manipulate. Calculate the index in the array that
28 // contains this specific bit. E.g.: 76 / 32 = 2 (integer division).
29 offset := uintptr(num) / (siz * bitsPerByte) * siz
30 return (*uint32)(unsafe.Pointer(uintptr(p.P) + offset))
31 }
32 33 // Present checks for the presence of a specific field number in a presence set.
34 func (p presence) Present(num uint32) bool {
35 return Export{}.Present(p.toElem(num), num)
36 }
37 38 // SetPresent adds presence for a specific field number in a presence set.
39 func (p presence) SetPresent(num uint32, size presenceSize) {
40 Export{}.SetPresent(p.toElem(num), num, uint32(size))
41 }
42 43 // SetPresentUnatomic adds presence for a specific field number in a presence set without using
44 // atomic operations. Only to be called during unmarshaling.
45 func (p presence) SetPresentUnatomic(num uint32, size presenceSize) {
46 Export{}.SetPresentNonAtomic(p.toElem(num), num, uint32(size))
47 }
48 49 // ClearPresent removes presence for a specific field number in a presence set.
50 func (p presence) ClearPresent(num uint32) {
51 Export{}.ClearPresent(p.toElem(num), num)
52 }
53 54 // LoadPresenceCache (together with PresentInCache) allows for a
55 // cached version of checking for presence without re-reading the word
56 // for every field. It is optimized for efficiency and assumes no
57 // simltaneous mutation of the presence set (or at least does not have
58 // a problem with simultaneous mutation giving inconsistent results).
59 func (p presence) LoadPresenceCache() (current uint32) {
60 if p.P == nil {
61 return 0
62 }
63 return atomic.LoadUint32((*uint32)(p.P))
64 }
65 66 // PresentInCache reads presence from a cached word in the presence
67 // bitmap. It caches up a new word if the bit is outside the
68 // word. This is for really fast iteration through bitmaps in cases
69 // where we either know that the bitmap will not be altered, or we
70 // don't care about inconsistencies caused by simultaneous writes.
71 func (p presence) PresentInCache(num uint32, cachedElement *uint32, current *uint32) bool {
72 if num/32 != *cachedElement {
73 o := uintptr(num/32) * unsafe.Sizeof(uint32(0))
74 q := (*uint32)(unsafe.Pointer(uintptr(p.P) + o))
75 *current = atomic.LoadUint32(q)
76 *cachedElement = num / 32
77 }
78 return (*current & (1 << (num % 32))) > 0
79 }
80 81 // AnyPresent checks if any field is marked as present in the bitmap.
82 func (p presence) AnyPresent(size presenceSize) bool {
83 n := uintptr((size + 31) / 32)
84 for j := uintptr(0); j < n; j++ {
85 o := j * unsafe.Sizeof(uint32(0))
86 q := (*uint32)(unsafe.Pointer(uintptr(p.P) + o))
87 b := atomic.LoadUint32(q)
88 if b > 0 {
89 return true
90 }
91 }
92 return false
93 }
94 95 // toRaceDetectData finds the preceding RaceDetectHookData in a
96 // message by using pointer arithmetic. As the type of the presence
97 // set (bitmap) varies with the number of fields in the protobuf, we
98 // can not have a struct type containing the array and the
99 // RaceDetectHookData. instead the RaceDetectHookData is placed
100 // immediately before the bitmap array, and we find it by walking
101 // backwards in the struct.
102 //
103 // This method is only called from the race-detect version of the code,
104 // so RaceDetectHookData is never an empty struct.
105 func (p presence) toRaceDetectData() *RaceDetectHookData {
106 var template struct {
107 d RaceDetectHookData
108 a [1]uint32
109 }
110 o := (uintptr(unsafe.Pointer(&template.a)) - uintptr(unsafe.Pointer(&template.d)))
111 return (*RaceDetectHookData)(unsafe.Pointer(uintptr(p.P) - o))
112 }
113 114 func atomicLoadShadowPresence(p **[]byte) *[]byte {
115 return (*[]byte)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
116 }
117 func atomicStoreShadowPresence(p **[]byte, v *[]byte) {
118 atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(p)), nil, unsafe.Pointer(v))
119 }
120 121 // findPointerToRaceDetectData finds the preceding RaceDetectHookData
122 // in a message by using pointer arithmetic. For the methods called
123 // directy from generated code, we don't have a pointer to the
124 // beginning of the presence set, but a pointer inside the array. As
125 // we know the index of the bit we're manipulating (num), we can
126 // calculate which element of the array ptr is pointing to. With that
127 // information we find the preceding RaceDetectHookData and can
128 // manipulate the shadow bitmap.
129 //
130 // This method is only called from the race-detect version of the
131 // code, so RaceDetectHookData is never an empty struct.
132 func findPointerToRaceDetectData(ptr *uint32, num uint32) *RaceDetectHookData {
133 var template struct {
134 d RaceDetectHookData
135 a [1]uint32
136 }
137 o := (uintptr(unsafe.Pointer(&template.a)) - uintptr(unsafe.Pointer(&template.d))) + uintptr(num/32)*unsafe.Sizeof(uint32(0))
138 return (*RaceDetectHookData)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) - o))
139 }
140