-
Notifications
You must be signed in to change notification settings - Fork 28
/
structtype.go
309 lines (266 loc) · 9.23 KB
/
structtype.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
302
303
304
305
306
307
308
309
// Copyright 2019 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package clone
import (
"crypto/elliptic"
"fmt"
"reflect"
"sync"
"sync/atomic"
"time"
"unsafe"
)
type structType struct {
ZeroFields []structFieldSize
PointerFields []structFieldType
fn Func
}
type structFieldSize struct {
Offset uintptr // The offset from the beginning of the struct.
Size uintptr // The size of the field.
}
type structFieldType struct {
Offset uintptr // The offset from the beginning of the struct.
Index int // The index of the field.
}
var zeroStructType = structType{}
func init() {
// Some well-known scalar-like structs.
MarkAsScalar(reflect.TypeOf(time.Time{}))
MarkAsScalar(reflect.TypeOf(reflect.Value{}))
// Special case for elliptic.Curve which is used by TLS ECC certificate.
// Package crypto/tls uses elliptic.Curve as enum values
// so that they should be treated as opaque pointers.
//
// As elliptic.Curve is an interface, it can be *elliptic.CurveParam or elliptic.p256Curve.
MarkAsOpaquePointer(reflect.TypeOf(&elliptic.CurveParams{}))
curves := []elliptic.Curve{
elliptic.P224(),
elliptic.P256(),
elliptic.P384(),
elliptic.P521(),
}
for _, curve := range curves {
MarkAsOpaquePointer(reflect.ValueOf(curve).Type())
}
// Special case for reflect.Type (actually *reflect.rtype):
// The *reflect.rtype should not be copied as it is immutable and
// may point to a variable that actual type is not reflect.rtype,
// e.g. *reflect.arrayType or *reflect.chanType.
MarkAsOpaquePointer(reflect.TypeOf(reflect.TypeOf(0)))
// Some well-known no-copy structs.
//
// Almost all structs defined in package "sync" and "sync/atomic" are set
// except `sync.Once` which can be safely cloned with a correct done value.
SetCustomFunc(reflect.TypeOf(sync.Mutex{}), emptyCloneFunc)
SetCustomFunc(reflect.TypeOf(sync.RWMutex{}), emptyCloneFunc)
SetCustomFunc(reflect.TypeOf(sync.WaitGroup{}), emptyCloneFunc)
SetCustomFunc(reflect.TypeOf(sync.Cond{}), func(allocator *Allocator, old, new reflect.Value) {
// Copy the New func from old value.
oldL := old.FieldByName("L")
newL := allocator.Clone(oldL)
new.FieldByName("L").Set(newL)
})
SetCustomFunc(reflect.TypeOf(sync.Pool{}), func(allocator *Allocator, old, new reflect.Value) {
// Copy the New func from old value.
oldFn := old.FieldByName("New")
newFn := allocator.Clone(oldFn)
new.FieldByName("New").Set(newFn)
})
SetCustomFunc(reflect.TypeOf(sync.Map{}), func(allocator *Allocator, old, new reflect.Value) {
if !old.CanAddr() {
return
}
// Clone all values inside sync.Map.
oldMap := old.Addr().Interface().(*sync.Map)
newMap := new.Addr().Interface().(*sync.Map)
oldMap.Range(func(key, value interface{}) bool {
k := clone(allocator, key)
v := clone(allocator, value)
newMap.Store(k, v)
return true
})
})
SetCustomFunc(reflect.TypeOf(atomic.Value{}), func(allocator *Allocator, old, new reflect.Value) {
if !old.CanAddr() {
return
}
// Clone value inside atomic.Value.
oldValue := old.Addr().Interface().(*atomic.Value)
newValue := new.Addr().Interface().(*atomic.Value)
v := oldValue.Load()
cloned := clone(allocator, v)
newValue.Store(cloned)
})
}
// MarkAsScalar marks t as a scalar type in heap allocator,
// so that all clone methods will copy t by value.
// If t is not struct or pointer to struct, MarkAsScalar ignores t.
//
// In the most cases, it's not necessary to call it explicitly.
// If a struct type contains scalar type fields only, the struct will be marked as scalar automatically.
//
// Here is a list of types marked as scalar by default:
// - time.Time
// - reflect.Value
func MarkAsScalar(t reflect.Type) {
defaultAllocator.MarkAsScalar(t)
}
// MarkAsOpaquePointer marks t as an opaque pointer in heap allocator,
// so that all clone methods will copy t by value.
// If t is not a pointer, MarkAsOpaquePointer ignores t.
//
// Here is a list of types marked as opaque pointers by default:
// - `elliptic.Curve`, which is `*elliptic.CurveParam` or `elliptic.p256Curve`;
// - `reflect.Type`, which is `*reflect.rtype` defined in `runtime`.
func MarkAsOpaquePointer(t reflect.Type) {
defaultAllocator.MarkAsOpaquePointer(t)
}
// Func is a custom func to clone value from old to new.
// The new is a zero value
// which `new.CanSet()` and `new.CanAddr()` is guaranteed to be true.
//
// Func must update the new to return result.
type Func func(allocator *Allocator, old, new reflect.Value)
// emptyCloneFunc is used to disable shadow copy.
// It's useful when cloning sync.Mutex as cloned value must be a zero value.
func emptyCloneFunc(allocator *Allocator, old, new reflect.Value) {}
// SetCustomFunc sets a custom clone function for type t in heap allocator.
// If t is not struct or pointer to struct, SetCustomFunc ignores t.
//
// If fn is nil, remove the custom clone function for type t.
func SetCustomFunc(t reflect.Type, fn Func) {
defaultAllocator.SetCustomFunc(t, fn)
}
// Init creates a new value of src.Type() and shadow copies all content from src.
// If noCustomFunc is set to true, custom clone function will be ignored.
//
// Init returns true if the value is cloned by a custom func.
// Caller should skip cloning struct fields in depth.
func (st *structType) Init(allocator *Allocator, src, nv reflect.Value, noCustomFunc bool) (done bool) {
dst := nv.Elem()
if !noCustomFunc && st.fn != nil {
if !src.CanInterface() {
src = forceClearROFlag(src)
}
st.fn(allocator, src, dst)
done = true
return
}
ptr := unsafe.Pointer(nv.Pointer())
shadowCopy(src, ptr)
done = len(st.PointerFields) == 0
return
}
func (st *structType) CanShadowCopy() bool {
return len(st.PointerFields) == 0 && st.fn == nil
}
// IsScalar returns true if k should be considered as a scalar type.
//
// For the sake of performance, string is considered as a scalar type unless arena is enabled.
// If we need to deep copy string value in some cases, we can create a new allocator with custom isScalar function
// in which we can return false when k is reflect.String.
//
// // Create a new allocator which treats string as non-scalar type.
// allocator := NewAllocator(nil, &AllocatorMethods{
// IsScalar: func(k reflect.Kind) bool {
// return k != reflect.String && IsScalar(k)
// },
// })
func IsScalar(k reflect.Kind) bool {
switch k {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64,
reflect.Complex64, reflect.Complex128,
reflect.Func,
reflect.UnsafePointer,
reflect.Invalid:
return true
case reflect.String:
// If arena is not enabled, string can be copied as scalar safely
// as it's immutable by design.
return !arenaIsEnabled
}
return false
}
func copyScalarValue(src reflect.Value) reflect.Value {
if src.CanInterface() {
return src
}
dst := newScalarValue(src)
return dst.Convert(src.Type())
}
func newScalarValue(src reflect.Value) reflect.Value {
// src is an unexported field value. Copy its value.
switch src.Kind() {
case reflect.Bool:
return reflect.ValueOf(src.Bool())
case reflect.Int:
return reflect.ValueOf(int(src.Int()))
case reflect.Int8:
return reflect.ValueOf(int8(src.Int()))
case reflect.Int16:
return reflect.ValueOf(int16(src.Int()))
case reflect.Int32:
return reflect.ValueOf(int32(src.Int()))
case reflect.Int64:
return reflect.ValueOf(src.Int())
case reflect.Uint:
return reflect.ValueOf(uint(src.Uint()))
case reflect.Uint8:
return reflect.ValueOf(uint8(src.Uint()))
case reflect.Uint16:
return reflect.ValueOf(uint16(src.Uint()))
case reflect.Uint32:
return reflect.ValueOf(uint32(src.Uint()))
case reflect.Uint64:
return reflect.ValueOf(src.Uint())
case reflect.Uintptr:
return reflect.ValueOf(uintptr(src.Uint()))
case reflect.Float32:
return reflect.ValueOf(float32(src.Float()))
case reflect.Float64:
return reflect.ValueOf(src.Float())
case reflect.Complex64:
return reflect.ValueOf(complex64(src.Complex()))
case reflect.Complex128:
return reflect.ValueOf(src.Complex())
case reflect.String:
return reflect.ValueOf(src.String())
case reflect.Func:
t := src.Type()
if src.IsNil() {
return reflect.Zero(t)
}
// Don't use this trick unless we have no choice.
return forceClearROFlag(src)
case reflect.UnsafePointer:
return reflect.ValueOf(unsafe.Pointer(src.Pointer()))
}
panic(fmt.Errorf("go-clone: <bug> impossible type `%v` when cloning private field", src.Type()))
}
var typeOfInterface = reflect.TypeOf((*interface{})(nil)).Elem()
// forceClearROFlag clears all RO flags in v to make v accessible.
// It's a hack based on the fact that InterfaceData is always available on RO data.
// This hack can be broken in any Go version.
// Don't use it unless we have no choice, e.g. copying func in some edge cases.
func forceClearROFlag(v reflect.Value) reflect.Value {
var i interface{}
indirect := 0
// Save flagAddr.
for v.CanAddr() {
v = v.Addr()
indirect++
}
v = v.Convert(typeOfInterface)
nv := reflect.ValueOf(&i)
*(*interfaceData)(unsafe.Pointer(nv.Pointer())) = parseReflectValue(v)
cleared := nv.Elem().Elem()
for indirect > 0 {
cleared = cleared.Elem()
indirect--
}
return cleared
}