presence.go raw

   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