api_export_opaque.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  	"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