| ... | @@ -190,6 +190,104 @@ pub const DirectAllocator = struct { | ... | @@ -190,6 +190,104 @@ pub const DirectAllocator = struct { |
| 190 | } | 190 | } |
| 191 | }; | 191 | }; |
| 192 | | 192 | |
| | 193 | pub 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 | |
| 193 | /// This allocator takes an existing allocator, wraps it, and provides an interface | 291 | /// This allocator takes an existing allocator, wraps it, and provides an interface |
| 194 | /// where you can allocate without freeing, and then free it all together. | 292 | /// where you can allocate without freeing, and then free it all together. |
| 195 | pub const ArenaAllocator = struct { | 293 | pub const ArenaAllocator = struct { |
| ... | @@ -590,6 +688,19 @@ test "DirectAllocator" { | ... | @@ -590,6 +688,19 @@ test "DirectAllocator" { |
| 590 | try testAllocatorAlignedShrink(allocator); | 688 | try testAllocatorAlignedShrink(allocator); |
| 591 | } | 689 | } |
| 592 | | 690 | |
| | 691 | test "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 | |
| 593 | test "ArenaAllocator" { | 704 | test "ArenaAllocator" { |
| 594 | var direct_allocator = DirectAllocator.init(); | 705 | var direct_allocator = DirectAllocator.init(); |
| 595 | defer direct_allocator.deinit(); | 706 | defer direct_allocator.deinit(); |