-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathallocator_sample_test.go
118 lines (96 loc) · 2.63 KB
/
allocator_sample_test.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
// Copyright 2023 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
//go:build !goexperiment.arenas
package clone
import (
"fmt"
"reflect"
"runtime"
"sync"
"unsafe"
)
func ExampleAllocator() {
// We can create a new allocator to hold customized config without poluting the default allocator.
// Calling FromHeap() is a convenient way to create a new allocator which allocates memory from heap.
allocator := FromHeap()
// Mark T as scalar only in the allocator.
type T struct {
Value *int
}
allocator.MarkAsScalar(reflect.TypeOf(new(T)))
t := &T{
Value: new(int),
}
cloned1 := allocator.Clone(reflect.ValueOf(t)).Interface().(*T)
cloned2 := Clone(t).(*T)
fmt.Println(t.Value == cloned1.Value)
fmt.Println(t.Value == cloned2.Value)
// Output:
// true
// false
}
func ExampleAllocator_syncPool() {
type Foo struct {
Bar int
}
typeOfFoo := reflect.TypeOf(Foo{})
poolUsed := 0 // For test only.
// A sync pool to allocate Foo.
p := &sync.Pool{
New: func() interface{} {
return &Foo{}
},
}
// Creates a custom allocator using p as pool.
allocator := NewAllocator(unsafe.Pointer(p), &AllocatorMethods{
New: func(pool unsafe.Pointer, t reflect.Type) reflect.Value {
// If t is Foo, allocate value from the sync pool p.
if t == typeOfFoo {
poolUsed++ // For test only.
p := (*sync.Pool)(pool)
v := p.Get()
runtime.SetFinalizer(v, func(v *Foo) {
*v = Foo{}
p.Put(v)
})
return reflect.ValueOf(v)
}
// Fallback to reflect API.
return reflect.New(t)
},
})
// Do clone.
target := []*Foo{
{Bar: 1},
{Bar: 2},
}
cloned := allocator.Clone(reflect.ValueOf(target)).Interface().([]*Foo)
fmt.Println(reflect.DeepEqual(target, cloned))
fmt.Println(poolUsed)
// Output:
// true
// 2
}
func ExampleAllocator_deepCloneString() {
// By default, string is considered as scalar and copied by value.
// In some cases, we may need to clone string deeply, that is, copy the underlying bytes.
// We can use a custom allocator to do this.
allocator := NewAllocator(nil, &AllocatorMethods{
IsScalar: func(t reflect.Kind) bool {
return t != reflect.String && IsScalar(t)
},
})
cloner := MakeCloner(allocator)
data := []byte("bytes")
s1 := *(*string)(unsafe.Pointer(&data)) // Unsafe conversion from []byte to string.
s2 := Clone(s1).(string) // s2 shares the same underlying bytes with s1.
s3 := cloner.Clone(s1).(string) // s3 has its own underlying bytes.
copy(data, "magic") // Change the underlying bytes.
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
// Output:
// magic
// magic
// bytes
}