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