-
Notifications
You must be signed in to change notification settings - Fork 3
/
FastReflect.go
264 lines (230 loc) · 7.27 KB
/
FastReflect.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
package fastreflect
import (
"unsafe"
)
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
// tflag is used by an rtype to signal what extra type information is
// available in the memory directly following the rtype value.
//
// tflag values must be kept in sync with copies in:
// cmd/compile/internal/gc/reflect.go
// cmd/link/internal/ld/decodesym.go
// runtime/type.go
type tflag uint8
type nameOff int32 // offset to a name
type typeOff int32 // offset to an *rtype
// rtype is the common implementation of most values.
// It is embedded in other struct types.
//
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type 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
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
equal func(unsafe.Pointer, unsafe.Pointer) bool
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
type flag uintptr
const flagMethod flag = 1 << 9 // @ go/srcreflect/value.go
const (
invalidKind uint8 = iota
boolKind
intKind
int8Kind
int16Kind
int32Kind
int64Kind
uintKind
uint8Kind
uint16Kind
uint32Kind
uint64Kind
uintptrKind
float32Kind
float64Kind
complex64Kind
complex128Kind
arrayKind
chanKind
funcKind
interfaceKind
mapKind
ptrKind
sliceKind
stringKind
structKind
unsafePointerKind
)
// structType represents a struct type.
type structType struct {
rtype
pkgPath name
fields []structField // sorted by offset
}
// sliceType represents a slice type.
type sliceType struct {
rtype
elem *rtype // slice element type
}
// name is an encoded type name with optional extra data.
//
// The first byte is a bit field containing:
//
// 1<<0 the name is exported
// 1<<1 tag data follows the name
// 1<<2 pkgPath nameOff follows the name and tag
//
// The next two bytes are the data length:
//
// l := uint16(data[1])<<8 | uint16(data[2])
//
// Bytes [3:3+l] are the string data.
//
// If tag data follows then bytes 3+l and 3+l+1 are the tag length,
// with the data following.
//
// If the import path follows, then 4 bytes at the end of
// the data form a nameOff. The import path is only set for concrete
// methods that are defined in a different package than their type.
//
// If a name starts with "*", then the exported bit represents
// whether the pointed to type is exported.
type name struct {
bytes *byte
}
type UnsafeString struct {
Data unsafe.Pointer
Len int
}
// Slice is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
//
// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the
// data it references will not be garbage collected.
type UnsafeSlice struct {
Data unsafe.Pointer
Len int
Cap int
}
func (n name) name() (s string) {
if n.bytes == nil {
return
}
b := (*[4]byte)(unsafe.Pointer(n.bytes))
hdr := (*UnsafeString)(unsafe.Pointer(&s))
hdr.Data = unsafe.Pointer(&b[3])
hdr.Len = int(b[1])<<8 | int(b[2])
return s
}
// Struct field
type structField struct {
name name // name is always non-empty
typ *rtype // type of field
offsetEmbed uintptr // byte offset of field<<1 | isEmbedded
}
func (f *structField) offset() uintptr {
return f.offsetEmbed >> 1
}
// add returns p+x.
//
// The whySafe string is ignored, so that the function still inlines
// as efficiently as p+x, but all call sites should use the string to
// record why the addition is safe, which is to say why the addition
// does not cause x to advance to the very end of p's allocation
// and therefore point incorrectly at the next block in memory.
func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
return unsafe.Pointer(uintptr(p) + x)
}
// arrayAt returns the i-th element of p,
// an array whose elements are eltSize bytes wide.
// The array pointed at by p must have at least i+1 elements:
// it is invalid (but impossible to check here) to pass i >= len,
// because then the result will point outside the array.
// whySafe must explain why i < len. (Passing "i < len" is fine;
// the benefit is to surface this assumption at the call site.)
func arrayAt(p unsafe.Pointer, i int, eltSize uintptr, whySafe string) unsafe.Pointer {
return add(p, uintptr(i)*eltSize, "i < len")
}
// StructFieldByName returns the struct field with the given name.
// It returns the nil if no field was found.
// It panics if input data Kind is not struct.
func StructFieldByName(structData interface{}, name string) interface{} {
// unpack Struct
e := (*emptyInterface)(unsafe.Pointer(&structData))
startPointer := e.word
// check flag
valueMetadataFlag := e.typ.kind
if valueMetadataFlag == 0 {
panic("invalied reflect.Value.Type")
}
// please input a struct type data
if valueMetadataFlag != structKind {
panic("fast-reflect: Field of non-struct type ")
}
// pick up target field
var targetStructField *structField
tt := (*structType)(unsafe.Pointer(e.typ))
for i := range tt.fields {
tf := &tt.fields[i]
if tf.name.name() == name {
targetStructField = tf
}
}
if targetStructField == nil {
return nil
}
// repack target field
var packed interface{}
pe := (*emptyInterface)(unsafe.Pointer(&packed))
pe.typ = targetStructField.typ
pe.word = add(startPointer, targetStructField.offset(), "same as non-reflect &v.field")
return packed
}
// Get all elements from an interface (slice type)
// SliceAllElements returns the all elements from an interface (slice type).
// It returns the nil if no field was found.
// It panics if input data Kind is not slice.
func SliceAllElements(sliceData interface{}) []interface{} {
// unpack Struct
e := (*emptyInterface)(unsafe.Pointer(&sliceData))
// check flag
valueMetadataFlag := e.typ.kind
if valueMetadataFlag == 0 {
panic("invalied reflect.Value.Type")
}
// please input a slice type data
if valueMetadataFlag != sliceKind {
panic("fast-reflect: non-slice type ")
}
s := (*UnsafeSlice)(e.word)
// check slice len and init a container for return
if s.Len < 1 { // an empty slice
return nil
}
// pickup and return
allElements := make([]interface{}, s.Len)
tt := (*sliceType)(unsafe.Pointer(e.typ))
typ := tt.elem
for i := 0; i < s.Len; i++ {
element := arrayAt(s.Data, i, typ.size, "i < s.Len")
var packed interface{}
pe := (*emptyInterface)(unsafe.Pointer(&packed))
pe.typ = typ
pe.word = element
allElements[i] = packed
}
return allElements
}