| ... | ... | @@ -15,48 +15,88 @@ pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator; |
| 15 | 15 | |
| 16 | 16 | const Allocator = mem.Allocator; |
| 17 | 17 | |
| 18 | | pub const c_allocator = &c_allocator_state; |
| 18 | usingnamespace if (comptime @hasDecl(c, "malloc_size")) struct { |
| 19 | pub const supports_malloc_size = true; |
| 20 | pub const malloc_size = c.malloc_size; |
| 21 | } else if (comptime @hasDecl(c, "malloc_usable_size")) struct { |
| 22 | pub const supports_malloc_size = true; |
| 23 | pub const malloc_size = c.malloc_usable_size; |
| 24 | } else struct { |
| 25 | pub const supports_malloc_size = false; |
| 26 | }; |
| 27 | |
| 28 | pub const c_allocator = mem.getAllocatorPtr(&c_allocator_state); |
| 19 | 29 | var c_allocator_state = Allocator{ |
| 20 | | .reallocFn = cRealloc, |
| 21 | | .shrinkFn = cShrink, |
| 30 | .allocFn = cAlloc, |
| 31 | .resizeFn = cResize, |
| 22 | 32 | }; |
| 23 | 33 | |
| 24 | | fn cRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 25 | | assert(new_align <= @alignOf(c_longdouble)); |
| 26 | | const old_ptr = if (old_mem.len == 0) null else @ptrCast(*c_void, old_mem.ptr); |
| 27 | | const buf = c.realloc(old_ptr, new_size) orelse return error.OutOfMemory; |
| 28 | | return @ptrCast([*]u8, buf)[0..new_size]; |
| 34 | fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Allocator.Error![]u8 { |
| 35 | assert(ptr_align <= @alignOf(c_longdouble)); |
| 36 | const ptr = @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory); |
| 37 | if (len_align == 0) { |
| 38 | return ptr[0..len]; |
| 39 | } |
| 40 | const full_len = init: { |
| 41 | if (comptime supports_malloc_size) { |
| 42 | const s = malloc_size(ptr); |
| 43 | assert(s >= len); |
| 44 | break :init s; |
| 45 | } |
| 46 | break :init len; |
| 47 | }; |
| 48 | return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)]; |
| 29 | 49 | } |
| 30 | 50 | |
| 31 | | fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 32 | | const old_ptr = @ptrCast(*c_void, old_mem.ptr); |
| 33 | | const buf = c.realloc(old_ptr, new_size) orelse return old_mem[0..new_size]; |
| 34 | | return @ptrCast([*]u8, buf)[0..new_size]; |
| 51 | fn cResize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Allocator.Error!usize { |
| 52 | if (new_len == 0) { |
| 53 | c.free(buf.ptr); |
| 54 | return 0; |
| 55 | } |
| 56 | if (new_len <= buf.len) { |
| 57 | return mem.alignAllocLen(buf.len, new_len, len_align); |
| 58 | } |
| 59 | if (comptime supports_malloc_size) { |
| 60 | const full_len = malloc_size(buf.ptr); |
| 61 | if (new_len <= full_len) { |
| 62 | return mem.alignAllocLen(full_len, new_len, len_align); |
| 63 | } |
| 64 | } |
| 65 | // TODO: could we still use realloc? are there any cases where we can guarantee that realloc won't move memory? |
| 66 | return error.OutOfMemory; |
| 35 | 67 | } |
| 36 | 68 | |
| 37 | 69 | /// This allocator makes a syscall directly for every allocation and free. |
| 38 | 70 | /// Thread-safe and lock-free. |
| 39 | 71 | pub const page_allocator = if (std.Target.current.isWasm()) |
| 40 | | &wasm_page_allocator_state |
| 72 | mem.getAllocatorPtr(&wasm_page_allocator_state) |
| 41 | 73 | else if (std.Target.current.os.tag == .freestanding) |
| 42 | 74 | root.os.heap.page_allocator |
| 43 | 75 | else |
| 44 | | &page_allocator_state; |
| 76 | mem.getAllocatorPtr(&page_allocator_state); |
| 45 | 77 | |
| 46 | 78 | var page_allocator_state = Allocator{ |
| 47 | | .reallocFn = PageAllocator.realloc, |
| 48 | | .shrinkFn = PageAllocator.shrink, |
| 79 | .allocFn = PageAllocator.alloc, |
| 80 | .resizeFn = PageAllocator.resize, |
| 49 | 81 | }; |
| 50 | 82 | var wasm_page_allocator_state = Allocator{ |
| 51 | | .reallocFn = WasmPageAllocator.realloc, |
| 52 | | .shrinkFn = WasmPageAllocator.shrink, |
| 83 | .allocFn = WasmPageAllocator.alloc, |
| 84 | .resizeFn = WasmPageAllocator.resize, |
| 53 | 85 | }; |
| 54 | 86 | |
| 55 | 87 | pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator"); |
| 56 | 88 | |
| 89 | /// Verifies that the adjusted length will still map to the full length |
| 90 | pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize { |
| 91 | const aligned_len = mem.alignAllocLen(full_len, len, len_align); |
| 92 | assert(mem.alignForward(aligned_len, mem.page_size) == full_len); |
| 93 | return aligned_len; |
| 94 | } |
| 95 | |
| 57 | 96 | const PageAllocator = struct { |
| 58 | | fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { |
| 59 | | if (n == 0) return &[0]u8{}; |
| 97 | fn alloc(allocator: *Allocator, n: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 { |
| 98 | assert(n > 0); |
| 99 | const alignedLen = mem.alignForward(n, mem.page_size); |
| 60 | 100 | |
| 61 | 101 | if (builtin.os.tag == .windows) { |
| 62 | 102 | const w = os.windows; |
| ... | ... | @@ -68,21 +108,21 @@ const PageAllocator = struct { |
| 68 | 108 | // see https://devblogs.microsoft.com/oldnewthing/?p=42223 |
| 69 | 109 | const addr = w.VirtualAlloc( |
| 70 | 110 | null, |
| 71 | | n, |
| 111 | alignedLen, |
| 72 | 112 | w.MEM_COMMIT | w.MEM_RESERVE, |
| 73 | 113 | w.PAGE_READWRITE, |
| 74 | 114 | ) catch return error.OutOfMemory; |
| 75 | 115 | |
| 76 | 116 | // If the allocation is sufficiently aligned, use it. |
| 77 | 117 | if (@ptrToInt(addr) & (alignment - 1) == 0) { |
| 78 | | return @ptrCast([*]u8, addr)[0..n]; |
| 118 | return @ptrCast([*]u8, addr)[0..alignPageAllocLen(alignedLen, n, len_align)]; |
| 79 | 119 | } |
| 80 | 120 | |
| 81 | 121 | // If it wasn't, actually do an explicitely aligned allocation. |
| 82 | 122 | w.VirtualFree(addr, 0, w.MEM_RELEASE); |
| 83 | | const alloc_size = n + alignment; |
| 123 | const alloc_size = n + alignment - mem.page_size; |
| 84 | 124 | |
| 85 | | const final_addr = while (true) { |
| 125 | while (true) { |
| 86 | 126 | // Reserve a range of memory large enough to find a sufficiently |
| 87 | 127 | // aligned address. |
| 88 | 128 | const reserved_addr = w.VirtualAlloc( |
| ... | ... | @@ -102,48 +142,50 @@ const PageAllocator = struct { |
| 102 | 142 | // until it succeeds. |
| 103 | 143 | const ptr = w.VirtualAlloc( |
| 104 | 144 | @intToPtr(*c_void, aligned_addr), |
| 105 | | n, |
| 145 | alignedLen, |
| 106 | 146 | w.MEM_COMMIT | w.MEM_RESERVE, |
| 107 | 147 | w.PAGE_READWRITE, |
| 108 | 148 | ) catch continue; |
| 109 | 149 | |
| 110 | | return @ptrCast([*]u8, ptr)[0..n]; |
| 111 | | }; |
| 112 | | |
| 113 | | return @ptrCast([*]u8, final_addr)[0..n]; |
| 150 | return @ptrCast([*]u8, ptr)[0..alignPageAllocLen(alignedLen, n, len_align)]; |
| 151 | } |
| 114 | 152 | } |
| 115 | 153 | |
| 116 | | const alloc_size = if (alignment <= mem.page_size) n else n + alignment; |
| 154 | const maxDropLen = alignment - std.math.min(alignment, mem.page_size); |
| 155 | const allocLen = if (maxDropLen <= alignedLen - n) alignedLen |
| 156 | else mem.alignForward(alignedLen + maxDropLen, mem.page_size); |
| 117 | 157 | const slice = os.mmap( |
| 118 | 158 | null, |
| 119 | | mem.alignForward(alloc_size, mem.page_size), |
| 159 | allocLen, |
| 120 | 160 | os.PROT_READ | os.PROT_WRITE, |
| 121 | 161 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, |
| 122 | 162 | -1, |
| 123 | 163 | 0, |
| 124 | 164 | ) catch return error.OutOfMemory; |
| 125 | | if (alloc_size == n) return slice[0..n]; |
| 165 | assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size)); |
| 126 | 166 | |
| 127 | 167 | const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment); |
| 128 | 168 | |
| 129 | 169 | // Unmap the extra bytes that were only requested in order to guarantee |
| 130 | 170 | // that the range of memory we were provided had a proper alignment in |
| 131 | 171 | // it somewhere. The extra bytes could be at the beginning, or end, or both. |
| 132 | | const unused_start_len = aligned_addr - @ptrToInt(slice.ptr); |
| 133 | | if (unused_start_len != 0) { |
| 134 | | os.munmap(slice[0..unused_start_len]); |
| 172 | const dropLen = aligned_addr - @ptrToInt(slice.ptr); |
| 173 | if (dropLen != 0) { |
| 174 | os.munmap(slice[0..dropLen]); |
| 135 | 175 | } |
| 136 | | const aligned_end_addr = mem.alignForward(aligned_addr + n, mem.page_size); |
| 137 | | const unused_end_len = @ptrToInt(slice.ptr) + slice.len - aligned_end_addr; |
| 138 | | if (unused_end_len != 0) { |
| 139 | | os.munmap(@intToPtr([*]align(mem.page_size) u8, aligned_end_addr)[0..unused_end_len]); |
| 176 | |
| 177 | // Unmap extra pages |
| 178 | const alignedBufferLen = allocLen - dropLen; |
| 179 | if (alignedBufferLen > alignedLen) { |
| 180 | os.munmap(@alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr))[alignedLen..alignedBufferLen]); |
| 140 | 181 | } |
| 141 | 182 | |
| 142 | | return @intToPtr([*]u8, aligned_addr)[0..n]; |
| 183 | return @intToPtr([*]u8, aligned_addr)[0..alignPageAllocLen(alignedLen, n, len_align)]; |
| 143 | 184 | } |
| 144 | 185 | |
| 145 | | fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 146 | | const old_mem = @alignCast(mem.page_size, old_mem_unaligned); |
| 186 | fn resize(allocator: *Allocator, buf_unaligned: []u8, new_size: usize, len_align: u29) Allocator.Error!usize { |
| 187 | const new_size_aligned = mem.alignForward(new_size, mem.page_size); |
| 188 | |
| 147 | 189 | if (builtin.os.tag == .windows) { |
| 148 | 190 | const w = os.windows; |
| 149 | 191 | if (new_size == 0) { |
| ... | ... | @@ -153,100 +195,45 @@ const PageAllocator = struct { |
| 153 | 195 | // is reserved in the initial allocation call to VirtualAlloc." |
| 154 | 196 | // So we can only use MEM_RELEASE when actually releasing the |
| 155 | 197 | // whole allocation. |
| 156 | | w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE); |
| 157 | | } else { |
| 158 | | const base_addr = @ptrToInt(old_mem.ptr); |
| 159 | | const old_addr_end = base_addr + old_mem.len; |
| 160 | | const new_addr_end = base_addr + new_size; |
| 161 | | const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size); |
| 162 | | if (old_addr_end > new_addr_end_rounded) { |
| 198 | w.VirtualFree(buf_unaligned.ptr, 0, w.MEM_RELEASE); |
| 199 | return 0; |
| 200 | } |
| 201 | if (new_size < buf_unaligned.len) { |
| 202 | const base_addr = @ptrToInt(buf_unaligned.ptr); |
| 203 | const old_addr_end = base_addr + buf_unaligned.len; |
| 204 | const new_addr_end = mem.alignForward(base_addr + new_size, mem.page_size); |
| 205 | if (old_addr_end > new_addr_end) { |
| 163 | 206 | // For shrinking that is not releasing, we will only |
| 164 | 207 | // decommit the pages not needed anymore. |
| 165 | 208 | w.VirtualFree( |
| 166 | | @intToPtr(*c_void, new_addr_end_rounded), |
| 167 | | old_addr_end - new_addr_end_rounded, |
| 209 | @intToPtr(*c_void, new_addr_end), |
| 210 | old_addr_end - new_addr_end, |
| 168 | 211 | w.MEM_DECOMMIT, |
| 169 | 212 | ); |
| 170 | 213 | } |
| 214 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 171 | 215 | } |
| 172 | | return old_mem[0..new_size]; |
| 173 | | } |
| 174 | | const base_addr = @ptrToInt(old_mem.ptr); |
| 175 | | const old_addr_end = base_addr + old_mem.len; |
| 176 | | const new_addr_end = base_addr + new_size; |
| 177 | | const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size); |
| 178 | | if (old_addr_end > new_addr_end_rounded) { |
| 179 | | const ptr = @intToPtr([*]align(mem.page_size) u8, new_addr_end_rounded); |
| 180 | | os.munmap(ptr[0 .. old_addr_end - new_addr_end_rounded]); |
| 181 | | } |
| 182 | | return old_mem[0..new_size]; |
| 183 | | } |
| 184 | | |
| 185 | | fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 186 | | const old_mem = @alignCast(mem.page_size, old_mem_unaligned); |
| 187 | | if (builtin.os.tag == .windows) { |
| 188 | | if (old_mem.len == 0) { |
| 189 | | return alloc(allocator, new_size, new_align); |
| 216 | if (new_size == buf_unaligned.len) { |
| 217 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 190 | 218 | } |
| 219 | // new_size > buf_unaligned.len not implemented |
| 220 | return error.OutOfMemory; |
| 221 | } |
| 191 | 222 | |
| 192 | | if (new_size <= old_mem.len and new_align <= old_align) { |
| 193 | | return shrink(allocator, old_mem, old_align, new_size, new_align); |
| 194 | | } |
| 195 | | |
| 196 | | const w = os.windows; |
| 197 | | const base_addr = @ptrToInt(old_mem.ptr); |
| 198 | | |
| 199 | | if (new_align > old_align and base_addr & (new_align - 1) != 0) { |
| 200 | | // Current allocation doesn't satisfy the new alignment. |
| 201 | | // For now we'll do a new one no matter what, but maybe |
| 202 | | // there is something smarter to do instead. |
| 203 | | const result = try alloc(allocator, new_size, new_align); |
| 204 | | assert(old_mem.len != 0); |
| 205 | | @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); |
| 206 | | w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE); |
| 207 | | |
| 208 | | return result; |
| 209 | | } |
| 210 | | |
| 211 | | const old_addr_end = base_addr + old_mem.len; |
| 212 | | const old_addr_end_rounded = mem.alignForward(old_addr_end, mem.page_size); |
| 213 | | const new_addr_end = base_addr + new_size; |
| 214 | | const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size); |
| 215 | | if (new_addr_end_rounded == old_addr_end_rounded) { |
| 216 | | // The reallocation fits in the already allocated pages. |
| 217 | | return @ptrCast([*]u8, old_mem.ptr)[0..new_size]; |
| 218 | | } |
| 219 | | assert(new_addr_end_rounded > old_addr_end_rounded); |
| 220 | | |
| 221 | | // We need to commit new pages. |
| 222 | | const additional_size = new_addr_end - old_addr_end_rounded; |
| 223 | | const realloc_addr = w.kernel32.VirtualAlloc( |
| 224 | | @intToPtr(*c_void, old_addr_end_rounded), |
| 225 | | additional_size, |
| 226 | | w.MEM_COMMIT | w.MEM_RESERVE, |
| 227 | | w.PAGE_READWRITE, |
| 228 | | ) orelse { |
| 229 | | // Committing new pages at the end of the existing allocation |
| 230 | | // failed, we need to try a new one. |
| 231 | | const new_alloc_mem = try alloc(allocator, new_size, new_align); |
| 232 | | @memcpy(new_alloc_mem.ptr, old_mem.ptr, old_mem.len); |
| 233 | | w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE); |
| 234 | | |
| 235 | | return new_alloc_mem; |
| 236 | | }; |
| 223 | const buf_aligned_len = mem.alignForward(buf_unaligned.len, mem.page_size); |
| 224 | if (new_size_aligned == buf_aligned_len) |
| 225 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 237 | 226 | |
| 238 | | assert(@ptrToInt(realloc_addr) == old_addr_end_rounded); |
| 239 | | return @ptrCast([*]u8, old_mem.ptr)[0..new_size]; |
| 240 | | } |
| 241 | | if (new_size <= old_mem.len and new_align <= old_align) { |
| 242 | | return shrink(allocator, old_mem, old_align, new_size, new_align); |
| 243 | | } |
| 244 | | const result = try alloc(allocator, new_size, new_align); |
| 245 | | if (old_mem.len != 0) { |
| 246 | | @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); |
| 247 | | os.munmap(old_mem); |
| 227 | if (new_size_aligned < buf_aligned_len) { |
| 228 | const ptr = @intToPtr([*]align(mem.page_size) u8, @ptrToInt(buf_unaligned.ptr) + new_size_aligned); |
| 229 | os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]); |
| 230 | if (new_size_aligned == 0) |
| 231 | return 0; |
| 232 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 248 | 233 | } |
| 249 | | return result; |
| 234 | |
| 235 | // TODO: call mremap |
| 236 | return error.OutOfMemory; |
| 250 | 237 | } |
| 251 | 238 | }; |
| 252 | 239 | |
| ... | ... | @@ -338,16 +325,24 @@ const WasmPageAllocator = struct { |
| 338 | 325 | } |
| 339 | 326 | |
| 340 | 327 | fn nPages(memsize: usize) usize { |
| 341 | | return std.mem.alignForward(memsize, std.mem.page_size) / std.mem.page_size; |
| 328 | return mem.alignForward(memsize, mem.page_size) / mem.page_size; |
| 342 | 329 | } |
| 343 | 330 | |
| 344 | | fn alloc(allocator: *Allocator, page_count: usize, alignment: u29) error{OutOfMemory}!usize { |
| 345 | | var idx = conventional.useRecycled(page_count); |
| 346 | | if (idx != FreeBlock.not_found) { |
| 347 | | return idx; |
| 331 | fn alloc(allocator: *Allocator, len: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 { |
| 332 | const page_count = nPages(len); |
| 333 | const page_idx = try allocPages(page_count); |
| 334 | return @intToPtr([*]u8, page_idx * mem.page_size) |
| 335 | [0..alignPageAllocLen(page_count * mem.page_size, len, len_align)]; |
| 336 | } |
| 337 | fn allocPages(page_count: usize) !usize { |
| 338 | { |
| 339 | const idx = conventional.useRecycled(page_count); |
| 340 | if (idx != FreeBlock.not_found) { |
| 341 | return idx; |
| 342 | } |
| 348 | 343 | } |
| 349 | 344 | |
| 350 | | idx = extended.useRecycled(page_count); |
| 345 | const idx = extended.useRecycled(page_count); |
| 351 | 346 | if (idx != FreeBlock.not_found) { |
| 352 | 347 | return idx + extendedOffset(); |
| 353 | 348 | } |
| ... | ... | @@ -360,51 +355,36 @@ const WasmPageAllocator = struct { |
| 360 | 355 | return @intCast(usize, prev_page_count); |
| 361 | 356 | } |
| 362 | 357 | |
| 363 | | pub fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) Allocator.Error![]u8 { |
| 364 | | if (new_align > std.mem.page_size) { |
| 365 | | return error.OutOfMemory; |
| 358 | fn freePages(start: usize, end: usize) void { |
| 359 | if (start < extendedOffset()) { |
| 360 | conventional.recycle(start, std.math.min(extendedOffset(), end) - start); |
| 366 | 361 | } |
| 367 | | |
| 368 | | if (nPages(new_size) == nPages(old_mem.len)) { |
| 369 | | return old_mem.ptr[0..new_size]; |
| 370 | | } else if (new_size < old_mem.len) { |
| 371 | | return shrink(allocator, old_mem, old_align, new_size, new_align); |
| 372 | | } else { |
| 373 | | const page_idx = try alloc(allocator, nPages(new_size), new_align); |
| 374 | | const new_mem = @intToPtr([*]u8, page_idx * std.mem.page_size)[0..new_size]; |
| 375 | | std.mem.copy(u8, new_mem, old_mem); |
| 376 | | _ = shrink(allocator, old_mem, old_align, 0, 0); |
| 377 | | return new_mem; |
| 362 | if (end > extendedOffset()) { |
| 363 | var new_end = end; |
| 364 | if (!extended.isInitialized()) { |
| 365 | // Steal the last page from the memory currently being recycled |
| 366 | // TODO: would it be better if we use the first page instead? |
| 367 | new_end -= 1; |
| 368 | |
| 369 | extended.data = @intToPtr([*]u128, new_end * mem.page_size)[0 .. mem.page_size / @sizeOf(u128)]; |
| 370 | // Since this is the first page being freed and we consume it, assume *nothing* is free. |
| 371 | mem.set(u128, extended.data, PageStatus.none_free); |
| 372 | } |
| 373 | const clamped_start = std.math.max(extendedOffset(), start); |
| 374 | extended.recycle(clamped_start - extendedOffset(), new_end - clamped_start); |
| 378 | 375 | } |
| 379 | 376 | } |
| 380 | 377 | |
| 381 | | pub fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 382 | | @setCold(true); |
| 383 | | const free_start = nPages(@ptrToInt(old_mem.ptr) + new_size); |
| 384 | | var free_end = nPages(@ptrToInt(old_mem.ptr) + old_mem.len); |
| 385 | | |
| 386 | | if (free_end > free_start) { |
| 387 | | if (free_start < extendedOffset()) { |
| 388 | | const clamped_end = std.math.min(extendedOffset(), free_end); |
| 389 | | conventional.recycle(free_start, clamped_end - free_start); |
| 390 | | } |
| 391 | | |
| 392 | | if (free_end > extendedOffset()) { |
| 393 | | if (!extended.isInitialized()) { |
| 394 | | // Steal the last page from the memory currently being recycled |
| 395 | | // TODO: would it be better if we use the first page instead? |
| 396 | | free_end -= 1; |
| 397 | | |
| 398 | | extended.data = @intToPtr([*]u128, free_end * std.mem.page_size)[0 .. std.mem.page_size / @sizeOf(u128)]; |
| 399 | | // Since this is the first page being freed and we consume it, assume *nothing* is free. |
| 400 | | std.mem.set(u128, extended.data, PageStatus.none_free); |
| 401 | | } |
| 402 | | const clamped_start = std.math.max(extendedOffset(), free_start); |
| 403 | | extended.recycle(clamped_start - extendedOffset(), free_end - clamped_start); |
| 404 | | } |
| 378 | fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize { |
| 379 | const aligned_len = mem.alignForward(buf.len, mem.page_size); |
| 380 | if (new_len > aligned_len) return error.OutOfMemory; |
| 381 | const current_n = nPages(aligned_len); |
| 382 | const new_n = nPages(new_len); |
| 383 | if (new_n != current_n) { |
| 384 | const base = nPages(@ptrToInt(buf.ptr)); |
| 385 | freePages(base + new_n, base + current_n); |
| 405 | 386 | } |
| 406 | | |
| 407 | | return old_mem[0..new_size]; |
| 387 | return if (new_len == 0) 0 else alignPageAllocLen(new_n * mem.page_size, new_len, len_align); |
| 408 | 388 | } |
| 409 | 389 | }; |
| 410 | 390 | |
| ... | ... | @@ -418,8 +398,8 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 418 | 398 | pub fn init() HeapAllocator { |
| 419 | 399 | return HeapAllocator{ |
| 420 | 400 | .allocator = Allocator{ |
| 421 | | .reallocFn = realloc, |
| 422 | | .shrinkFn = shrink, |
| 401 | .allocFn = alloc, |
| 402 | .resizeFn = resize, |
| 423 | 403 | }, |
| 424 | 404 | .heap_handle = null, |
| 425 | 405 | }; |
| ... | ... | @@ -431,11 +411,14 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 431 | 411 | } |
| 432 | 412 | } |
| 433 | 413 | |
| 434 | | fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { |
| 414 | fn getRecordPtr(buf: []u8) *align(1) usize { |
| 415 | return @intToPtr(*align(1) usize, @ptrToInt(buf.ptr) + buf.len); |
| 416 | } |
| 417 | |
| 418 | fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 { |
| 435 | 419 | const self = @fieldParentPtr(HeapAllocator, "allocator", allocator); |
| 436 | | if (n == 0) return &[0]u8{}; |
| 437 | 420 | |
| 438 | | const amt = n + alignment + @sizeOf(usize); |
| 421 | const amt = n + ptr_align - 1 + @sizeOf(usize); |
| 439 | 422 | const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst); |
| 440 | 423 | const heap_handle = optional_heap_handle orelse blk: { |
| 441 | 424 | const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0; |
| ... | ... | @@ -446,66 +429,60 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 446 | 429 | }; |
| 447 | 430 | const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; |
| 448 | 431 | const root_addr = @ptrToInt(ptr); |
| 449 | | const adjusted_addr = mem.alignForward(root_addr, alignment); |
| 450 | | const record_addr = adjusted_addr + n; |
| 451 | | @intToPtr(*align(1) usize, record_addr).* = root_addr; |
| 452 | | return @intToPtr([*]u8, adjusted_addr)[0..n]; |
| 453 | | } |
| 454 | | |
| 455 | | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 456 | | return realloc(allocator, old_mem, old_align, new_size, new_align) catch { |
| 457 | | const old_adjusted_addr = @ptrToInt(old_mem.ptr); |
| 458 | | const old_record_addr = old_adjusted_addr + old_mem.len; |
| 459 | | const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; |
| 460 | | const old_ptr = @intToPtr(*c_void, root_addr); |
| 461 | | const new_record_addr = old_record_addr - new_size + old_mem.len; |
| 462 | | @intToPtr(*align(1) usize, new_record_addr).* = root_addr; |
| 463 | | return old_mem[0..new_size]; |
| 432 | const aligned_addr = mem.alignForward(root_addr, ptr_align); |
| 433 | const return_len = init: { |
| 434 | if (len_align == 0) break :init n; |
| 435 | const full_len = os.windows.kernel32.HeapSize(heap_handle, 0, ptr); |
| 436 | assert(full_len != std.math.maxInt(usize)); |
| 437 | assert(full_len >= amt); |
| 438 | break :init mem.alignBackwardAnyAlign(full_len - (aligned_addr - root_addr), len_align); |
| 464 | 439 | }; |
| 440 | const buf = @intToPtr([*]u8, aligned_addr)[0..return_len]; |
| 441 | getRecordPtr(buf).* = root_addr; |
| 442 | return buf; |
| 465 | 443 | } |
| 466 | 444 | |
| 467 | | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 468 | | if (old_mem.len == 0) return alloc(allocator, new_size, new_align); |
| 469 | | |
| 445 | fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) error{OutOfMemory}!usize { |
| 470 | 446 | const self = @fieldParentPtr(HeapAllocator, "allocator", allocator); |
| 471 | | const old_adjusted_addr = @ptrToInt(old_mem.ptr); |
| 472 | | const old_record_addr = old_adjusted_addr + old_mem.len; |
| 473 | | const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; |
| 474 | | const old_ptr = @intToPtr(*c_void, root_addr); |
| 475 | | |
| 476 | 447 | if (new_size == 0) { |
| 477 | | os.windows.HeapFree(self.heap_handle.?, 0, old_ptr); |
| 478 | | return old_mem[0..0]; |
| 448 | os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*c_void ,getRecordPtr(buf).*)); |
| 449 | return 0; |
| 479 | 450 | } |
| 480 | 451 | |
| 481 | | const amt = new_size + new_align + @sizeOf(usize); |
| 452 | const root_addr = getRecordPtr(buf).*; |
| 453 | const align_offset = @ptrToInt(buf.ptr) - root_addr; |
| 454 | const amt = align_offset + new_size + @sizeOf(usize); |
| 482 | 455 | const new_ptr = os.windows.kernel32.HeapReAlloc( |
| 483 | 456 | self.heap_handle.?, |
| 484 | | 0, |
| 485 | | old_ptr, |
| 457 | os.windows.HEAP_REALLOC_IN_PLACE_ONLY, |
| 458 | @intToPtr(*c_void, root_addr), |
| 486 | 459 | amt, |
| 487 | 460 | ) orelse return error.OutOfMemory; |
| 488 | | const offset = old_adjusted_addr - root_addr; |
| 489 | | const new_root_addr = @ptrToInt(new_ptr); |
| 490 | | var new_adjusted_addr = new_root_addr + offset; |
| 491 | | const offset_is_valid = new_adjusted_addr + new_size + @sizeOf(usize) <= new_root_addr + amt; |
| 492 | | const offset_is_aligned = new_adjusted_addr % new_align == 0; |
| 493 | | if (!offset_is_valid or !offset_is_aligned) { |
| 494 | | // If HeapReAlloc didn't happen to move the memory to the new alignment, |
| 495 | | // or the memory starting at the old offset would be outside of the new allocation, |
| 496 | | // then we need to copy the memory to a valid aligned address and use that |
| 497 | | const new_aligned_addr = mem.alignForward(new_root_addr, new_align); |
| 498 | | @memcpy(@intToPtr([*]u8, new_aligned_addr), @intToPtr([*]u8, new_adjusted_addr), std.math.min(old_mem.len, new_size)); |
| 499 | | new_adjusted_addr = new_aligned_addr; |
| 500 | | } |
| 501 | | const new_record_addr = new_adjusted_addr + new_size; |
| 502 | | @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr; |
| 503 | | return @intToPtr([*]u8, new_adjusted_addr)[0..new_size]; |
| 461 | assert(new_ptr == @intToPtr(*c_void, root_addr)); |
| 462 | const return_len = init: { |
| 463 | if (len_align == 0) break :init new_size; |
| 464 | const full_len = os.windows.kernel32.HeapSize(self.heap_handle.?, 0, new_ptr); |
| 465 | assert(full_len != std.math.maxInt(usize)); |
| 466 | assert(full_len >= amt); |
| 467 | break :init mem.alignBackwardAnyAlign(full_len - align_offset, len_align); |
| 468 | }; |
| 469 | getRecordPtr(buf.ptr[0..return_len]).* = root_addr; |
| 470 | return return_len; |
| 504 | 471 | } |
| 505 | 472 | }, |
| 506 | 473 | else => @compileError("Unsupported OS"), |
| 507 | 474 | }; |
| 508 | 475 | |
| 476 | fn sliceContainsPtr(container: []u8, ptr: [*]u8) bool { |
| 477 | return @ptrToInt(ptr) >= @ptrToInt(container.ptr) and |
| 478 | @ptrToInt(ptr) < (@ptrToInt(container.ptr) + container.len); |
| 479 | } |
| 480 | |
| 481 | fn sliceContainsSlice(container: []u8, slice: []u8) bool { |
| 482 | return @ptrToInt(slice.ptr) >= @ptrToInt(container.ptr) and |
| 483 | (@ptrToInt(slice.ptr) + slice.len) <= (@ptrToInt(container.ptr) + container.len); |
| 484 | } |
| 485 | |
| 509 | 486 | pub const FixedBufferAllocator = struct { |
| 510 | 487 | allocator: Allocator, |
| 511 | 488 | end_index: usize, |
| ... | ... | @@ -514,19 +491,33 @@ pub const FixedBufferAllocator = struct { |
| 514 | 491 | pub fn init(buffer: []u8) FixedBufferAllocator { |
| 515 | 492 | return FixedBufferAllocator{ |
| 516 | 493 | .allocator = Allocator{ |
| 517 | | .reallocFn = realloc, |
| 518 | | .shrinkFn = shrink, |
| 494 | .allocFn = alloc, |
| 495 | .resizeFn = resize, |
| 519 | 496 | }, |
| 520 | 497 | .buffer = buffer, |
| 521 | 498 | .end_index = 0, |
| 522 | 499 | }; |
| 523 | 500 | } |
| 524 | 501 | |
| 525 | | fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { |
| 502 | pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool { |
| 503 | return sliceContainsPtr(self.buffer, ptr); |
| 504 | } |
| 505 | |
| 506 | pub fn ownsSlice(self: *FixedBufferAllocator, slice: []u8) bool { |
| 507 | return sliceContainsSlice(self.buffer, slice); |
| 508 | } |
| 509 | |
| 510 | // NOTE: this will not work in all cases, if the last allocation had an adjusted_index |
| 511 | // then we won't be able to determine what the last allocation was. This is because |
| 512 | // the alignForward operation done in alloc is not reverisible. |
| 513 | pub fn isLastAllocation(self: *FixedBufferAllocator, buf: []u8) bool { |
| 514 | return buf.ptr + buf.len == self.buffer.ptr + self.end_index; |
| 515 | } |
| 516 | |
| 517 | fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 { |
| 526 | 518 | const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); |
| 527 | | const addr = @ptrToInt(self.buffer.ptr) + self.end_index; |
| 528 | | const adjusted_addr = mem.alignForward(addr, alignment); |
| 529 | | const adjusted_index = self.end_index + (adjusted_addr - addr); |
| 519 | const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align); |
| 520 | const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr); |
| 530 | 521 | const new_end_index = adjusted_index + n; |
| 531 | 522 | if (new_end_index > self.buffer.len) { |
| 532 | 523 | return error.OutOfMemory; |
| ... | ... | @@ -534,33 +525,32 @@ pub const FixedBufferAllocator = struct { |
| 534 | 525 | const result = self.buffer[adjusted_index..new_end_index]; |
| 535 | 526 | self.end_index = new_end_index; |
| 536 | 527 | |
| 537 | | return result; |
| 528 | return result[0..mem.alignAllocLen(result.len, n, len_align)]; |
| 538 | 529 | } |
| 539 | 530 | |
| 540 | | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 531 | fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) Allocator.Error!usize { |
| 541 | 532 | const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); |
| 542 | | assert(old_mem.len <= self.end_index); |
| 543 | | if (old_mem.ptr == self.buffer.ptr + self.end_index - old_mem.len and |
| 544 | | mem.alignForward(@ptrToInt(old_mem.ptr), new_align) == @ptrToInt(old_mem.ptr)) |
| 545 | | { |
| 546 | | const start_index = self.end_index - old_mem.len; |
| 547 | | const new_end_index = start_index + new_size; |
| 548 | | if (new_end_index > self.buffer.len) return error.OutOfMemory; |
| 549 | | const result = self.buffer[start_index..new_end_index]; |
| 550 | | self.end_index = new_end_index; |
| 551 | | return result; |
| 552 | | } else if (new_size <= old_mem.len and new_align <= old_align) { |
| 553 | | // We can't do anything with the memory, so tell the client to keep it. |
| 554 | | return error.OutOfMemory; |
| 555 | | } else { |
| 556 | | const result = try alloc(allocator, new_size, new_align); |
| 557 | | @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); |
| 558 | | return result; |
| 533 | assert(self.ownsSlice(buf)); // sanity check |
| 534 | |
| 535 | if (!self.isLastAllocation(buf)) { |
| 536 | if (new_size > buf.len) |
| 537 | return error.OutOfMemory; |
| 538 | return if (new_size == 0) 0 else mem.alignAllocLen(buf.len, new_size, len_align); |
| 559 | 539 | } |
| 560 | | } |
| 561 | 540 | |
| 562 | | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 563 | | return old_mem[0..new_size]; |
| 541 | if (new_size <= buf.len) { |
| 542 | const sub = buf.len - new_size; |
| 543 | self.end_index -= sub; |
| 544 | return if (new_size == 0) 0 else mem.alignAllocLen(buf.len - sub, new_size, len_align); |
| 545 | } |
| 546 | |
| 547 | var add = new_size - buf.len; |
| 548 | if (add + self.end_index > self.buffer.len) { |
| 549 | //add = self.buffer.len - self.end_index; |
| 550 | return error.OutOfMemory; |
| 551 | } |
| 552 | self.end_index += add; |
| 553 | return mem.alignAllocLen(buf.len + add, new_size, len_align); |
| 564 | 554 | } |
| 565 | 555 | |
| 566 | 556 | pub fn reset(self: *FixedBufferAllocator) void { |
| ... | ... | @@ -581,20 +571,20 @@ pub const ThreadSafeFixedBufferAllocator = blk: { |
| 581 | 571 | pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { |
| 582 | 572 | return ThreadSafeFixedBufferAllocator{ |
| 583 | 573 | .allocator = Allocator{ |
| 584 | | .reallocFn = realloc, |
| 585 | | .shrinkFn = shrink, |
| 574 | .allocFn = alloc, |
| 575 | .resizeFn = Allocator.noResize, |
| 586 | 576 | }, |
| 587 | 577 | .buffer = buffer, |
| 588 | 578 | .end_index = 0, |
| 589 | 579 | }; |
| 590 | 580 | } |
| 591 | 581 | |
| 592 | | fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { |
| 582 | fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 { |
| 593 | 583 | const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); |
| 594 | 584 | var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); |
| 595 | 585 | while (true) { |
| 596 | 586 | const addr = @ptrToInt(self.buffer.ptr) + end_index; |
| 597 | | const adjusted_addr = mem.alignForward(addr, alignment); |
| 587 | const adjusted_addr = mem.alignForward(addr, ptr_align); |
| 598 | 588 | const adjusted_index = end_index + (adjusted_addr - addr); |
| 599 | 589 | const new_end_index = adjusted_index + n; |
| 600 | 590 | if (new_end_index > self.buffer.len) { |
| ... | ... | @@ -604,21 +594,6 @@ pub const ThreadSafeFixedBufferAllocator = blk: { |
| 604 | 594 | } |
| 605 | 595 | } |
| 606 | 596 | |
| 607 | | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 608 | | if (new_size <= old_mem.len and new_align <= old_align) { |
| 609 | | // We can't do anything useful with the memory, tell the client to keep it. |
| 610 | | return error.OutOfMemory; |
| 611 | | } else { |
| 612 | | const result = try alloc(allocator, new_size, new_align); |
| 613 | | @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len)); |
| 614 | | return result; |
| 615 | | } |
| 616 | | } |
| 617 | | |
| 618 | | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 619 | | return old_mem[0..new_size]; |
| 620 | | } |
| 621 | | |
| 622 | 597 | pub fn reset(self: *ThreadSafeFixedBufferAllocator) void { |
| 623 | 598 | self.end_index = 0; |
| 624 | 599 | } |
| ... | ... | @@ -632,8 +607,8 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack |
| 632 | 607 | .fallback_allocator = fallback_allocator, |
| 633 | 608 | .fixed_buffer_allocator = undefined, |
| 634 | 609 | .allocator = Allocator{ |
| 635 | | .reallocFn = StackFallbackAllocator(size).realloc, |
| 636 | | .shrinkFn = StackFallbackAllocator(size).shrink, |
| 610 | .allocFn = StackFallbackAllocator(size).realloc, |
| 611 | .resizeFn = StackFallbackAllocator(size).resize, |
| 637 | 612 | }, |
| 638 | 613 | }; |
| 639 | 614 | } |
| ... | ... | @@ -652,58 +627,19 @@ pub fn StackFallbackAllocator(comptime size: usize) type { |
| 652 | 627 | return &self.allocator; |
| 653 | 628 | } |
| 654 | 629 | |
| 655 | | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 630 | fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![*]u8 { |
| 656 | 631 | const self = @fieldParentPtr(Self, "allocator", allocator); |
| 657 | | const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and |
| 658 | | @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len; |
| 659 | | if (in_buffer) { |
| 660 | | return FixedBufferAllocator.realloc( |
| 661 | | &self.fixed_buffer_allocator.allocator, |
| 662 | | old_mem, |
| 663 | | old_align, |
| 664 | | new_size, |
| 665 | | new_align, |
| 666 | | ) catch { |
| 667 | | const result = try self.fallback_allocator.reallocFn( |
| 668 | | self.fallback_allocator, |
| 669 | | &[0]u8{}, |
| 670 | | undefined, |
| 671 | | new_size, |
| 672 | | new_align, |
| 673 | | ); |
| 674 | | mem.copy(u8, result, old_mem); |
| 675 | | return result; |
| 676 | | }; |
| 677 | | } |
| 678 | | return self.fallback_allocator.reallocFn( |
| 679 | | self.fallback_allocator, |
| 680 | | old_mem, |
| 681 | | old_align, |
| 682 | | new_size, |
| 683 | | new_align, |
| 684 | | ); |
| 632 | return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch |
| 633 | return fallback_allocator.alloc(len, ptr_align); |
| 685 | 634 | } |
| 686 | 635 | |
| 687 | | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 636 | fn resize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!void { |
| 688 | 637 | const self = @fieldParentPtr(Self, "allocator", allocator); |
| 689 | | const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and |
| 690 | | @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len; |
| 691 | | if (in_buffer) { |
| 692 | | return FixedBufferAllocator.shrink( |
| 693 | | &self.fixed_buffer_allocator.allocator, |
| 694 | | old_mem, |
| 695 | | old_align, |
| 696 | | new_size, |
| 697 | | new_align, |
| 698 | | ); |
| 638 | if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) { |
| 639 | try self.fixed_buffer_allocator.callResizeFn(buf, new_len); |
| 640 | } else { |
| 641 | try self.fallback_allocator.callResizeFn(buf, new_len); |
| 699 | 642 | } |
| 700 | | return self.fallback_allocator.shrinkFn( |
| 701 | | self.fallback_allocator, |
| 702 | | old_mem, |
| 703 | | old_align, |
| 704 | | new_size, |
| 705 | | new_align, |
| 706 | | ); |
| 707 | 643 | } |
| 708 | 644 | }; |
| 709 | 645 | } |
| ... | ... | @@ -718,8 +654,8 @@ test "c_allocator" { |
| 718 | 654 | |
| 719 | 655 | test "WasmPageAllocator internals" { |
| 720 | 656 | if (comptime std.Target.current.isWasm()) { |
| 721 | | const conventional_memsize = WasmPageAllocator.conventional.totalPages() * std.mem.page_size; |
| 722 | | const initial = try page_allocator.alloc(u8, std.mem.page_size); |
| 657 | const conventional_memsize = WasmPageAllocator.conventional.totalPages() * mem.page_size; |
| 658 | const initial = try page_allocator.alloc(u8, mem.page_size); |
| 723 | 659 | std.debug.assert(@ptrToInt(initial.ptr) < conventional_memsize); // If this isn't conventional, the rest of these tests don't make sense. Also we have a serious memory leak in the test suite. |
| 724 | 660 | |
| 725 | 661 | var inplace = try page_allocator.realloc(initial, 1); |
| ... | ... | @@ -799,7 +735,7 @@ test "ArenaAllocator" { |
| 799 | 735 | |
| 800 | 736 | var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined; |
| 801 | 737 | test "FixedBufferAllocator" { |
| 802 | | var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); |
| 738 | var fixed_buffer_allocator = mem.sanityWrap(FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..])); |
| 803 | 739 | |
| 804 | 740 | try testAllocator(&fixed_buffer_allocator.allocator); |
| 805 | 741 | try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16); |
| ... | ... | @@ -865,7 +801,10 @@ test "ThreadSafeFixedBufferAllocator" { |
| 865 | 801 | try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator); |
| 866 | 802 | } |
| 867 | 803 | |
| 868 | | fn testAllocator(allocator: *mem.Allocator) !void { |
| 804 | fn testAllocator(base_allocator: *mem.Allocator) !void { |
| 805 | var sanityAllocator = mem.sanityWrap(base_allocator); |
| 806 | const allocator = &sanityAllocator.allocator; |
| 807 | |
| 869 | 808 | var slice = try allocator.alloc(*i32, 100); |
| 870 | 809 | testing.expect(slice.len == 100); |
| 871 | 810 | for (slice) |*item, i| { |
| ... | ... | @@ -893,7 +832,10 @@ fn testAllocator(allocator: *mem.Allocator) !void { |
| 893 | 832 | allocator.free(slice); |
| 894 | 833 | } |
| 895 | 834 | |
| 896 | | fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void { |
| 835 | fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void { |
| 836 | var sanityAllocator = mem.sanityWrap(base_allocator); |
| 837 | const allocator = &sanityAllocator.allocator; |
| 838 | |
| 897 | 839 | // initial |
| 898 | 840 | var slice = try allocator.alignedAlloc(u8, alignment, 10); |
| 899 | 841 | testing.expect(slice.len == 10); |
| ... | ... | @@ -917,7 +859,10 @@ fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !voi |
| 917 | 859 | testing.expect(slice.len == 0); |
| 918 | 860 | } |
| 919 | 861 | |
| 920 | | fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void { |
| 862 | fn testAllocatorLargeAlignment(base_allocator: *mem.Allocator) mem.Allocator.Error!void { |
| 863 | var sanityAllocator = mem.sanityWrap(base_allocator); |
| 864 | const allocator = &sanityAllocator.allocator; |
| 865 | |
| 921 | 866 | //Maybe a platform's page_size is actually the same as or |
| 922 | 867 | // very near usize? |
| 923 | 868 | if (mem.page_size << 2 > maxInt(usize)) return; |
| ... | ... | @@ -946,7 +891,10 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo |
| 946 | 891 | allocator.free(slice); |
| 947 | 892 | } |
| 948 | 893 | |
| 949 | | fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!void { |
| 894 | fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) mem.Allocator.Error!void { |
| 895 | var sanityAllocator = mem.sanityWrap(base_allocator); |
| 896 | const allocator = &sanityAllocator.allocator; |
| 897 | |
| 950 | 898 | var debug_buffer: [1000]u8 = undefined; |
| 951 | 899 | const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator; |
| 952 | 900 | |