| ... | ... | @@ -15,23 +15,54 @@ pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator; |
| 15 | 15 | |
| 16 | 16 | const Allocator = mem.Allocator; |
| 17 | 17 | |
| 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 | |
| 18 | 28 | pub const c_allocator = &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 (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 (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 | return error.OutOfMemory; |
| 35 | 66 | } |
| 36 | 67 | |
| 37 | 68 | /// This allocator makes a syscall directly for every allocation and free. |
| ... | ... | @@ -44,19 +75,27 @@ else |
| 44 | 75 | &page_allocator_state; |
| 45 | 76 | |
| 46 | 77 | var page_allocator_state = Allocator{ |
| 47 | | .reallocFn = PageAllocator.realloc, |
| 48 | | .shrinkFn = PageAllocator.shrink, |
| 78 | .allocFn = PageAllocator.alloc, |
| 79 | .resizeFn = PageAllocator.resize, |
| 49 | 80 | }; |
| 50 | 81 | var wasm_page_allocator_state = Allocator{ |
| 51 | | .reallocFn = WasmPageAllocator.realloc, |
| 52 | | .shrinkFn = WasmPageAllocator.shrink, |
| 82 | .allocFn = WasmPageAllocator.alloc, |
| 83 | .resizeFn = WasmPageAllocator.resize, |
| 53 | 84 | }; |
| 54 | 85 | |
| 55 | 86 | pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator"); |
| 56 | 87 | |
| 88 | /// Verifies that the adjusted length will still map to the full length |
| 89 | pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize { |
| 90 | const aligned_len = mem.alignAllocLen(full_len, len, len_align); |
| 91 | assert(mem.alignForward(aligned_len, mem.page_size) == full_len); |
| 92 | return aligned_len; |
| 93 | } |
| 94 | |
| 57 | 95 | const PageAllocator = struct { |
| 58 | | fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { |
| 59 | | if (n == 0) return &[0]u8{}; |
| 96 | fn alloc(allocator: *Allocator, n: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 { |
| 97 | assert(n > 0); |
| 98 | const alignedLen = mem.alignForward(n, mem.page_size); |
| 60 | 99 | |
| 61 | 100 | if (builtin.os.tag == .windows) { |
| 62 | 101 | const w = os.windows; |
| ... | ... | @@ -68,21 +107,21 @@ const PageAllocator = struct { |
| 68 | 107 | // see https://devblogs.microsoft.com/oldnewthing/?p=42223 |
| 69 | 108 | const addr = w.VirtualAlloc( |
| 70 | 109 | null, |
| 71 | | n, |
| 110 | alignedLen, |
| 72 | 111 | w.MEM_COMMIT | w.MEM_RESERVE, |
| 73 | 112 | w.PAGE_READWRITE, |
| 74 | 113 | ) catch return error.OutOfMemory; |
| 75 | 114 | |
| 76 | 115 | // If the allocation is sufficiently aligned, use it. |
| 77 | 116 | if (@ptrToInt(addr) & (alignment - 1) == 0) { |
| 78 | | return @ptrCast([*]u8, addr)[0..n]; |
| 117 | return @ptrCast([*]u8, addr)[0..alignPageAllocLen(alignedLen, n, len_align)]; |
| 79 | 118 | } |
| 80 | 119 | |
| 81 | 120 | // If it wasn't, actually do an explicitely aligned allocation. |
| 82 | 121 | w.VirtualFree(addr, 0, w.MEM_RELEASE); |
| 83 | | const alloc_size = n + alignment; |
| 122 | const alloc_size = n + alignment - mem.page_size; |
| 84 | 123 | |
| 85 | | const final_addr = while (true) { |
| 124 | while (true) { |
| 86 | 125 | // Reserve a range of memory large enough to find a sufficiently |
| 87 | 126 | // aligned address. |
| 88 | 127 | const reserved_addr = w.VirtualAlloc( |
| ... | ... | @@ -102,48 +141,50 @@ const PageAllocator = struct { |
| 102 | 141 | // until it succeeds. |
| 103 | 142 | const ptr = w.VirtualAlloc( |
| 104 | 143 | @intToPtr(*c_void, aligned_addr), |
| 105 | | n, |
| 144 | alignedLen, |
| 106 | 145 | w.MEM_COMMIT | w.MEM_RESERVE, |
| 107 | 146 | w.PAGE_READWRITE, |
| 108 | 147 | ) catch continue; |
| 109 | 148 | |
| 110 | | return @ptrCast([*]u8, ptr)[0..n]; |
| 111 | | }; |
| 112 | | |
| 113 | | return @ptrCast([*]u8, final_addr)[0..n]; |
| 149 | return @ptrCast([*]u8, ptr)[0..alignPageAllocLen(alignedLen, n, len_align)]; |
| 150 | } |
| 114 | 151 | } |
| 115 | 152 | |
| 116 | | const alloc_size = if (alignment <= mem.page_size) n else n + alignment; |
| 153 | const maxDropLen = alignment - std.math.min(alignment, mem.page_size); |
| 154 | const allocLen = if (maxDropLen <= alignedLen - n) alignedLen |
| 155 | else mem.alignForward(alignedLen + maxDropLen, mem.page_size); |
| 117 | 156 | const slice = os.mmap( |
| 118 | 157 | null, |
| 119 | | mem.alignForward(alloc_size, mem.page_size), |
| 158 | allocLen, |
| 120 | 159 | os.PROT_READ | os.PROT_WRITE, |
| 121 | 160 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, |
| 122 | 161 | -1, |
| 123 | 162 | 0, |
| 124 | 163 | ) catch return error.OutOfMemory; |
| 125 | | if (alloc_size == n) return slice[0..n]; |
| 164 | assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size)); |
| 126 | 165 | |
| 127 | 166 | const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment); |
| 128 | 167 | |
| 129 | 168 | // Unmap the extra bytes that were only requested in order to guarantee |
| 130 | 169 | // that the range of memory we were provided had a proper alignment in |
| 131 | 170 | // 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]); |
| 171 | const dropLen = aligned_addr - @ptrToInt(slice.ptr); |
| 172 | if (dropLen != 0) { |
| 173 | os.munmap(slice[0..dropLen]); |
| 135 | 174 | } |
| 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]); |
| 175 | |
| 176 | // Unmap extra pages |
| 177 | const alignedBufferLen = allocLen - dropLen; |
| 178 | if (alignedBufferLen > alignedLen) { |
| 179 | os.munmap(@alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr))[alignedLen..alignedBufferLen]); |
| 140 | 180 | } |
| 141 | 181 | |
| 142 | | return @intToPtr([*]u8, aligned_addr)[0..n]; |
| 182 | return @intToPtr([*]u8, aligned_addr)[0..alignPageAllocLen(alignedLen, n, len_align)]; |
| 143 | 183 | } |
| 144 | 184 | |
| 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); |
| 185 | fn resize(allocator: *Allocator, buf_unaligned: []u8, new_size: usize, len_align: u29) Allocator.Error!usize { |
| 186 | const new_size_aligned = mem.alignForward(new_size, mem.page_size); |
| 187 | |
| 147 | 188 | if (builtin.os.tag == .windows) { |
| 148 | 189 | const w = os.windows; |
| 149 | 190 | if (new_size == 0) { |
| ... | ... | @@ -153,100 +194,45 @@ const PageAllocator = struct { |
| 153 | 194 | // is reserved in the initial allocation call to VirtualAlloc." |
| 154 | 195 | // So we can only use MEM_RELEASE when actually releasing the |
| 155 | 196 | // 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) { |
| 197 | w.VirtualFree(buf_unaligned.ptr, 0, w.MEM_RELEASE); |
| 198 | return 0; |
| 199 | } |
| 200 | if (new_size < buf_unaligned.len) { |
| 201 | const base_addr = @ptrToInt(buf_unaligned.ptr); |
| 202 | const old_addr_end = base_addr + buf_unaligned.len; |
| 203 | const new_addr_end = mem.alignForward(base_addr + new_size, mem.page_size); |
| 204 | if (old_addr_end > new_addr_end) { |
| 163 | 205 | // For shrinking that is not releasing, we will only |
| 164 | 206 | // decommit the pages not needed anymore. |
| 165 | 207 | w.VirtualFree( |
| 166 | | @intToPtr(*c_void, new_addr_end_rounded), |
| 167 | | old_addr_end - new_addr_end_rounded, |
| 208 | @intToPtr(*c_void, new_addr_end), |
| 209 | old_addr_end - new_addr_end, |
| 168 | 210 | w.MEM_DECOMMIT, |
| 169 | 211 | ); |
| 170 | 212 | } |
| 213 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 171 | 214 | } |
| 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); |
| 215 | if (new_size == buf_unaligned.len) { |
| 216 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 190 | 217 | } |
| 218 | // new_size > buf_unaligned.len not implemented |
| 219 | return error.OutOfMemory; |
| 220 | } |
| 191 | 221 | |
| 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 | | }; |
| 222 | const buf_aligned_len = mem.alignForward(buf_unaligned.len, mem.page_size); |
| 223 | if (new_size_aligned == buf_aligned_len) |
| 224 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 237 | 225 | |
| 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); |
| 226 | if (new_size_aligned < buf_aligned_len) { |
| 227 | const ptr = @intToPtr([*]align(mem.page_size) u8, @ptrToInt(buf_unaligned.ptr) + new_size_aligned); |
| 228 | os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]); |
| 229 | if (new_size_aligned == 0) |
| 230 | return 0; |
| 231 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 248 | 232 | } |
| 249 | | return result; |
| 233 | |
| 234 | // TODO: call mremap |
| 235 | return error.OutOfMemory; |
| 250 | 236 | } |
| 251 | 237 | }; |
| 252 | 238 | |
| ... | ... | @@ -338,16 +324,24 @@ const WasmPageAllocator = struct { |
| 338 | 324 | } |
| 339 | 325 | |
| 340 | 326 | fn nPages(memsize: usize) usize { |
| 341 | | return std.mem.alignForward(memsize, std.mem.page_size) / std.mem.page_size; |
| 327 | return mem.alignForward(memsize, mem.page_size) / mem.page_size; |
| 342 | 328 | } |
| 343 | 329 | |
| 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; |
| 330 | fn alloc(allocator: *Allocator, len: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 { |
| 331 | const page_count = nPages(len); |
| 332 | const page_idx = try allocPages(page_count); |
| 333 | return @intToPtr([*]u8, page_idx * mem.page_size) |
| 334 | [0..alignPageAllocLen(page_count * mem.page_size, len, len_align)]; |
| 335 | } |
| 336 | fn allocPages(page_count: usize) !usize { |
| 337 | { |
| 338 | const idx = conventional.useRecycled(page_count); |
| 339 | if (idx != FreeBlock.not_found) { |
| 340 | return idx; |
| 341 | } |
| 348 | 342 | } |
| 349 | 343 | |
| 350 | | idx = extended.useRecycled(page_count); |
| 344 | const idx = extended.useRecycled(page_count); |
| 351 | 345 | if (idx != FreeBlock.not_found) { |
| 352 | 346 | return idx + extendedOffset(); |
| 353 | 347 | } |
| ... | ... | @@ -360,51 +354,36 @@ const WasmPageAllocator = struct { |
| 360 | 354 | return @intCast(usize, prev_page_count); |
| 361 | 355 | } |
| 362 | 356 | |
| 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; |
| 357 | fn freePages(start: usize, end: usize) void { |
| 358 | if (start < extendedOffset()) { |
| 359 | conventional.recycle(start, std.math.min(extendedOffset(), end) - start); |
| 366 | 360 | } |
| 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; |
| 361 | if (end > extendedOffset()) { |
| 362 | var new_end = end; |
| 363 | if (!extended.isInitialized()) { |
| 364 | // Steal the last page from the memory currently being recycled |
| 365 | // TODO: would it be better if we use the first page instead? |
| 366 | new_end -= 1; |
| 367 | |
| 368 | extended.data = @intToPtr([*]u128, new_end * mem.page_size)[0 .. mem.page_size / @sizeOf(u128)]; |
| 369 | // Since this is the first page being freed and we consume it, assume *nothing* is free. |
| 370 | mem.set(u128, extended.data, PageStatus.none_free); |
| 371 | } |
| 372 | const clamped_start = std.math.max(extendedOffset(), start); |
| 373 | extended.recycle(clamped_start - extendedOffset(), new_end - clamped_start); |
| 378 | 374 | } |
| 379 | 375 | } |
| 380 | 376 | |
| 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 | | } |
| 377 | fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize { |
| 378 | const aligned_len = mem.alignForward(buf.len, mem.page_size); |
| 379 | if (new_len > aligned_len) return error.OutOfMemory; |
| 380 | const current_n = nPages(aligned_len); |
| 381 | const new_n = nPages(new_len); |
| 382 | if (new_n != current_n) { |
| 383 | const base = nPages(@ptrToInt(buf.ptr)); |
| 384 | freePages(base + new_n, base + current_n); |
| 405 | 385 | } |
| 406 | | |
| 407 | | return old_mem[0..new_size]; |
| 386 | return if (new_len == 0) 0 else alignPageAllocLen(new_n * mem.page_size, new_len, len_align); |
| 408 | 387 | } |
| 409 | 388 | }; |
| 410 | 389 | |
| ... | ... | @@ -418,8 +397,8 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 418 | 397 | pub fn init() HeapAllocator { |
| 419 | 398 | return HeapAllocator{ |
| 420 | 399 | .allocator = Allocator{ |
| 421 | | .reallocFn = realloc, |
| 422 | | .shrinkFn = shrink, |
| 400 | .allocFn = alloc, |
| 401 | .resizeFn = resize, |
| 423 | 402 | }, |
| 424 | 403 | .heap_handle = null, |
| 425 | 404 | }; |
| ... | ... | @@ -431,11 +410,14 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 431 | 410 | } |
| 432 | 411 | } |
| 433 | 412 | |
| 434 | | fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { |
| 413 | fn getRecordPtr(buf: []u8) *align(1) usize { |
| 414 | return @intToPtr(*align(1) usize, @ptrToInt(buf.ptr) + buf.len); |
| 415 | } |
| 416 | |
| 417 | fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 { |
| 435 | 418 | const self = @fieldParentPtr(HeapAllocator, "allocator", allocator); |
| 436 | | if (n == 0) return &[0]u8{}; |
| 437 | 419 | |
| 438 | | const amt = n + alignment + @sizeOf(usize); |
| 420 | const amt = n + ptr_align - 1 + @sizeOf(usize); |
| 439 | 421 | const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst); |
| 440 | 422 | const heap_handle = optional_heap_handle orelse blk: { |
| 441 | 423 | const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0; |
| ... | ... | @@ -446,66 +428,60 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 446 | 428 | }; |
| 447 | 429 | const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; |
| 448 | 430 | 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]; |
| 431 | const aligned_addr = mem.alignForward(root_addr, ptr_align); |
| 432 | const return_len = init: { |
| 433 | if (len_align == 0) break :init n; |
| 434 | const full_len = os.windows.kernel32.HeapSize(heap_handle, 0, ptr); |
| 435 | assert(full_len != std.math.maxInt(usize)); |
| 436 | assert(full_len >= amt); |
| 437 | break :init mem.alignBackwardAnyAlign(full_len - (aligned_addr - root_addr), len_align); |
| 464 | 438 | }; |
| 439 | const buf = @intToPtr([*]u8, aligned_addr)[0..return_len]; |
| 440 | getRecordPtr(buf).* = root_addr; |
| 441 | return buf; |
| 465 | 442 | } |
| 466 | 443 | |
| 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 | | |
| 444 | fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) error{OutOfMemory}!usize { |
| 470 | 445 | 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 | 446 | if (new_size == 0) { |
| 477 | | os.windows.HeapFree(self.heap_handle.?, 0, old_ptr); |
| 478 | | return old_mem[0..0]; |
| 447 | os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*c_void ,getRecordPtr(buf).*)); |
| 448 | return 0; |
| 479 | 449 | } |
| 480 | 450 | |
| 481 | | const amt = new_size + new_align + @sizeOf(usize); |
| 451 | const root_addr = getRecordPtr(buf).*; |
| 452 | const align_offset = @ptrToInt(buf.ptr) - root_addr; |
| 453 | const amt = align_offset + new_size + @sizeOf(usize); |
| 482 | 454 | const new_ptr = os.windows.kernel32.HeapReAlloc( |
| 483 | 455 | self.heap_handle.?, |
| 484 | | 0, |
| 485 | | old_ptr, |
| 456 | os.windows.HEAP_REALLOC_IN_PLACE_ONLY, |
| 457 | @intToPtr(*c_void, root_addr), |
| 486 | 458 | amt, |
| 487 | 459 | ) 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]; |
| 460 | assert(new_ptr == @intToPtr(*c_void, root_addr)); |
| 461 | const return_len = init: { |
| 462 | if (len_align == 0) break :init new_size; |
| 463 | const full_len = os.windows.kernel32.HeapSize(self.heap_handle.?, 0, new_ptr); |
| 464 | assert(full_len != std.math.maxInt(usize)); |
| 465 | assert(full_len >= amt); |
| 466 | break :init mem.alignBackwardAnyAlign(full_len - align_offset, len_align); |
| 467 | }; |
| 468 | getRecordPtr(buf.ptr[0..return_len]).* = root_addr; |
| 469 | return return_len; |
| 504 | 470 | } |
| 505 | 471 | }, |
| 506 | 472 | else => @compileError("Unsupported OS"), |
| 507 | 473 | }; |
| 508 | 474 | |
| 475 | fn sliceContainsPtr(container: []u8, ptr: [*]u8) bool { |
| 476 | return @ptrToInt(ptr) >= @ptrToInt(container.ptr) and |
| 477 | @ptrToInt(ptr) < (@ptrToInt(container.ptr) + container.len); |
| 478 | } |
| 479 | |
| 480 | fn sliceContainsSlice(container: []u8, slice: []u8) bool { |
| 481 | return @ptrToInt(slice.ptr) >= @ptrToInt(container.ptr) and |
| 482 | (@ptrToInt(slice.ptr) + slice.len) <= (@ptrToInt(container.ptr) + container.len); |
| 483 | } |
| 484 | |
| 509 | 485 | pub const FixedBufferAllocator = struct { |
| 510 | 486 | allocator: Allocator, |
| 511 | 487 | end_index: usize, |
| ... | ... | @@ -514,19 +490,33 @@ pub const FixedBufferAllocator = struct { |
| 514 | 490 | pub fn init(buffer: []u8) FixedBufferAllocator { |
| 515 | 491 | return FixedBufferAllocator{ |
| 516 | 492 | .allocator = Allocator{ |
| 517 | | .reallocFn = realloc, |
| 518 | | .shrinkFn = shrink, |
| 493 | .allocFn = alloc, |
| 494 | .resizeFn = resize, |
| 519 | 495 | }, |
| 520 | 496 | .buffer = buffer, |
| 521 | 497 | .end_index = 0, |
| 522 | 498 | }; |
| 523 | 499 | } |
| 524 | 500 | |
| 525 | | fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { |
| 501 | pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool { |
| 502 | return sliceContainsPtr(self.buffer, ptr); |
| 503 | } |
| 504 | |
| 505 | pub fn ownsSlice(self: *FixedBufferAllocator, slice: []u8) bool { |
| 506 | return sliceContainsSlice(self.buffer, slice); |
| 507 | } |
| 508 | |
| 509 | /// NOTE: this will not work in all cases, if the last allocation had an adjusted_index |
| 510 | /// then we won't be able to determine what the last allocation was. This is because |
| 511 | /// the alignForward operation done in alloc is not reverisible. |
| 512 | pub fn isLastAllocation(self: *FixedBufferAllocator, buf: []u8) bool { |
| 513 | return buf.ptr + buf.len == self.buffer.ptr + self.end_index; |
| 514 | } |
| 515 | |
| 516 | fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 { |
| 526 | 517 | 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); |
| 518 | const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align); |
| 519 | const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr); |
| 530 | 520 | const new_end_index = adjusted_index + n; |
| 531 | 521 | if (new_end_index > self.buffer.len) { |
| 532 | 522 | return error.OutOfMemory; |
| ... | ... | @@ -537,30 +527,28 @@ pub const FixedBufferAllocator = struct { |
| 537 | 527 | return result; |
| 538 | 528 | } |
| 539 | 529 | |
| 540 | | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 530 | fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) Allocator.Error!usize { |
| 541 | 531 | 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; |
| 532 | assert(self.ownsSlice(buf)); // sanity check |
| 533 | |
| 534 | if (!self.isLastAllocation(buf)) { |
| 535 | if (new_size > buf.len) |
| 536 | return error.OutOfMemory; |
| 537 | return if (new_size == 0) 0 else mem.alignAllocLen(buf.len, new_size, len_align); |
| 559 | 538 | } |
| 560 | | } |
| 561 | 539 | |
| 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]; |
| 540 | if (new_size <= buf.len) { |
| 541 | const sub = buf.len - new_size; |
| 542 | self.end_index -= sub; |
| 543 | return if (new_size == 0) 0 else mem.alignAllocLen(buf.len - sub, new_size, len_align); |
| 544 | } |
| 545 | |
| 546 | const add = new_size - buf.len; |
| 547 | if (add + self.end_index > self.buffer.len) { |
| 548 | return error.OutOfMemory; |
| 549 | } |
| 550 | self.end_index += add; |
| 551 | return new_size; |
| 564 | 552 | } |
| 565 | 553 | |
| 566 | 554 | pub fn reset(self: *FixedBufferAllocator) void { |
| ... | ... | @@ -581,20 +569,20 @@ pub const ThreadSafeFixedBufferAllocator = blk: { |
| 581 | 569 | pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { |
| 582 | 570 | return ThreadSafeFixedBufferAllocator{ |
| 583 | 571 | .allocator = Allocator{ |
| 584 | | .reallocFn = realloc, |
| 585 | | .shrinkFn = shrink, |
| 572 | .allocFn = alloc, |
| 573 | .resizeFn = Allocator.noResize, |
| 586 | 574 | }, |
| 587 | 575 | .buffer = buffer, |
| 588 | 576 | .end_index = 0, |
| 589 | 577 | }; |
| 590 | 578 | } |
| 591 | 579 | |
| 592 | | fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { |
| 580 | fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 { |
| 593 | 581 | const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); |
| 594 | 582 | var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); |
| 595 | 583 | while (true) { |
| 596 | 584 | const addr = @ptrToInt(self.buffer.ptr) + end_index; |
| 597 | | const adjusted_addr = mem.alignForward(addr, alignment); |
| 585 | const adjusted_addr = mem.alignForward(addr, ptr_align); |
| 598 | 586 | const adjusted_index = end_index + (adjusted_addr - addr); |
| 599 | 587 | const new_end_index = adjusted_index + n; |
| 600 | 588 | if (new_end_index > self.buffer.len) { |
| ... | ... | @@ -604,21 +592,6 @@ pub const ThreadSafeFixedBufferAllocator = blk: { |
| 604 | 592 | } |
| 605 | 593 | } |
| 606 | 594 | |
| 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 | 595 | pub fn reset(self: *ThreadSafeFixedBufferAllocator) void { |
| 623 | 596 | self.end_index = 0; |
| 624 | 597 | } |
| ... | ... | @@ -632,8 +605,8 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack |
| 632 | 605 | .fallback_allocator = fallback_allocator, |
| 633 | 606 | .fixed_buffer_allocator = undefined, |
| 634 | 607 | .allocator = Allocator{ |
| 635 | | .reallocFn = StackFallbackAllocator(size).realloc, |
| 636 | | .shrinkFn = StackFallbackAllocator(size).shrink, |
| 608 | .allocFn = StackFallbackAllocator(size).realloc, |
| 609 | .resizeFn = StackFallbackAllocator(size).resize, |
| 637 | 610 | }, |
| 638 | 611 | }; |
| 639 | 612 | } |
| ... | ... | @@ -652,58 +625,19 @@ pub fn StackFallbackAllocator(comptime size: usize) type { |
| 652 | 625 | return &self.allocator; |
| 653 | 626 | } |
| 654 | 627 | |
| 655 | | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 628 | fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![*]u8 { |
| 656 | 629 | 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 | | ); |
| 630 | return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch |
| 631 | return fallback_allocator.alloc(len, ptr_align); |
| 685 | 632 | } |
| 686 | 633 | |
| 687 | | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 634 | fn resize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!void { |
| 688 | 635 | 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 | | ); |
| 636 | if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) { |
| 637 | try self.fixed_buffer_allocator.callResizeFn(buf, new_len); |
| 638 | } else { |
| 639 | try self.fallback_allocator.callResizeFn(buf, new_len); |
| 699 | 640 | } |
| 700 | | return self.fallback_allocator.shrinkFn( |
| 701 | | self.fallback_allocator, |
| 702 | | old_mem, |
| 703 | | old_align, |
| 704 | | new_size, |
| 705 | | new_align, |
| 706 | | ); |
| 707 | 641 | } |
| 708 | 642 | }; |
| 709 | 643 | } |
| ... | ... | @@ -718,8 +652,8 @@ test "c_allocator" { |
| 718 | 652 | |
| 719 | 653 | test "WasmPageAllocator internals" { |
| 720 | 654 | 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); |
| 655 | const conventional_memsize = WasmPageAllocator.conventional.totalPages() * mem.page_size; |
| 656 | const initial = try page_allocator.alloc(u8, mem.page_size); |
| 723 | 657 | 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 | 658 | |
| 725 | 659 | var inplace = try page_allocator.realloc(initial, 1); |
| ... | ... | @@ -799,7 +733,7 @@ test "ArenaAllocator" { |
| 799 | 733 | |
| 800 | 734 | var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined; |
| 801 | 735 | test "FixedBufferAllocator" { |
| 802 | | var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); |
| 736 | var fixed_buffer_allocator = mem.validationWrap(FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..])); |
| 803 | 737 | |
| 804 | 738 | try testAllocator(&fixed_buffer_allocator.allocator); |
| 805 | 739 | try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16); |
| ... | ... | @@ -865,7 +799,10 @@ test "ThreadSafeFixedBufferAllocator" { |
| 865 | 799 | try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator); |
| 866 | 800 | } |
| 867 | 801 | |
| 868 | | fn testAllocator(allocator: *mem.Allocator) !void { |
| 802 | fn testAllocator(base_allocator: *mem.Allocator) !void { |
| 803 | var validationAllocator = mem.validationWrap(base_allocator); |
| 804 | const allocator = &validationAllocator.allocator; |
| 805 | |
| 869 | 806 | var slice = try allocator.alloc(*i32, 100); |
| 870 | 807 | testing.expect(slice.len == 100); |
| 871 | 808 | for (slice) |*item, i| { |
| ... | ... | @@ -893,7 +830,10 @@ fn testAllocator(allocator: *mem.Allocator) !void { |
| 893 | 830 | allocator.free(slice); |
| 894 | 831 | } |
| 895 | 832 | |
| 896 | | fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void { |
| 833 | fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void { |
| 834 | var validationAllocator = mem.validationWrap(base_allocator); |
| 835 | const allocator = &validationAllocator.allocator; |
| 836 | |
| 897 | 837 | // initial |
| 898 | 838 | var slice = try allocator.alignedAlloc(u8, alignment, 10); |
| 899 | 839 | testing.expect(slice.len == 10); |
| ... | ... | @@ -917,7 +857,10 @@ fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !voi |
| 917 | 857 | testing.expect(slice.len == 0); |
| 918 | 858 | } |
| 919 | 859 | |
| 920 | | fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void { |
| 860 | fn testAllocatorLargeAlignment(base_allocator: *mem.Allocator) mem.Allocator.Error!void { |
| 861 | var validationAllocator = mem.validationWrap(base_allocator); |
| 862 | const allocator = &validationAllocator.allocator; |
| 863 | |
| 921 | 864 | //Maybe a platform's page_size is actually the same as or |
| 922 | 865 | // very near usize? |
| 923 | 866 | if (mem.page_size << 2 > maxInt(usize)) return; |
| ... | ... | @@ -946,7 +889,10 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo |
| 946 | 889 | allocator.free(slice); |
| 947 | 890 | } |
| 948 | 891 | |
| 949 | | fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!void { |
| 892 | fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) mem.Allocator.Error!void { |
| 893 | var validationAllocator = mem.validationWrap(base_allocator); |
| 894 | const allocator = &validationAllocator.allocator; |
| 895 | |
| 950 | 896 | var debug_buffer: [1000]u8 = undefined; |
| 951 | 897 | const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator; |
| 952 | 898 | |