@@ -16,6 +16,35 @@ pub const page_size = switch (builtin.arch) {
16
16
pub const Allocator = struct {
17
17
pub const Error = error {OutOfMemory };
18
18
19
+ /// Allocate memory.
20
+ allocFn : fn (self : * Allocator , len : usize , alignment : u29 ) Error ! []u8 ,
21
+
22
+ /// Free memory returned by allocFn, this function must succeed.
23
+ freeFn : fn (self : * Allocator , buf : []u8 ) void ,
24
+
25
+ /// Resizes memory in-place returned by allocFn. This function is optional.
26
+ resizeFn : fn (self : * Allocator , buf : []u8 , new_len : usize ) Error ! void ,
27
+
28
+ /// call allocFn with the given allocator
29
+ pub fn allocMem (self : * Allocator , new_len : usize , alignment : u29 ) Error ! []u8 {
30
+ return self .allocFn (self , new_len , alignment );
31
+ }
32
+
33
+ /// call freeFn with the given allocator
34
+ pub fn freeMem (self : * Allocator , buf : []u8 ) void {
35
+ self .freeFn (self , buf );
36
+ }
37
+
38
+ /// call allocFn with the given allocator
39
+ pub fn resizeMem (self : * Allocator , buf : []u8 , new_len : usize ) Error ! void {
40
+ return self .resizeFn (self , buf , new_len );
41
+ }
42
+
43
+ /// Set to resizeFn if in-place resize is not supported.
44
+ pub fn noResize (self : * Allocator , buf : []u8 , new_len : usize ) Error ! void {
45
+ return Error .OutOfMemory ;
46
+ }
47
+
19
48
/// Realloc is used to modify the size or alignment of an existing allocation,
20
49
/// as well as to provide the allocator with an opportunity to move an allocation
21
50
/// to a better location.
@@ -24,7 +53,7 @@ pub const Allocator = struct {
24
53
/// When the size/alignment is less than or equal to the previous allocation,
25
54
/// this function returns `error.OutOfMemory` when the allocator decides the client
26
55
/// would be better off keeping the extra alignment/size. Clients will call
27
- /// `shrinkFn ` when they require the allocator to track a new alignment/size,
56
+ /// `shrinkMem ` when they require the allocator to track a new alignment/size,
28
57
/// and so this function should only return success when the allocator considers
29
58
/// the reallocation desirable from the allocator's perspective.
30
59
/// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle
@@ -37,16 +66,16 @@ pub const Allocator = struct {
37
66
/// as `old_mem` was when `reallocFn` is called. The bytes of
38
67
/// `return_value[old_mem.len..]` have undefined values.
39
68
/// The returned slice must have its pointer aligned at least to `new_alignment` bytes.
40
- reallocFn : fn (
69
+ fn reallocMem (
41
70
self : * Allocator ,
42
71
/// Guaranteed to be the same as what was returned from most recent call to
43
- /// `reallocFn` or `shrinkFn `.
72
+ /// `reallocFn` or `shrinkMem `.
44
73
/// If `old_mem.len == 0` then this is a new allocation and `new_byte_count`
45
74
/// is guaranteed to be >= 1.
46
75
old_mem : []u8 ,
47
76
/// If `old_mem.len == 0` then this is `undefined`, otherwise:
48
77
/// Guaranteed to be the same as what was returned from most recent call to
49
- /// `reallocFn` or `shrinkFn `.
78
+ /// `reallocFn` or `shrinkMem `.
50
79
/// Guaranteed to be >= 1.
51
80
/// Guaranteed to be a power of 2.
52
81
old_alignment : u29 ,
@@ -57,23 +86,63 @@ pub const Allocator = struct {
57
86
/// Guaranteed to be a power of 2.
58
87
/// Returned slice's pointer must have this alignment.
59
88
new_alignment : u29 ,
60
- ) Error ! []u8 ,
89
+ ) Error ! []u8 {
90
+ if (old_mem .len == 0 )
91
+ return self .allocMem (new_byte_count , new_alignment );
92
+ if (new_byte_count == 0 ) {
93
+ self .freeMem (old_mem );
94
+ return old_mem [0.. 0];
95
+ }
96
+ if (isAligned (@ptrToInt (old_mem .ptr ), new_alignment )) {
97
+ if (self .resizeMem (old_mem , new_byte_count )) {
98
+ return old_mem .ptr [0.. new_byte_count ];
99
+ } else | e | switch (e ) {
100
+ error .OutOfMemory = > {},
101
+ }
102
+ }
103
+ if (new_byte_count <= old_mem .len and new_alignment <= old_alignment ) {
104
+ return error .OutOfMemory ;
105
+ }
106
+ return self .moveMem (old_mem , new_byte_count , new_alignment );
107
+ }
61
108
62
- /// This function deallocates memory. It must succeed.
63
- shrinkFn : fn (
109
+ /// Move the given memory to a new location in the given allocator to accomodate a new
110
+ /// size and alignment.
111
+ fn moveMem (self : * Allocator , old_mem : []u8 , new_len : usize , new_alignment : u29 ) Error ! []u8 {
112
+ assert (old_mem .len > 0 );
113
+ assert (new_len > 0 );
114
+ const new_mem = try self .allocMem (new_len , new_alignment );
115
+ @memcpy (new_mem .ptr , old_mem .ptr , std .math .min (new_len , old_mem .len ));
116
+ self .freeMem (old_mem );
117
+ return new_mem ;
118
+ }
119
+
120
+ /// This function attempts to deallocates memory. It must succeed.
121
+ fn shrinkMem (
64
122
self : * Allocator ,
65
123
/// Guaranteed to be the same as what was returned from most recent call to
66
- /// `reallocFn` or `shrinkFn `.
124
+ /// `reallocFn` or `shrinkMem `.
67
125
old_mem : []u8 ,
68
126
/// Guaranteed to be the same as what was returned from most recent call to
69
- /// `reallocFn` or `shrinkFn `.
127
+ /// `reallocFn` or `shrinkMem `.
70
128
old_alignment : u29 ,
71
129
/// Guaranteed to be less than or equal to `old_mem.len`.
72
130
new_byte_count : usize ,
73
131
/// If `new_byte_count == 0` then this is `undefined`, otherwise:
74
132
/// Guaranteed to be less than or equal to `old_alignment`.
75
133
new_alignment : u29 ,
76
- ) []u8 ,
134
+ ) []u8 {
135
+ assert (new_byte_count <= old_mem .len );
136
+ assert (new_alignment <= old_alignment );
137
+ if (new_byte_count == 0 ) {
138
+ self .freeMem (old_mem );
139
+ } else {
140
+ self .resizeMem (old_mem , new_byte_count ) catch | e | switch (e ) {
141
+ error .OutOfMemory = > {}, // ignore the error, can't fail
142
+ };
143
+ }
144
+ return old_mem [0.. new_byte_count ];
145
+ }
77
146
78
147
/// Returns a pointer to undefined memory.
79
148
/// Call `destroy` with the result to free the memory.
@@ -89,8 +158,7 @@ pub const Allocator = struct {
89
158
const T = @TypeOf (ptr ).Child ;
90
159
if (@sizeOf (T ) == 0 ) return ;
91
160
const non_const_ptr = @intToPtr ([* ]u8 , @ptrToInt (ptr ));
92
- const shrink_result = self .shrinkFn (self , non_const_ptr [0.. @sizeOf (T )], @alignOf (T ), 0 , 1 );
93
- assert (shrink_result .len == 0 );
161
+ self .freeMem (non_const_ptr [0.. @sizeOf (T )]);
94
162
}
95
163
96
164
/// Allocates an array of `n` items of type `T` and sets all the
@@ -161,7 +229,7 @@ pub const Allocator = struct {
161
229
}
162
230
163
231
const byte_count = math .mul (usize , @sizeOf (T ), n ) catch return Error .OutOfMemory ;
164
- const byte_slice = try self .reallocFn ( self , &[ 0 ] u8 {}, undefined , byte_count , a );
232
+ const byte_slice = try self .allocMem ( byte_count , a );
165
233
assert (byte_slice .len == byte_count );
166
234
@memset (byte_slice .ptr , undefined , byte_slice .len );
167
235
if (alignment == null ) {
@@ -215,7 +283,7 @@ pub const Allocator = struct {
215
283
const old_byte_slice = mem .sliceAsBytes (old_mem );
216
284
const byte_count = math .mul (usize , @sizeOf (T ), new_n ) catch return Error .OutOfMemory ;
217
285
// Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure
218
- const byte_slice = try self .reallocFn ( self , old_byte_slice , Slice .alignment , byte_count , new_alignment );
286
+ const byte_slice = try self .reallocMem ( old_byte_slice , Slice .alignment , byte_count , new_alignment );
219
287
assert (byte_slice .len == byte_count );
220
288
if (new_n > old_mem .len ) {
221
289
@memset (byte_slice .ptr + old_byte_slice .len , undefined , byte_slice .len - old_byte_slice .len );
@@ -262,7 +330,7 @@ pub const Allocator = struct {
262
330
263
331
const old_byte_slice = mem .sliceAsBytes (old_mem );
264
332
@memset (old_byte_slice .ptr + byte_count , undefined , old_byte_slice .len - byte_count );
265
- const byte_slice = self .shrinkFn ( self , old_byte_slice , Slice .alignment , byte_count , new_alignment );
333
+ const byte_slice = self .shrinkMem ( old_byte_slice , Slice .alignment , byte_count , new_alignment );
266
334
assert (byte_slice .len == byte_count );
267
335
return mem .bytesAsSlice (T , @alignCast (new_alignment , byte_slice ));
268
336
}
@@ -276,7 +344,7 @@ pub const Allocator = struct {
276
344
if (bytes_len == 0 ) return ;
277
345
const non_const_ptr = @intToPtr ([* ]u8 , @ptrToInt (bytes .ptr ));
278
346
@memset (non_const_ptr , undefined , bytes_len );
279
- const shrink_result = self .shrinkFn ( self , non_const_ptr [0.. bytes_len ], Slice .alignment , 0 , 1 );
347
+ const shrink_result = self .shrinkMem ( non_const_ptr [0.. bytes_len ], Slice .alignment , 0 , 1 );
280
348
assert (shrink_result .len == 0 );
281
349
}
282
350
0 commit comments