package reflect2 import ( "github.com/modern-go/concurrent" "reflect" "unsafe" ) type Type interface { Kind() reflect.Kind // New return pointer to data of this type New() interface{} // UnsafeNew return the allocated space pointed by unsafe.Pointer UnsafeNew() unsafe.Pointer // PackEFace cast a unsafe pointer to object represented pointer PackEFace(ptr unsafe.Pointer) interface{} // Indirect dereference object represented pointer to this type Indirect(obj interface{}) interface{} // UnsafeIndirect dereference pointer to this type UnsafeIndirect(ptr unsafe.Pointer) interface{} // Type1 returns reflect.Type Type1() reflect.Type Implements(thatType Type) bool String() string RType() uintptr // interface{} of this type has pointer like behavior LikePtr() bool IsNullable() bool IsNil(obj interface{}) bool UnsafeIsNil(ptr unsafe.Pointer) bool Set(obj interface{}, val interface{}) UnsafeSet(ptr unsafe.Pointer, val unsafe.Pointer) AssignableTo(anotherType Type) bool } type ListType interface { Type Elem() Type SetIndex(obj interface{}, index int, elem interface{}) UnsafeSetIndex(obj unsafe.Pointer, index int, elem unsafe.Pointer) GetIndex(obj interface{}, index int) interface{} UnsafeGetIndex(obj unsafe.Pointer, index int) unsafe.Pointer } type ArrayType interface { ListType Len() int } type SliceType interface { ListType MakeSlice(length int, cap int) interface{} UnsafeMakeSlice(length int, cap int) unsafe.Pointer Grow(obj interface{}, newLength int) UnsafeGrow(ptr unsafe.Pointer, newLength int) Append(obj interface{}, elem interface{}) UnsafeAppend(obj unsafe.Pointer, elem unsafe.Pointer) LengthOf(obj interface{}) int UnsafeLengthOf(ptr unsafe.Pointer) int SetNil(obj interface{}) UnsafeSetNil(ptr unsafe.Pointer) Cap(obj interface{}) int UnsafeCap(ptr unsafe.Pointer) int } type StructType interface { Type NumField() int Field(i int) StructField FieldByName(name string) StructField FieldByIndex(index []int) StructField FieldByNameFunc(match func(string) bool) StructField } type StructField interface { Offset() uintptr Name() string PkgPath() string Type() Type Tag() reflect.StructTag Index() []int Anonymous() bool Set(obj interface{}, value interface{}) UnsafeSet(obj unsafe.Pointer, value unsafe.Pointer) Get(obj interface{}) interface{} UnsafeGet(obj unsafe.Pointer) unsafe.Pointer } type MapType interface { Type Key() Type Elem() Type MakeMap(cap int) interface{} UnsafeMakeMap(cap int) unsafe.Pointer SetIndex(obj interface{}, key interface{}, elem interface{}) UnsafeSetIndex(obj unsafe.Pointer, key unsafe.Pointer, elem unsafe.Pointer) TryGetIndex(obj interface{}, key interface{}) (interface{}, bool) GetIndex(obj interface{}, key interface{}) interface{} UnsafeGetIndex(obj unsafe.Pointer, key unsafe.Pointer) unsafe.Pointer Iterate(obj interface{}) MapIterator UnsafeIterate(obj unsafe.Pointer) MapIterator } type MapIterator interface { HasNext() bool Next() (key interface{}, elem interface{}) UnsafeNext() (key unsafe.Pointer, elem unsafe.Pointer) } type PtrType interface { Type Elem() Type } type InterfaceType interface { NumMethod() int } type Config struct { UseSafeImplementation bool } type API interface { TypeOf(obj interface{}) Type Type2(type1 reflect.Type) Type } var ConfigUnsafe = Config{UseSafeImplementation: false}.Froze() var ConfigSafe = Config{UseSafeImplementation: true}.Froze() type frozenConfig struct { useSafeImplementation bool cache *concurrent.Map } func (cfg Config) Froze() *frozenConfig { return &frozenConfig{ useSafeImplementation: cfg.UseSafeImplementation, cache: concurrent.NewMap(), } } func (cfg *frozenConfig) TypeOf(obj interface{}) Type { cacheKey := uintptr(unpackEFace(obj).rtype) typeObj, found := cfg.cache.Load(cacheKey) if found { return typeObj.(Type) } return cfg.Type2(reflect.TypeOf(obj)) } func (cfg *frozenConfig) Type2(type1 reflect.Type) Type { if type1 == nil { return nil } cacheKey := uintptr(unpackEFace(type1).data) typeObj, found := cfg.cache.Load(cacheKey) if found { return typeObj.(Type) } type2 := cfg.wrapType(type1) cfg.cache.Store(cacheKey, type2) return type2 } func (cfg *frozenConfig) wrapType(type1 reflect.Type) Type { safeType := safeType{Type: type1, cfg: cfg} switch type1.Kind() { case reflect.Struct: if cfg.useSafeImplementation { return &safeStructType{safeType} } return newUnsafeStructType(cfg, type1) case reflect.Array: if cfg.useSafeImplementation { return &safeSliceType{safeType} } return newUnsafeArrayType(cfg, type1) case reflect.Slice: if cfg.useSafeImplementation { return &safeSliceType{safeType} } return newUnsafeSliceType(cfg, type1) case reflect.Map: if cfg.useSafeImplementation { return &safeMapType{safeType} } return newUnsafeMapType(cfg, type1) case reflect.Ptr, reflect.Chan, reflect.Func: if cfg.useSafeImplementation { return &safeMapType{safeType} } return newUnsafePtrType(cfg, type1) case reflect.Interface: if cfg.useSafeImplementation { return &safeMapType{safeType} } if type1.NumMethod() == 0 { return newUnsafeEFaceType(cfg, type1) } return newUnsafeIFaceType(cfg, type1) default: if cfg.useSafeImplementation { return &safeType } return newUnsafeType(cfg, type1) } } func TypeOf(obj interface{}) Type { return ConfigUnsafe.TypeOf(obj) } func TypeOfPtr(obj interface{}) PtrType { return TypeOf(obj).(PtrType) } func Type2(type1 reflect.Type) Type { if type1 == nil { return nil } return ConfigUnsafe.Type2(type1) } func PtrTo(typ Type) Type { return Type2(reflect.PtrTo(typ.Type1())) } func PtrOf(obj interface{}) unsafe.Pointer { return unpackEFace(obj).data } func RTypeOf(obj interface{}) uintptr { return uintptr(unpackEFace(obj).rtype) } func IsNil(obj interface{}) bool { if obj == nil { return true } return unpackEFace(obj).data == nil } func IsNullable(kind reflect.Kind) bool { switch kind { case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func, reflect.Slice, reflect.Interface: return true } return false } func likePtrKind(kind reflect.Kind) bool { switch kind { case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func: return true } return false } func likePtrType(typ reflect.Type) bool { if likePtrKind(typ.Kind()) { return true } if typ.Kind() == reflect.Struct { if typ.NumField() != 1 { return false } return likePtrType(typ.Field(0).Type) } if typ.Kind() == reflect.Array { if typ.Len() != 1 { return false } return likePtrType(typ.Elem()) } return false } // NoEscape hides a pointer from escape analysis. noescape is // the identity function but escape analysis doesn't think the // output depends on the input. noescape is inlined and currently // compiles down to zero instructions. // USE CAREFULLY! //go:nosplit func NoEscape(p unsafe.Pointer) unsafe.Pointer { x := uintptr(p) return unsafe.Pointer(x ^ 0) } func UnsafeCastString(str string) []byte { stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str)) sliceHeader := &reflect.SliceHeader{ Data: stringHeader.Data, Cap: stringHeader.Len, Len: stringHeader.Len, } return *(*[]byte)(unsafe.Pointer(sliceHeader)) }