github.com/hedzr/evendeep@v0.4.8/internal/tool/unsafe.go (about)

     1  //go:build !appengine && !disableunsafe
     2  // +build !appengine,!disableunsafe
     3  
     4  package tool
     5  
     6  import (
     7  	"reflect"
     8  	"unsafe"
     9  )
    10  
    11  const (
    12  	// UnsafeDisabled is a build-time constant which specifies whether or
    13  	// not access to the unsafe package is available.
    14  	UnsafeDisabled = false
    15  
    16  	// ptrSize is the size of a pointer on the current arch.
    17  	ptrSize = unsafe.Sizeof((*byte)(nil))
    18  )
    19  
    20  //nolint:gochecknoglobals //i do
    21  var (
    22  	// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
    23  	// internal reflect.Value fields.  These values are valid before golang
    24  	// commit ecccf07e7f9d which changed the format.  The are also valid
    25  	// after commit 82f48826c6c7 which changed the format again to mirror
    26  	// the original format.  Code in the init function updates these offsets
    27  	// as necessary.
    28  	offsetPtr    = ptrSize
    29  	offsetScalar = uintptr(0)
    30  	offsetFlag   = ptrSize * 2
    31  
    32  	// flagKindWidth and flagKindShift indicate various bits that the
    33  	// reflect package uses internally to track kind information.
    34  	//
    35  	// flagRO indicates whether or not the value field of a reflect.Value is
    36  	// read-only.
    37  	//
    38  	// flagIndir indicates whether the value field of a reflect.Value is
    39  	// the actual data or a pointer to the data.
    40  	//
    41  	// These values are valid before golang commit 90a7c3c86944 which
    42  	// changed their positions.  Code in the init function updates these
    43  	// flags as necessary.
    44  	flagKindWidth = uintptr(5)                 //nolint:gomnd //i do
    45  	flagKindShift = uintptr(flagKindWidth - 1) //nolint:unconvert //i do
    46  	flagRO        = uintptr(1 << 0)            //nolint:unused //i do
    47  	flagIndir     = uintptr(1 << 1)
    48  )
    49  
    50  func init() { //nolint:gochecknoinits //no need
    51  	// Older versions of reflect.Value stored small integers directly in the
    52  	// ptr field (which is named val in the older versions).  Versions
    53  	// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
    54  	// scalar for this purpose which unfortunately came before the flag
    55  	// field, so the offset of the flag field is different for those
    56  	// versions.
    57  	//
    58  	// This code constructs a new reflect.Value from a known small integer
    59  	// and checks if the size of the reflect.Value struct indicates it has
    60  	// the scalar field. When it does, the offsets are updated accordingly.
    61  	vv := reflect.ValueOf(0xf00)            //nolint:gomnd //no need
    62  	if unsafe.Sizeof(vv) == (ptrSize * 4) { //nolint:gomnd //no need
    63  		offsetScalar = ptrSize * 2 //nolint:gomnd //no need
    64  		offsetFlag = ptrSize * 3   //nolint:gomnd //no need
    65  	}
    66  
    67  	// Commit 90a7c3c86944 changed the flag positions such that the low
    68  	// order bits are the kind.  This code extracts the kind from the flags
    69  	// field and ensures it's the correct type.  When it's not, the flag
    70  	// order has been changed to the newer format, so the flags are updated
    71  	// accordingly.
    72  	upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
    73  	upfv := *(*uintptr)(upf)
    74  	flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
    75  	if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
    76  		flagKindShift = 0
    77  		flagRO = 1 << 5    //nolint:gomnd //no need
    78  		flagIndir = 1 << 6 //nolint:gomnd //no need
    79  
    80  		// Commit adf9b30e5594 modified the flags to separate the
    81  		// flagRO flag into two bits which specifies whether or not the
    82  		// field is embedded.  This causes flagIndir to move over a bit
    83  		// and means that flagRO is the combination of either of the
    84  		// original flagRO bit and the new bit.
    85  		//
    86  		// This code detects the change by extracting what used to be
    87  		// the indirect bit to ensure it's set.  When it's not, the flag
    88  		// order has been changed to the newer format, so the flags are
    89  		// updated accordingly.
    90  		if upfv&flagIndir == 0 {
    91  			flagRO = 3 << 5    //nolint:gomnd //no need
    92  			flagIndir = 1 << 7 //nolint:gomnd //no need
    93  		}
    94  	}
    95  }
    96  
    97  // UnsafeReflectValue converts the passed reflect.Value into a one that bypasses
    98  // the typical safety restrictions preventing access to unaddressable and
    99  // unexported data.  It works by digging the raw pointer to the underlying
   100  // value out of the protected value and generating a new unprotected (unsafe)
   101  // reflect.Value to it.
   102  //
   103  // This allows us to check for implementations of the Stringer and error
   104  // interfaces to be used for pretty printing ordinarily unaddressable and
   105  // inaccessible values such as unexported struct fields.
   106  func UnsafeReflectValue(v reflect.Value) (rv reflect.Value) {
   107  	indirects := 1
   108  	vt := v.Type()
   109  	upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
   110  	rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
   111  	if rvf&flagIndir != 0 {
   112  		vt = reflect.PtrTo(v.Type())
   113  		indirects++
   114  	} else if offsetScalar != 0 {
   115  		// The value is in the scalar field when it's not one of the
   116  		// reference types.
   117  		switch vt.Kind() { //nolint:exhaustive //no need
   118  		case reflect.Uintptr:
   119  		case reflect.Chan:
   120  		case reflect.Func:
   121  		case reflect.Map:
   122  		case reflect.Ptr:
   123  		case reflect.UnsafePointer:
   124  		default:
   125  			upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
   126  				offsetScalar)
   127  		}
   128  	}
   129  
   130  	pv := reflect.NewAt(vt, upv)
   131  	rv = pv
   132  	for i := 0; i < indirects; i++ {
   133  		rv = rv.Elem()
   134  	}
   135  	return rv
   136  }