api_export_opaque.go

  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
  5package impl
  6
  7import (
  8	"strconv"
  9	"sync/atomic"
 10	"unsafe"
 11
 12	"google.golang.org/protobuf/reflect/protoreflect"
 13)
 14
 15func (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)
 24func (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.
 35func (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.
 50func (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.
 60func (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
 75func interfaceToPointer(i *any) pointer {
 76	return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
 77}
 78
 79func (p pointer) atomicGetPointer() pointer {
 80	return pointer{p: atomic.LoadPointer((*unsafe.Pointer)(p.p))}
 81}
 82
 83func (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.
 91func (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.
 99func (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.
105func (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.
114func (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.
122func (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}