• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

huandu / go-clone / 4282543940

27 Feb 2023 12:51PM UTC coverage: 89.483% (+0.4%) from 89.052%
4282543940

push

github

GitHub
Merge pull request #12 from huandu/feature-custom-func-call-allocator

32 of 32 new or added lines in 3 files covered. (100.0%)

1055 of 1179 relevant lines covered (89.48%)

0.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.53
/structtype.go
1
// Copyright 2019 Huan Du. All rights reserved.
2
// Licensed under the MIT license that can be found in the LICENSE file.
3

4
package clone
5

6
import (
7
        "crypto/elliptic"
8
        "fmt"
9
        "reflect"
10
        "sync"
11
        "sync/atomic"
12
        "time"
13
        "unsafe"
14
)
15

16
type structType struct {
17
        PointerFields []structFieldType
18
        fn            Func
19
}
20

21
type structFieldType struct {
22
        Offset uintptr // The offset from the beginning of the struct.
23
        Index  int     // The index of the field.
24
}
25

26
var (
27
        cachedStructTypes     sync.Map
28
        cachedPointerTypes    sync.Map
29
        cachedCustomFuncTypes sync.Map
30
)
31

32
var zeroStructType = structType{}
33

34
func init() {
1✔
35
        // Some well-known scalar-like structs.
1✔
36
        MarkAsScalar(reflect.TypeOf(time.Time{}))
1✔
37
        MarkAsScalar(reflect.TypeOf(reflect.Value{}))
1✔
38

1✔
39
        // Special case for elliptic.Curve which is used by TLS ECC certificate.
1✔
40
        // Package crypto/tls uses elliptic.Curve as enum values
1✔
41
        // so that they should be treated as opaque pointers.
1✔
42
        //
1✔
43
        // As elliptic.Curve is an interface, it can be *elliptic.CurveParam or elliptic.p256Curve.
1✔
44
        MarkAsOpaquePointer(reflect.TypeOf(&elliptic.CurveParams{}))
1✔
45
        curves := []elliptic.Curve{
1✔
46
                elliptic.P224(),
1✔
47
                elliptic.P256(),
1✔
48
                elliptic.P384(),
1✔
49
                elliptic.P521(),
1✔
50
        }
1✔
51

1✔
52
        for _, curve := range curves {
2✔
53
                MarkAsOpaquePointer(reflect.ValueOf(curve).Type())
1✔
54
        }
1✔
55

56
        // Special case for reflect.Type (actually *reflect.rtype):
57
        // The *reflect.rtype should not be copied as it is immutable and
58
        // may point to a variable that actual type is not reflect.rtype,
59
        // e.g. *reflect.arrayType or *reflect.chanType.
60
        MarkAsOpaquePointer(reflect.TypeOf(reflect.TypeOf(0)))
1✔
61

1✔
62
        // Some well-known no-copy structs.
1✔
63
        //
1✔
64
        // Almost all structs defined in package "sync" and "sync/atomic" are set
1✔
65
        // except `sync.Once` which can be safely cloned with a correct done value.
1✔
66
        SetCustomFunc(reflect.TypeOf(sync.Mutex{}), emptyCloneFunc)
1✔
67
        SetCustomFunc(reflect.TypeOf(sync.RWMutex{}), emptyCloneFunc)
1✔
68
        SetCustomFunc(reflect.TypeOf(sync.WaitGroup{}), emptyCloneFunc)
1✔
69
        SetCustomFunc(reflect.TypeOf(sync.Cond{}), func(allocator *Allocator, old, new reflect.Value) {
2✔
70
                // Copy the New func from old value.
1✔
71
                oldL := old.FieldByName("L")
1✔
72
                newL := allocator.Clone(oldL)
1✔
73
                new.FieldByName("L").Set(newL)
1✔
74
        })
1✔
75
        SetCustomFunc(reflect.TypeOf(sync.Pool{}), func(allocator *Allocator, old, new reflect.Value) {
2✔
76
                // Copy the New func from old value.
1✔
77
                oldFn := old.FieldByName("New")
1✔
78
                newFn := allocator.Clone(oldFn)
1✔
79
                new.FieldByName("New").Set(newFn)
1✔
80
        })
1✔
81
        SetCustomFunc(reflect.TypeOf(sync.Map{}), func(allocator *Allocator, old, new reflect.Value) {
2✔
82
                if !old.CanAddr() {
1✔
83
                        return
×
84
                }
×
85

86
                // Clone all values inside sync.Map.
87
                oldMap := old.Addr().Interface().(*sync.Map)
1✔
88
                newMap := new.Addr().Interface().(*sync.Map)
1✔
89
                oldMap.Range(func(key, value interface{}) bool {
2✔
90
                        k := clone(allocator, key)
1✔
91
                        v := clone(allocator, value)
1✔
92
                        newMap.Store(k, v)
1✔
93
                        return true
1✔
94
                })
1✔
95
        })
96
        SetCustomFunc(reflect.TypeOf(atomic.Value{}), func(allocator *Allocator, old, new reflect.Value) {
2✔
97
                if !old.CanAddr() {
1✔
98
                        return
×
99
                }
×
100

101
                // Clone value inside atomic.Value.
102
                oldValue := old.Addr().Interface().(*atomic.Value)
1✔
103
                newValue := new.Addr().Interface().(*atomic.Value)
1✔
104
                v := oldValue.Load()
1✔
105
                cloned := clone(allocator, v)
1✔
106
                newValue.Store(cloned)
1✔
107
        })
108
}
109

110
// MarkAsScalar marks t as a scalar type so that all clone methods will copy t by value.
111
// If t is not struct or pointer to struct, MarkAsScalar ignores t.
112
//
113
// In the most cases, it's not necessary to call it explicitly.
114
// If a struct type contains scalar type fields only, the struct will be marked as scalar automatically.
115
//
116
// Here is a list of types marked as scalar by default:
117
//   - time.Time
118
//   - reflect.Value
119
func MarkAsScalar(t reflect.Type) {
1✔
120
        for t.Kind() == reflect.Ptr {
2✔
121
                t = t.Elem()
1✔
122
        }
1✔
123

124
        if t.Kind() != reflect.Struct {
2✔
125
                return
1✔
126
        }
1✔
127

128
        cachedStructTypes.Store(t, zeroStructType)
1✔
129
}
130

131
// MarkAsOpaquePointer marks t as an opaque pointer so that all clone methods will copy t by value.
132
// If t is not a pointer, MarkAsOpaquePointer ignores t.
133
//
134
// Here is a list of types marked as opaque pointers by default:
135
//   - `elliptic.Curve`, which is `*elliptic.CurveParam` or `elliptic.p256Curve`;
136
//   - `reflect.Type`, which is `*reflect.rtype` defined in `runtime`.
137
func MarkAsOpaquePointer(t reflect.Type) {
1✔
138
        if t.Kind() != reflect.Ptr {
2✔
139
                return
1✔
140
        }
1✔
141

142
        cachedPointerTypes.Store(t, struct{}{})
1✔
143
}
144

145
// Func is a custom func to clone value from old to new.
146
// The new is a zero value
147
// which `new.CanSet()` and `new.CanAddr()` is guaranteed to be true.
148
//
149
// Func must update the new to return result.
150
type Func func(allocator *Allocator, old, new reflect.Value)
151

152
// emptyCloneFunc is used to disable shadow copy.
153
// It's useful when cloning sync.Mutex as cloned value must be a zero value.
154
func emptyCloneFunc(allocator *Allocator, old, new reflect.Value) {}
1✔
155

156
// SetCustomFunc sets a custom clone function for type t.
157
// If t is not struct or pointer to struct, SetCustomFunc ignores t.
158
//
159
// If fn is nil, remove the custom clone function for type t.
160
func SetCustomFunc(t reflect.Type, fn Func) {
1✔
161
        if fn == nil {
1✔
162
                cachedCustomFuncTypes.Delete(t)
×
163
                return
×
164
        }
×
165

166
        for t.Kind() == reflect.Ptr {
1✔
167
                t = t.Elem()
×
168
        }
×
169

170
        if t.Kind() != reflect.Struct {
1✔
171
                return
×
172
        }
×
173

174
        cachedCustomFuncTypes.Store(t, fn)
1✔
175
}
176

177
// Init creates a new value of src.Type() and shadow copies all content from src.
178
// If noCustomFunc is set to true, custom clone function will be ignored.
179
//
180
// Init returns true if the value is cloned by a custom func.
181
// Caller should skip cloning struct fields in depth.
182
func (st *structType) Init(allocator *Allocator, src, nv reflect.Value, noCustomFunc bool) (done bool) {
1✔
183
        dst := nv.Elem()
1✔
184

1✔
185
        if !noCustomFunc && st.fn != nil {
2✔
186
                if !src.CanInterface() {
2✔
187
                        src = forceClearROFlag(src)
1✔
188
                }
1✔
189

190
                st.fn(allocator, src, dst)
1✔
191
                done = true
1✔
192
                return
1✔
193
        }
194

195
        ptr := unsafe.Pointer(nv.Pointer())
1✔
196
        shadowCopy(src, ptr)
1✔
197
        done = len(st.PointerFields) == 0
1✔
198
        return
1✔
199
}
200

201
func (st *structType) CanShadowCopy() bool {
1✔
202
        return len(st.PointerFields) == 0 && st.fn == nil
1✔
203
}
1✔
204

205
func loadStructType(t reflect.Type) (st structType) {
1✔
206
        if v, ok := cachedStructTypes.Load(t); ok {
2✔
207
                st = v.(structType)
1✔
208
                return
1✔
209
        }
1✔
210

211
        num := t.NumField()
1✔
212
        pointerFields := make([]structFieldType, 0, num)
1✔
213

1✔
214
        for i := 0; i < num; i++ {
2✔
215
                field := t.Field(i)
1✔
216
                ft := field.Type
1✔
217
                k := ft.Kind()
1✔
218

1✔
219
                if isScalar(k) {
2✔
220
                        continue
1✔
221
                }
222

223
                switch k {
1✔
224
                case reflect.Array:
1✔
225
                        if ft.Len() == 0 {
1✔
226
                                continue
×
227
                        }
228

229
                        elem := ft.Elem()
1✔
230

1✔
231
                        if isScalar(elem.Kind()) {
2✔
232
                                continue
1✔
233
                        }
234

235
                        if elem.Kind() == reflect.Struct {
1✔
236
                                if fst := loadStructType(elem); fst.CanShadowCopy() {
×
237
                                        continue
×
238
                                }
239
                        }
240
                case reflect.Struct:
1✔
241
                        if fst := loadStructType(ft); fst.CanShadowCopy() {
2✔
242
                                continue
1✔
243
                        }
244
                }
245

246
                pointerFields = append(pointerFields, structFieldType{
1✔
247
                        Offset: field.Offset,
1✔
248
                        Index:  i,
1✔
249
                })
1✔
250
        }
251

252
        if len(pointerFields) == 0 {
2✔
253
                pointerFields = nil // Release memory ASAP.
1✔
254
        }
1✔
255

256
        st = structType{
1✔
257
                PointerFields: pointerFields,
1✔
258
        }
1✔
259

1✔
260
        if fn, ok := cachedCustomFuncTypes.Load(t); ok {
2✔
261
                st.fn = fn.(Func)
1✔
262
        }
1✔
263

264
        cachedStructTypes.LoadOrStore(t, st)
1✔
265
        return
1✔
266
}
267

268
func isScalar(k reflect.Kind) bool {
1✔
269
        switch k {
1✔
270
        case reflect.Bool,
271
                reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
272
                reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
273
                reflect.Float32, reflect.Float64,
274
                reflect.Complex64, reflect.Complex128,
275
                reflect.Func,
276
                reflect.UnsafePointer,
277
                reflect.Invalid:
1✔
278
                return true
1✔
279

280
        case reflect.String:
1✔
281
                // If arena is not enabled, string can be copied as scalar safely
1✔
282
                // as it's immutable by design.
1✔
283
                return !arenaIsEnabled
1✔
284
        }
285

286
        return false
1✔
287
}
288

289
func isOpaquePointer(t reflect.Type) (ok bool) {
1✔
290
        _, ok = cachedPointerTypes.Load(t)
1✔
291
        return
1✔
292
}
1✔
293

294
func copyScalarValue(src reflect.Value) reflect.Value {
1✔
295
        if src.CanInterface() {
2✔
296
                return src
1✔
297
        }
1✔
298

299
        // src is an unexported field value. Copy its value.
300
        switch src.Kind() {
1✔
301
        case reflect.Bool:
1✔
302
                return reflect.ValueOf(src.Bool())
1✔
303

304
        case reflect.Int:
1✔
305
                return reflect.ValueOf(int(src.Int()))
1✔
306
        case reflect.Int8:
1✔
307
                return reflect.ValueOf(int8(src.Int()))
1✔
308
        case reflect.Int16:
1✔
309
                return reflect.ValueOf(int16(src.Int()))
1✔
310
        case reflect.Int32:
1✔
311
                return reflect.ValueOf(int32(src.Int()))
1✔
312
        case reflect.Int64:
1✔
313
                return reflect.ValueOf(src.Int())
1✔
314

315
        case reflect.Uint:
1✔
316
                return reflect.ValueOf(uint(src.Uint()))
1✔
317
        case reflect.Uint8:
1✔
318
                return reflect.ValueOf(uint8(src.Uint()))
1✔
319
        case reflect.Uint16:
1✔
320
                return reflect.ValueOf(uint16(src.Uint()))
1✔
321
        case reflect.Uint32:
1✔
322
                return reflect.ValueOf(uint32(src.Uint()))
1✔
323
        case reflect.Uint64:
1✔
324
                return reflect.ValueOf(src.Uint())
1✔
325
        case reflect.Uintptr:
1✔
326
                return reflect.ValueOf(uintptr(src.Uint()))
1✔
327

328
        case reflect.Float32:
1✔
329
                return reflect.ValueOf(float32(src.Float()))
1✔
330
        case reflect.Float64:
1✔
331
                return reflect.ValueOf(src.Float())
1✔
332

333
        case reflect.Complex64:
1✔
334
                return reflect.ValueOf(complex64(src.Complex()))
1✔
335
        case reflect.Complex128:
1✔
336
                return reflect.ValueOf(src.Complex())
1✔
337

338
        case reflect.String:
1✔
339
                return reflect.ValueOf(src.String())
1✔
340
        case reflect.Func:
1✔
341
                t := src.Type()
1✔
342

1✔
343
                if src.IsNil() {
2✔
344
                        return reflect.Zero(t)
1✔
345
                }
1✔
346

347
                // Don't use this trick unless we have no choice.
348
                return forceClearROFlag(src)
1✔
349
        case reflect.UnsafePointer:
1✔
350
                return reflect.ValueOf(unsafe.Pointer(src.Pointer()))
1✔
351
        }
352

353
        panic(fmt.Errorf("go-clone: <bug> impossible type `%v` when cloning private field", src.Type()))
×
354
}
355

356
var typeOfInterface = reflect.TypeOf((*interface{})(nil)).Elem()
357

358
// forceClearROFlag clears all RO flags in v to make v accessible.
359
// It's a hack based on the fact that InterfaceData is always available on RO data.
360
// This hack can be broken in any Go version.
361
// Don't use it unless we have no choice, e.g. copying func in some edge cases.
362
func forceClearROFlag(v reflect.Value) reflect.Value {
1✔
363
        var i interface{}
1✔
364
        indirect := 0
1✔
365

1✔
366
        // Save flagAddr.
1✔
367
        for v.CanAddr() {
2✔
368
                v = v.Addr()
1✔
369
                indirect++
1✔
370
        }
1✔
371

372
        v = v.Convert(typeOfInterface)
1✔
373
        nv := reflect.ValueOf(&i)
1✔
374
        *(*interfaceData)(unsafe.Pointer(nv.Pointer())) = parseReflectValue(v)
1✔
375
        cleared := nv.Elem().Elem()
1✔
376

1✔
377
        for indirect > 0 {
2✔
378
                cleared = cleared.Elem()
1✔
379
                indirect--
1✔
380
        }
1✔
381

382
        return cleared
1✔
383
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc