| ... | @@ -79,19 +79,38 @@ pub const DirectAllocator = struct { | ... | @@ -79,19 +79,38 @@ pub const DirectAllocator = struct { |
| 79 | | 79 | |
| 80 | switch (builtin.os) { | 80 | switch (builtin.os) { |
| 81 | Os.linux, Os.macosx, Os.ios => { | 81 | Os.linux, Os.macosx, Os.ios => { |
| 82 | assert(alignment <= os.page_size); | | |
| 83 | const p = os.posix; | 82 | const p = os.posix; |
| 84 | const addr = p.mmap(null, n, p.PROT_READ|p.PROT_WRITE, | 83 | const alloc_size = if(alignment <= os.page_size) n else n + alignment; |
| 85 | p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0); | 84 | const addr = p.mmap(null, alloc_size, p.PROT_READ|p.PROT_WRITE, |
| 86 | if (addr == p.MAP_FAILED) { | 85 | p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0); |
| 87 | return error.OutOfMemory; | 86 | if(addr == p.MAP_FAILED) return error.OutOfMemory; |
| 88 | } | 87 | |
| 89 | return @intToPtr(&u8, addr)[0..n]; | 88 | if(alloc_size == n) return @intToPtr(&u8, addr)[0..n]; |
| | 89 | |
| | 90 | var aligned_addr = addr & ~usize(alignment - 1); |
| | 91 | aligned_addr += alignment; |
| | 92 | |
| | 93 | //We can unmap the unused portions of our mmap, but we must only |
| | 94 | // pass munmap bytes that exist outside our allocated pages or it |
| | 95 | // will happily eat us too |
| | 96 | |
| | 97 | //Since alignment > page_size, we are by definition on a page boundry |
| | 98 | const unused_start = addr; |
| | 99 | const unused_len = aligned_addr - 1 - unused_start; |
| | 100 | |
| | 101 | var err = p.munmap(@intToPtr(&u8, unused_start), unused_len); |
| | 102 | debug.assert(p.getErrno(err) == 0); |
| | 103 | |
| | 104 | //It is impossible that there is an unoccupied page at the top of our |
| | 105 | // mmap. |
| | 106 | |
| | 107 | return @intToPtr(&u8, aligned_addr)[0..n]; |
| 90 | }, | 108 | }, |
| 91 | Os.windows => { | 109 | Os.windows => { |
| 92 | const amt = n + alignment + @sizeOf(usize); | 110 | const amt = n + alignment + @sizeOf(usize); |
| 93 | const heap_handle = self.heap_handle ?? blk: { | 111 | const heap_handle = self.heap_handle ?? blk: { |
| 94 | const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory; | 112 | const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) |
| | 113 | ?? return error.OutOfMemory; |
| 95 | self.heap_handle = hh; | 114 | self.heap_handle = hh; |
| 96 | break :blk hh; | 115 | break :blk hh; |
| 97 | }; | 116 | }; |
| ... | @@ -322,6 +341,7 @@ test "DirectAllocator" { | ... | @@ -322,6 +341,7 @@ test "DirectAllocator" { |
| 322 | | 341 | |
| 323 | const allocator = &direct_allocator.allocator; | 342 | const allocator = &direct_allocator.allocator; |
| 324 | try testAllocator(allocator); | 343 | try testAllocator(allocator); |
| | 344 | try testAllocatorLargeAlignment(allocator); |
| 325 | } | 345 | } |
| 326 | | 346 | |
| 327 | test "ArenaAllocator" { | 347 | test "ArenaAllocator" { |
| ... | @@ -332,6 +352,7 @@ test "ArenaAllocator" { | ... | @@ -332,6 +352,7 @@ test "ArenaAllocator" { |
| 332 | defer arena_allocator.deinit(); | 352 | defer arena_allocator.deinit(); |
| 333 | | 353 | |
| 334 | try testAllocator(&arena_allocator.allocator); | 354 | try testAllocator(&arena_allocator.allocator); |
| | 355 | try testAllocatorLargeAlignment(&arena_allocator.allocator); |
| 335 | } | 356 | } |
| 336 | | 357 | |
| 337 | var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined; | 358 | var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined; |
| ... | @@ -339,6 +360,7 @@ test "FixedBufferAllocator" { | ... | @@ -339,6 +360,7 @@ test "FixedBufferAllocator" { |
| 339 | var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); | 360 | var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); |
| 340 | | 361 | |
| 341 | try testAllocator(&fixed_buffer_allocator.allocator); | 362 | try testAllocator(&fixed_buffer_allocator.allocator); |
| | 363 | try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); |
| 342 | } | 364 | } |
| 343 | | 365 | |
| 344 | fn testAllocator(allocator: &mem.Allocator) !void { | 366 | fn testAllocator(allocator: &mem.Allocator) !void { |
| ... | @@ -360,3 +382,32 @@ fn testAllocator(allocator: &mem.Allocator) !void { | ... | @@ -360,3 +382,32 @@ fn testAllocator(allocator: &mem.Allocator) !void { |
| 360 | | 382 | |
| 361 | allocator.free(slice); | 383 | allocator.free(slice); |
| 362 | } | 384 | } |
| | 385 | |
| | 386 | fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void { |
| | 387 | //Maybe a platform's page_size is actually the same as or |
| | 388 | // very near usize? |
| | 389 | if(os.page_size << 2 > @maxValue(usize)) return; |
| | 390 | |
| | 391 | const USizeShift = @IntType(false, std.math.log2(usize.bit_count)); |
| | 392 | const large_align = u29(os.page_size << 2); |
| | 393 | |
| | 394 | var align_mask: usize = undefined; |
| | 395 | _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask); |
| | 396 | |
| | 397 | var slice = try allocator.allocFn(allocator, 500, large_align); |
| | 398 | debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| | 399 | |
| | 400 | slice = try allocator.reallocFn(allocator, slice, 100, large_align); |
| | 401 | debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| | 402 | |
| | 403 | slice = try allocator.reallocFn(allocator, slice, 5000, large_align); |
| | 404 | debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| | 405 | |
| | 406 | slice = try allocator.reallocFn(allocator, slice, 10, large_align); |
| | 407 | debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| | 408 | |
| | 409 | slice = try allocator.reallocFn(allocator, slice, 20000, large_align); |
| | 410 | debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| | 411 | |
| | 412 | allocator.free(slice); |
| | 413 | } |