authorgravatar for sahnvour@pm.meSahnvour <sahnvour@pm.me> 2019-05-08 17:53:58+02:00
committergravatar for sahnvour@pm.meSahnvour <sahnvour@pm.me> 2019-05-08 18:19:36+02:00
logd665948dcf74cb4fff7e815dbcdabe363a337c76
treebbd5f454a174366f48f385780dd56e3c7a5ce120
parent24ee7653184e6fbac0d05227723ddff2d716a28a

Duplicate windows's DirectAllocator as HeapAllocator, which it is in reality.


1 files changed, 111 insertions(+), 0 deletions(-)

std/heap.zig+111
......@@ -190,6 +190,104 @@ pub const DirectAllocator = struct {
190190 }
191191};
192192
193pub const HeapAllocator = switch (builtin.os) {
194 .windows => struct {
195 allocator: Allocator,
196 heap_handle: ?HeapHandle,
197
198 const HeapHandle = os.windows.HANDLE;
199
200 pub fn init() HeapAllocator {
201 return HeapAllocator{
202 .allocator = Allocator{
203 .reallocFn = realloc,
204 .shrinkFn = shrink,
205 },
206 .heap_handle = null,
207 };
208 }
209
210 pub fn deinit(self: *HeapAllocator) void {
211 if (self.heap_handle) |heap_handle| {
212 _ = os.windows.HeapDestroy(heap_handle);
213 }
214 }
215
216 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
217 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
218 if (n == 0)
219 return (([*]u8)(undefined))[0..0];
220
221 const amt = n + alignment + @sizeOf(usize);
222 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
223 const heap_handle = optional_heap_handle orelse blk: {
224 const hh = os.windows.HeapCreate(0, amt, 0) orelse return error.OutOfMemory;
225 const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh;
226 _ = os.windows.HeapDestroy(hh);
227 break :blk other_hh.?; // can't be null because of the cmpxchg
228 };
229 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
230 const root_addr = @ptrToInt(ptr);
231 const adjusted_addr = mem.alignForward(root_addr, alignment);
232 const record_addr = adjusted_addr + n;
233 @intToPtr(*align(1) usize, record_addr).* = root_addr;
234 return @intToPtr([*]u8, adjusted_addr)[0..n];
235 }
236
237 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
238 return realloc(allocator, old_mem, old_align, new_size, new_align) catch {
239 const old_adjusted_addr = @ptrToInt(old_mem.ptr);
240 const old_record_addr = old_adjusted_addr + old_mem.len;
241 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
242 const old_ptr = @intToPtr(*c_void, root_addr);
243 const new_record_addr = old_record_addr - new_size + old_mem.len;
244 @intToPtr(*align(1) usize, new_record_addr).* = root_addr;
245 return old_mem[0..new_size];
246 };
247 }
248
249 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
250 if (old_mem.len == 0) return alloc(allocator, new_size, new_align);
251
252 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
253 const old_adjusted_addr = @ptrToInt(old_mem.ptr);
254 const old_record_addr = old_adjusted_addr + old_mem.len;
255 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
256 const old_ptr = @intToPtr(*c_void, root_addr);
257
258 if (new_size == 0) {
259 if (os.windows.HeapFree(self.heap_handle.?, 0, old_ptr) == 0) unreachable;
260 return old_mem[0..0];
261 }
262
263 const amt = new_size + new_align + @sizeOf(usize);
264 const new_ptr = os.windows.HeapReAlloc(
265 self.heap_handle.?,
266 0,
267 old_ptr,
268 amt,
269 ) orelse return error.OutOfMemory;
270 const offset = old_adjusted_addr - root_addr;
271 const new_root_addr = @ptrToInt(new_ptr);
272 var new_adjusted_addr = new_root_addr + offset;
273 const offset_is_valid = new_adjusted_addr + new_size + @sizeOf(usize) <= new_root_addr + amt;
274 const offset_is_aligned = new_adjusted_addr % new_align == 0;
275 if (!offset_is_valid or !offset_is_aligned) {
276 // If HeapReAlloc didn't happen to move the memory to the new alignment,
277 // or the memory starting at the old offset would be outside of the new allocation,
278 // then we need to copy the memory to a valid aligned address and use that
279 const new_aligned_addr = mem.alignForward(new_root_addr, new_align);
280 @memcpy(@intToPtr([*]u8, new_aligned_addr), @intToPtr([*]u8, new_adjusted_addr), std.math.min(old_mem.len, new_size));
281 new_adjusted_addr = new_aligned_addr;
282 }
283 const new_record_addr = new_adjusted_addr + new_size;
284 @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr;
285 return @intToPtr([*]u8, new_adjusted_addr)[0..new_size];
286 }
287 },
288 else => @compileError("Unsupported OS"),
289};
290
193291/// This allocator takes an existing allocator, wraps it, and provides an interface
194292/// where you can allocate without freeing, and then free it all together.
195293pub const ArenaAllocator = struct {
......@@ -590,6 +688,19 @@ test "DirectAllocator" {
590688 try testAllocatorAlignedShrink(allocator);
591689}
592690
691test "HeapAllocator" {
692 if (builtin.os == .windows) {
693 var heap_allocator = HeapAllocator.init();
694 defer heap_allocator.deinit();
695
696 const allocator = &heap_allocator.allocator;
697 try testAllocator(allocator);
698 try testAllocatorAligned(allocator, 16);
699 try testAllocatorLargeAlignment(allocator);
700 try testAllocatorAlignedShrink(allocator);
701 }
702}
703
593704test "ArenaAllocator" {
594705 var direct_allocator = DirectAllocator.init();
595706 defer direct_allocator.deinit();