github.com/Big-big-orange/protoreflect@v0.0.0-20240408141420-285cedfdf6a4/desc/descriptor_unsafe.go (about)

     1  //go:build !appengine && !gopherjs && !purego
     2  // +build !appengine,!gopherjs,!purego
     3  
     4  // NB: other environments where unsafe is unappropriate should use "purego" build tag
     5  // https://github.com/golang/go/issues/23172
     6  
     7  package desc
     8  
     9  import (
    10  	"sync/atomic"
    11  	"unsafe"
    12  )
    13  
    14  type jsonNameMap map[string]*FieldDescriptor // loaded/stored atomically via atomic+unsafe
    15  type memoizedDefault *interface{}            // loaded/stored atomically via atomic+unsafe
    16  
    17  // FindFieldByJSONName finds the field with the given JSON field name. If no such
    18  // field exists then nil is returned. Only regular fields are returned, not
    19  // extensions.
    20  func (md *MessageDescriptor) FindFieldByJSONName(jsonName string) *FieldDescriptor {
    21  	// NB: We don't want to eagerly index JSON names because many programs won't use it.
    22  	// So we want to do it lazily, but also make sure the result is thread-safe. So we
    23  	// atomically load/store the map as if it were a normal pointer. We don't use other
    24  	// mechanisms -- like sync.Mutex, sync.RWMutex, sync.Once, or atomic.Value -- to
    25  	// do this lazily because those types cannot be copied, and we'd rather not induce
    26  	// 'go vet' errors in programs that use descriptors and try to copy them.
    27  	// If multiple goroutines try to access the index at the same time, before it is
    28  	// built, they will all end up computing the index redundantly. Future reads of
    29  	// the index will use whatever was the "last one stored" by those racing goroutines.
    30  	// Since building the index is deterministic, this is fine: all indices computed
    31  	// will be the same.
    32  	addrOfJsonNames := (*unsafe.Pointer)(unsafe.Pointer(&md.jsonNames))
    33  	jsonNames := atomic.LoadPointer(addrOfJsonNames)
    34  	var index map[string]*FieldDescriptor
    35  	if jsonNames == nil {
    36  		// slow path: compute the index
    37  		index = map[string]*FieldDescriptor{}
    38  		for _, f := range md.fields {
    39  			jn := f.GetJSONName()
    40  			index[jn] = f
    41  		}
    42  		atomic.StorePointer(addrOfJsonNames, *(*unsafe.Pointer)(unsafe.Pointer(&index)))
    43  	} else {
    44  		*(*unsafe.Pointer)(unsafe.Pointer(&index)) = jsonNames
    45  	}
    46  	return index[jsonName]
    47  }
    48  
    49  func (fd *FieldDescriptor) getDefaultValue() interface{} {
    50  	addrOfDef := (*unsafe.Pointer)(unsafe.Pointer(&fd.def))
    51  	def := atomic.LoadPointer(addrOfDef)
    52  	if def != nil {
    53  		return *(*interface{})(def)
    54  	}
    55  	// slow path: compute the default, potentially involves decoding value
    56  	d := fd.determineDefault()
    57  	atomic.StorePointer(addrOfDef, (unsafe.Pointer(&d)))
    58  	return d
    59  }