-
Notifications
You must be signed in to change notification settings - Fork 7
/
field.go
136 lines (123 loc) · 3.47 KB
/
field.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
package xunsafe
import (
"reflect"
"unsafe"
)
//ptrKind defines generic ptr handling kind
type ptrKind byte
const (
ptrKindEmptyInterface = ptrKind(iota)
ptrKindInterface
ptrKindMethodInterface
)
//Field represents a field
type Field struct {
Name string
reflect.Type
Tag reflect.StructTag
Anonymous bool
Offset uintptr
Index uint16
kind reflect.Kind
rtype *rtype
rtypPtr *rtype
ptrKind
}
//Pointer return field pointer (structPtr + field.Offset)
func (f *Field) Pointer(structPtr unsafe.Pointer) unsafe.Pointer {
return unsafe.Pointer(uintptr(structPtr) + f.Offset)
}
//IsNil returns nil if structPtr is nil or field value is nil
func (f *Field) IsNil(structPtr unsafe.Pointer) bool {
if structPtr == nil {
return true
}
return *(*unsafe.Pointer)(f.Pointer(structPtr)) == nil
}
//ValuePointer return pointer to T, if value is as *T, this method would dereference it
func (f *Field) ValuePointer(structPtr unsafe.Pointer) unsafe.Pointer {
ret := unsafe.Pointer(uintptr(structPtr) + f.Offset)
if f.kind == reflect.Ptr {
ret = DerefPointer(ret)
}
return ret
}
//SafePointer returns field pointer, if field pointer is a pointer this method initialises that pointer
func (f *Field) SafePointer(structPtr unsafe.Pointer, target reflect.Type) unsafe.Pointer {
fieldPtr := f.Pointer(structPtr)
if f.kind == reflect.Ptr {
itemPtr := (*unsafe.Pointer)(fieldPtr)
if *itemPtr != nil {
return fieldPtr
}
EnsureAddressPointer(fieldPtr, f.Type)
}
return fieldPtr
}
//EnsurePointer initialises field type pointer if needed, and return filed type value pointer rather field pointer.
//for example if field is of T type this method returns *T, in case field is of *T, this method
//also return *T, if you need always field pointer use Field.Pointer method
func (f *Field) EnsurePointer(structPtr unsafe.Pointer) unsafe.Pointer {
addr := f.Pointer(structPtr)
ptr := (*unsafe.Pointer)(addr)
if f.kind != reflect.Ptr {
return addr
}
if *ptr == nil {
var newPointer unsafe.Pointer
*ptr = unsafe.Pointer(&newPointer)
}
return *ptr
}
func (f *Field) initType() {
fType := f.Type
f.ptrKind = ptrKindEmptyInterface
if f.Type.Kind() == reflect.Interface {
if f.Type.NumMethod() == 0 {
f.ptrKind = ptrKindInterface
} else {
f.ptrKind = ptrKindMethodInterface
}
}
ptrValue := reflect.New(fType)
ptrElemValue := ptrValue.Elem()
valPtr := ptrValue.Interface()
val := ptrElemValue.Interface()
f.rtypPtr = ((*emptyInterface)(unsafe.Pointer(&valPtr))).typ
f.rtype = ((*emptyInterface)(unsafe.Pointer(&val))).typ
}
//NewField creates a new filed
func NewField(field reflect.StructField) *Field {
fieldType := field.Type
f := &Field{
Name: field.Name,
Type: fieldType,
Tag: field.Tag,
Anonymous: field.Anonymous,
Offset: field.Offset,
kind: fieldType.Kind(),
}
if len(field.Index) > 0 {
f.Index = uint16(field.Index[0])
}
f.initType()
return f
}
//FieldByIndex creates a field for supplied struct type and field indexAddr
func FieldByIndex(structType reflect.Type, index int) *Field {
return NewField(structType.Field(index))
}
//FieldByName creates a field for supplied struct type and field name
func FieldByName(structType reflect.Type, name string) *Field {
switch structType.Kind() {
case reflect.Ptr:
return FieldByName(structType.Elem(), name)
case reflect.Slice:
return FieldByName(structType.Elem(), name)
}
structField, ok := structType.FieldByName(name)
if !ok {
return nil
}
return NewField(structField)
}