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 )