-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmalloc.go
More file actions
178 lines (164 loc) · 4.78 KB
/
malloc.go
File metadata and controls
178 lines (164 loc) · 4.78 KB
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
package unsafe
import (
"reflect"
"unsafe"
)
type goTyp struct {
Size uintptr
PtrData uintptr
Hash uint32
Flags uint8
Align uint8
FieldAlign uint8
KindFlags uint8
Traits unsafe.Pointer
GCData *byte
Str int32
PtrToSelf int32
}
type goItab struct {
it unsafe.Pointer
Vt *goTyp
hv uint32
_ [4]byte
fn [1]uintptr
}
type goIface struct {
Itab *goItab
Value unsafe.Pointer
}
func goType(t reflect.Type) *goTyp {
return (*goTyp)((*goIface)(unsafe.Pointer(&t)).Value)
}
//go:linkname mallocgc runtime.mallocgc
func mallocgc(size uintptr, typ *goTyp, needzero bool) unsafe.Pointer
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
// ClearOut zeros out selected blocks of memory.
// clearout is a slice containing offset-size pairs.
// The offset is always relative to baseAddr.
//
// NOTE: It uses runtime.memclrNoHeapPointers so make sure you read
// documentation on when it is safe to use.
func ClearOut(baseAddr unsafe.Pointer, clearout [][2]uintptr) {
for _, v := range clearout {
memclrNoHeapPointers(unsafe.Add(baseAddr, v[0]), v[1])
}
}
// Malloc allocates contiguous blocks of memory without zeroing.
// However, the memory will be cleared when zerofill is set.
//
// All allocated memory must sensibly be filled by the developer immediately,
// since the pre-existing data may contain garbage.
//
// WARNING: If the returned pointer will be cast to a struct with fields containing
// pointers, then typ must be provided to inform the garbage collector.
// As an optimization, typ can be stored in a global variable and reused.
//
// NOTE: Reference types (eg. strings, slices, interfaces, channels, maps etc.)
// contain pointers.
//
// Example:
//
// type Person struct {
// name [50]byte
// age int
// phone int
// }
//
// ptr := Malloc(unsafe.Sizeof(Person{}), false)
// ClearOut(ptr, [][2]uintptr{})
// p := *(*Person)(ptr)
func Malloc(size uintptr, zerofill bool, typ ...reflect.Type) unsafe.Pointer {
var t *goTyp
if len(typ) > 0 {
t = goType(typ[0])
}
return mallocgc(size, t, zerofill)
}
// UnsafeClearOutFields is used to return the offset and size of each field of a struct
// that should be cleared.
type UnsafeClearOutFields interface {
// UnsafeClearOutFields must be implemented on the pointer type *T.
// The return values can be hardcoded using unsafe.Offsetof() and unsafe.Sizeof().
UnsafeClearOutFields() [][2]uintptr
}
// New allocates memory for a struct without zeroing.
// A pointer to the newly allocated value is returned.
// The returned struct is not guaranteed to be the zero value.
//
// NOTE: UnsafeClearOutFields must be implemented as a method with a pointer receiver.
// UnsafeClearOutFields is used to selectively zero fields.
//
// WARNING: T must not have fields containing pointers. ...But if it
// does contain pointers, provide typ=nil to inform the garbage collector.
// As an optimization, typ can be stored in a global variable and reused.
//
// NOTE: Reference types (eg. strings, slices, interfaces, channels, maps etc.)
// contain pointers.
//
// Example:
//
// type Person struct {
// name [50]byte
// age int
// phone int
// }
//
// func (p *Person) UnsafeClearOutFields() [][2]uintptr {
// return [][2]uintptr {
// {unsafe.Offsetof(Person{}.name), unsafe.Sizeof(Person{}.name)},
// {unsafe.Offsetof(Person{}.phone), unsafe.Sizeof(Person{}.phone)},
// }
// return nil
// }
//
// func main() {
// p := New[Person]() // like p := new(Person)
// }
func New[T any, PtrT interface {
*T
UnsafeClearOutFields
}](typ ...reflect.Type,
) *T {
var x PtrT
var ptr unsafe.Pointer
if len(typ) > 0 {
if typ[0] == nil {
typ[0] = reflect.TypeFor[T]()
}
ptr = mallocgc(unsafe.Sizeof(*x), goType(typ[0]), false)
} else {
ptr = mallocgc(unsafe.Sizeof(*x), nil, false)
}
for _, v := range x.UnsafeClearOutFields() {
memclrNoHeapPointers(unsafe.Add(ptr, v[0]), v[1])
}
return (*T)(ptr)
}
// NewZero allocates memory for a zero-value struct.
// A pointer to the newly allocated value is returned.
// It is equivalent to new(T) (but seems to be faster).
//
// WARNING: T must not have fields containing pointers. ...But if it
// does contain pointers, provide typ=nil to inform the garbage collector.
// As an optimization, typ can be stored in a global variable and reused.
//
// NOTE: Reference types (eg. strings, slices, interfaces, channels, maps etc.)
// contain pointers.
//
// Example:
//
// p := NewZero[T]()
//
// Deprecated: From Go1.26+, new(T) will be on par or faster than NewZero.
func NewZero[T any](typ ...reflect.Type) *T {
var x *T
if len(typ) > 0 {
if typ[0] == nil {
typ[0] = reflect.TypeFor[T]()
}
return (*T)(mallocgc(unsafe.Sizeof(*x), goType(typ[0]), true))
}
return (*T)(mallocgc(unsafe.Sizeof(*x), nil, true))
}