github.com/andeya/ameda@v1.5.3/value.go (about)

     1  package ameda
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"runtime"
     7  	"strings"
     8  	"unsafe"
     9  )
    10  
    11  // Value go underlying type data
    12  type Value struct {
    13  	flag
    14  	typPtr uintptr
    15  	ptr    unsafe.Pointer
    16  	_iPtr  unsafe.Pointer // avoid being GC
    17  }
    18  
    19  // ValueOf unpacks i to go underlying type data.
    20  func ValueOf(i interface{}) Value {
    21  	checkValueUsable()
    22  	return newT(unsafe.Pointer(&i))
    23  }
    24  
    25  // ValueFrom gets go underlying type data from reflect.Value.
    26  func ValueFrom(v reflect.Value) Value {
    27  	return ValueFrom2(&v)
    28  }
    29  
    30  // ValueFrom2 gets go underlying type data from *reflect.Value.
    31  func ValueFrom2(v *reflect.Value) Value {
    32  	checkValueUsable()
    33  	vv := newT(unsafe.Pointer(v))
    34  	if v.CanAddr() {
    35  		vv.flag |= flagAddr
    36  	}
    37  	return vv
    38  }
    39  
    40  //go:nocheckptr
    41  func newT(iPtr unsafe.Pointer) Value {
    42  	typPtr := *(*uintptr)(iPtr)
    43  	return Value{
    44  		typPtr: typPtr,
    45  		flag:   getFlag(typPtr),
    46  		ptr:    pointerElem(unsafe.Pointer(uintptr(iPtr) + ptrOffset)),
    47  		_iPtr:  iPtr,
    48  	}
    49  }
    50  
    51  // RuntimeTypeIDOf returns the underlying type ID in current runtime from interface object.
    52  // NOTE:
    53  //
    54  //	*A and A returns the different runtime type ID;
    55  //	It is 10 times performance of t.String().
    56  //
    57  //go:nocheckptr
    58  func RuntimeTypeIDOf(i interface{}) uintptr {
    59  	checkValueUsable()
    60  	iPtr := unsafe.Pointer(&i)
    61  	typPtr := *(*uintptr)(iPtr)
    62  	return typPtr
    63  }
    64  
    65  // RuntimeTypeID returns the underlying type ID in current runtime from reflect.Type.
    66  // NOTE:
    67  //
    68  //	*A and A returns the different runtime type ID;
    69  //	It is 10 times performance of t.String().
    70  //
    71  //go:nocheckptr
    72  func RuntimeTypeID(t reflect.Type) uintptr {
    73  	checkValueUsable()
    74  	typPtr := uintptrElem(uintptr(unsafe.Pointer(&t)) + ptrOffset)
    75  	return typPtr
    76  }
    77  
    78  // RuntimeTypeID gets the underlying type ID in current runtime.
    79  // NOTE:
    80  //
    81  //	*A and A gets the different runtime type ID;
    82  //	It is 10 times performance of reflect.TypeOf(i).String().
    83  //
    84  //go:nocheckptr
    85  func (v Value) RuntimeTypeID() uintptr {
    86  	return v.typPtr
    87  }
    88  
    89  // Kind gets the reflect.Kind fastly.
    90  func (v Value) Kind() reflect.Kind {
    91  	return reflect.Kind(v.flag & flagKindMask)
    92  }
    93  
    94  // CanAddr reports whether the value's address can be obtained with Addr.
    95  // Such values are called addressable. A value is addressable if it is
    96  // an element of a slice, an element of an addressable array,
    97  // a field of an addressable struct, or the result of dereferencing a pointer.
    98  func (v Value) CanAddr() bool {
    99  	return v.flag&flagAddr != 0
   100  }
   101  
   102  // Elem returns the Value that the interface i contains
   103  // or that the pointer i points to.
   104  //
   105  //go:nocheckptr
   106  func (v Value) Elem() Value {
   107  	k := v.Kind()
   108  	switch k {
   109  	default:
   110  		return v
   111  	case reflect.Interface:
   112  		return newT(v.ptr)
   113  	case reflect.Ptr:
   114  		flag2, typPtr2, has := typeUnderlying(v.flag, v.typPtr)
   115  		if has {
   116  			v.typPtr = typPtr2
   117  			v.flag = flag2
   118  			if v.Kind() == reflect.Ptr {
   119  				v.ptr = pointerElem(v.ptr)
   120  			}
   121  		}
   122  		return v
   123  	}
   124  }
   125  
   126  // UnderlyingElem returns the underlying Value that the interface i contains
   127  // or that the pointer i points to.
   128  //
   129  //go:nocheckptr
   130  func (v Value) UnderlyingElem() Value {
   131  	for kind := v.Kind(); kind == reflect.Ptr || kind == reflect.Interface; kind = v.Kind() {
   132  		v = v.Elem()
   133  	}
   134  	return v
   135  }
   136  
   137  // Pointer gets the pointer of i.
   138  // NOTE:
   139  //
   140  //	*T and T, gets diffrent pointer
   141  //
   142  //go:nocheckptr
   143  func (v Value) Pointer() uintptr {
   144  	switch v.Kind() {
   145  	case reflect.Invalid:
   146  		return 0
   147  	case reflect.Slice:
   148  		return uintptrElem(uintptr(v.ptr)) + sliceDataOffset
   149  	default:
   150  		return uintptr(v.ptr)
   151  	}
   152  }
   153  
   154  // IsNil reports whether its argument i is nil.
   155  //
   156  //go:nocheckptr
   157  func (v Value) IsNil() bool {
   158  	return unsafe.Pointer(v.Pointer()) == nil
   159  }
   160  
   161  // FuncForPC returns a *Func describing the function that contains the
   162  // given program counter address, or else nil.
   163  //
   164  // If pc represents multiple functions because of inlining, it returns
   165  // the a *Func describing the innermost function, but with an entry
   166  // of the outermost function.
   167  //
   168  // NOTE: Its kind must be a reflect.Func, otherwise it returns nil
   169  //
   170  //go:nocheckptr
   171  func (v Value) FuncForPC() *runtime.Func {
   172  	return runtime.FuncForPC(*(*uintptr)(v.ptr))
   173  }
   174  
   175  //go:nocheckptr
   176  func typeUnderlying(flagVal flag, typPtr uintptr) (flag, uintptr, bool) {
   177  	typPtr2 := uintptrElem(typPtr + elemOffset)
   178  	if unsafe.Pointer(typPtr2) == nil {
   179  		return flagVal, typPtr, false
   180  	}
   181  	tt := (*ptrType)(unsafe.Pointer(typPtr2))
   182  	flagVal2 := flagVal&flagRO | flagIndir | flagAddr
   183  	flagVal2 |= flag(tt.kind) & flagKindMask
   184  	return flagVal2, typPtr2, true
   185  }
   186  
   187  //go:nocheckptr
   188  func getFlag(typPtr uintptr) flag {
   189  	if unsafe.Pointer(typPtr) == nil {
   190  		return 0
   191  	}
   192  	return *(*flag)(unsafe.Pointer(typPtr + kindOffset))
   193  }
   194  
   195  //go:nocheckptr
   196  func uintptrElem(ptr uintptr) uintptr {
   197  	return *(*uintptr)(unsafe.Pointer(ptr))
   198  }
   199  
   200  //go:nocheckptr
   201  func pointerElem(p unsafe.Pointer) unsafe.Pointer {
   202  	return *(*unsafe.Pointer)(p)
   203  }
   204  
   205  var errValueUsable error
   206  
   207  func init() {
   208  	if errValueUsable == nil {
   209  		errValueUsable = checkGoVersion(runtime.Version())
   210  	}
   211  }
   212  
   213  func checkGoVersion(goVer string) error {
   214  	const s = "go1."
   215  	if strings.HasPrefix(goVer, s) || strings.Contains(goVer, " "+s) {
   216  		return nil
   217  	}
   218  	return fmt.Errorf("ameda Value: required go<2.0, but current version is '%s'", goVer)
   219  }
   220  
   221  func checkValueUsable() {
   222  	if errValueUsable != nil {
   223  		panic(errValueUsable)
   224  	}
   225  }
   226  
   227  var (
   228  	e         = emptyInterface{typ: new(rtype)}
   229  	ptrOffset = func() uintptr {
   230  		return unsafe.Offsetof(e.word)
   231  	}()
   232  	kindOffset = func() uintptr {
   233  		return unsafe.Offsetof(e.typ.kind)
   234  	}()
   235  	elemOffset = func() uintptr {
   236  		return unsafe.Offsetof(new(ptrType).elem)
   237  	}()
   238  	sliceDataOffset = func() uintptr {
   239  		return unsafe.Offsetof(new(reflect.SliceHeader).Data)
   240  	}()
   241  	// valueFlagOffset = func() uintptr {
   242  	// 	t := reflect.TypeOf(reflect.Value{})
   243  	// 	s, ok := t.FieldByName("flag")
   244  	// 	if !ok {
   245  	// 		errValueUsable = errors.New("not found reflect.Value.flag field")
   246  	// 		return 0
   247  	// 	}
   248  	// 	return s.Offset
   249  	// }()
   250  )
   251  
   252  // NOTE: The following definitions must be consistent with those in the standard package!!!
   253  
   254  const (
   255  	flagKindWidth        = 5 // there are 27 kinds
   256  	flagKindMask    flag = 1<<flagKindWidth - 1
   257  	flagStickyRO    flag = 1 << 5
   258  	flagEmbedRO     flag = 1 << 6
   259  	flagIndir       flag = 1 << 7
   260  	flagAddr        flag = 1 << 8
   261  	flagMethod      flag = 1 << 9
   262  	flagMethodShift      = 10
   263  	flagRO          flag = flagStickyRO | flagEmbedRO
   264  )
   265  
   266  type (
   267  	// reflectValue struct {
   268  	// 	typ *rtype
   269  	// 	ptr unsafe.Pointer
   270  	// 	flag
   271  	// }
   272  	emptyInterface struct {
   273  		typ  *rtype
   274  		word unsafe.Pointer
   275  	}
   276  	rtype struct {
   277  		size       uintptr
   278  		ptrdata    uintptr  // number of bytes in the type that can contain pointers
   279  		hash       uint32   // hash of type; avoids computation in hash tables
   280  		tflag      tflag    // extra type information flags
   281  		align      uint8    // alignment of variable with this type
   282  		fieldAlign uint8    // alignment of struct field with this type
   283  		kind       uint8    // enumeration for C
   284  		alg        *typeAlg // algorithm table
   285  		gcdata     *byte    // garbage collection data
   286  		str        nameOff  // string form
   287  		ptrToThis  typeOff  // type for pointer to this type, may be zero
   288  	}
   289  	ptrType struct {
   290  		rtype
   291  		elem *rtype // pointer element (pointed at) type
   292  	}
   293  	typeAlg struct {
   294  		hash  func(unsafe.Pointer, uintptr) uintptr
   295  		equal func(unsafe.Pointer, unsafe.Pointer) bool
   296  	}
   297  	nameOff int32 // offset to a name
   298  	typeOff int32 // offset to an *rtype
   299  	flag    uintptr
   300  	tflag   uint8
   301  )