| author | |
| committer | |
| log | 4a4ea92cf38372dc63184957e2936fa7989743fb |
| tree | 94866dbdde8065d23016ed23f2dc09af18ed85da |
| parent | 445b03384a5ffcace11927aa9dd5f21604527f5c |
Use std.heap.FixedBufferAllocator combined with
std.heap.DirectAllocator instead.
std.mem.FixedBufferAllocator is moved to std.heap.FixedBufferAllocator7 files changed, 60 insertions(+), 140 deletions(-)
std/debug/index.zig+1-1| ... | ... | @@ -1078,5 +1078,5 @@ fn readILeb128(in_stream: var) !i64 { |
| 1078 | 1078 | } |
| 1079 | 1079 | |
| 1080 | 1080 | pub const global_allocator = &global_fixed_allocator.allocator; |
| 1081 | var global_fixed_allocator = mem.FixedBufferAllocator.init(global_allocator_mem[0..]); | |
| 1081 | var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]); | |
| 1082 | 1082 | var global_allocator_mem: [100 * 1024]u8 = undefined; |
std/heap.zig+52-87| ... | ... | @@ -39,74 +39,6 @@ fn cFree(self: &Allocator, old_mem: []u8) void { |
| 39 | 39 | c.free(old_ptr); |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | /// Use this allocator when you want to allocate completely up front and guarantee that individual | |
| 43 | /// allocations will never make syscalls. | |
| 44 | pub const IncrementingAllocator = struct { | |
| 45 | allocator: Allocator, | |
| 46 | bytes: []u8, | |
| 47 | end_index: usize, | |
| 48 | direct_allocator: DirectAllocator, | |
| 49 | ||
| 50 | pub fn init(capacity: usize) !IncrementingAllocator { | |
| 51 | var direct_allocator = DirectAllocator.init(); | |
| 52 | const bytes = try direct_allocator.allocator.alloc(u8, capacity); | |
| 53 | errdefer direct_allocator.allocator.free(bytes); | |
| 54 | ||
| 55 | return IncrementingAllocator { | |
| 56 | .allocator = Allocator { | |
| 57 | .allocFn = alloc, | |
| 58 | .reallocFn = realloc, | |
| 59 | .freeFn = free, | |
| 60 | }, | |
| 61 | .bytes = bytes, | |
| 62 | .direct_allocator = direct_allocator, | |
| 63 | .end_index = 0, | |
| 64 | }; | |
| 65 | } | |
| 66 | ||
| 67 | pub fn deinit(self: &IncrementingAllocator) void { | |
| 68 | self.direct_allocator.allocator.free(self.bytes); | |
| 69 | self.direct_allocator.deinit(); | |
| 70 | } | |
| 71 | ||
| 72 | fn reset(self: &IncrementingAllocator) void { | |
| 73 | self.end_index = 0; | |
| 74 | } | |
| 75 | ||
| 76 | fn bytesLeft(self: &const IncrementingAllocator) usize { | |
| 77 | return self.bytes.len - self.end_index; | |
| 78 | } | |
| 79 | ||
| 80 | fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { | |
| 81 | const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator); | |
| 82 | const addr = @ptrToInt(&self.bytes[self.end_index]); | |
| 83 | const rem = @rem(addr, alignment); | |
| 84 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); | |
| 85 | const adjusted_index = self.end_index + march_forward_bytes; | |
| 86 | const new_end_index = adjusted_index + n; | |
| 87 | if (new_end_index > self.bytes.len) { | |
| 88 | return error.OutOfMemory; | |
| 89 | } | |
| 90 | const result = self.bytes[adjusted_index .. new_end_index]; | |
| 91 | self.end_index = new_end_index; | |
| 92 | return result; | |
| 93 | } | |
| 94 | ||
| 95 | fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | |
| 96 | if (new_size <= old_mem.len) { | |
| 97 | return old_mem[0..new_size]; | |
| 98 | } else { | |
| 99 | const result = try alloc(allocator, new_size, alignment); | |
| 100 | mem.copy(u8, result, old_mem); | |
| 101 | return result; | |
| 102 | } | |
| 103 | } | |
| 104 | ||
| 105 | fn free(allocator: &Allocator, bytes: []u8) void { | |
| 106 | // Do nothing. That's the point of an incrementing allocator. | |
| 107 | } | |
| 108 | }; | |
| 109 | ||
| 110 | 42 | /// This allocator makes a syscall directly for every allocation and free. |
| 111 | 43 | pub const DirectAllocator = struct { |
| 112 | 44 | allocator: Allocator, |
| ... | ... | @@ -327,34 +259,60 @@ pub const ArenaAllocator = struct { |
| 327 | 259 | fn free(allocator: &Allocator, bytes: []u8) void { } |
| 328 | 260 | }; |
| 329 | 261 | |
| 262 | pub const FixedBufferAllocator = struct { | |
| 263 | allocator: Allocator, | |
| 264 | end_index: usize, | |
| 265 | buffer: []u8, | |
| 330 | 266 | |
| 331 | ||
| 332 | test "c_allocator" { | |
| 333 | if (builtin.link_libc) { | |
| 334 | var slice = c_allocator.alloc(u8, 50) catch return; | |
| 335 | defer c_allocator.free(slice); | |
| 336 | slice = c_allocator.realloc(u8, slice, 100) catch return; | |
| 267 | pub fn init(buffer: []u8) FixedBufferAllocator { | |
| 268 | return FixedBufferAllocator { | |
| 269 | .allocator = Allocator { | |
| 270 | .allocFn = alloc, | |
| 271 | .reallocFn = realloc, | |
| 272 | .freeFn = free, | |
| 273 | }, | |
| 274 | .buffer = buffer, | |
| 275 | .end_index = 0, | |
| 276 | }; | |
| 337 | 277 | } |
| 338 | } | |
| 339 | 278 | |
| 340 | test "IncrementingAllocator" { | |
| 341 | const total_bytes = 10 * 1024 * 1024; | |
| 342 | var inc_allocator = try IncrementingAllocator.init(total_bytes); | |
| 343 | defer inc_allocator.deinit(); | |
| 279 | fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { | |
| 280 | const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); | |
| 281 | const addr = @ptrToInt(&self.buffer[self.end_index]); | |
| 282 | const rem = @rem(addr, alignment); | |
| 283 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); | |
| 284 | const adjusted_index = self.end_index + march_forward_bytes; | |
| 285 | const new_end_index = adjusted_index + n; | |
| 286 | if (new_end_index > self.buffer.len) { | |
| 287 | return error.OutOfMemory; | |
| 288 | } | |
| 289 | const result = self.buffer[adjusted_index .. new_end_index]; | |
| 290 | self.end_index = new_end_index; | |
| 344 | 291 | |
| 345 | const allocator = &inc_allocator.allocator; | |
| 346 | const slice = try allocator.alloc(&i32, 100); | |
| 292 | return result; | |
| 293 | } | |
| 347 | 294 | |
| 348 | for (slice) |*item, i| { | |
| 349 | *item = try allocator.create(i32); | |
| 350 | **item = i32(i); | |
| 295 | fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | |
| 296 | if (new_size <= old_mem.len) { | |
| 297 | return old_mem[0..new_size]; | |
| 298 | } else { | |
| 299 | const result = try alloc(allocator, new_size, alignment); | |
| 300 | mem.copy(u8, result, old_mem); | |
| 301 | return result; | |
| 302 | } | |
| 351 | 303 | } |
| 352 | 304 | |
| 353 | assert(inc_allocator.bytesLeft() == total_bytes - @sizeOf(i32) * 100 - @sizeOf(usize) * 100); | |
| 305 | fn free(allocator: &Allocator, bytes: []u8) void { } | |
| 306 | }; | |
| 307 | ||
| 354 | 308 | |
| 355 | inc_allocator.reset(); | |
| 356 | 309 | |
| 357 | assert(inc_allocator.bytesLeft() == total_bytes); | |
| 310 | test "c_allocator" { | |
| 311 | if (builtin.link_libc) { | |
| 312 | var slice = c_allocator.alloc(u8, 50) catch return; | |
| 313 | defer c_allocator.free(slice); | |
| 314 | slice = c_allocator.realloc(u8, slice, 100) catch return; | |
| 315 | } | |
| 358 | 316 | } |
| 359 | 317 | |
| 360 | 318 | test "DirectAllocator" { |
| ... | ... | @@ -375,6 +333,13 @@ test "ArenaAllocator" { |
| 375 | 333 | try testAllocator(&arena_allocator.allocator); |
| 376 | 334 | } |
| 377 | 335 | |
| 336 | var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined; | |
| 337 | test "FixedBufferAllocator" { | |
| 338 | var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); | |
| 339 | ||
| 340 | try testAllocator(&fixed_buffer_allocator.allocator); | |
| 341 | } | |
| 342 | ||
| 378 | 343 | fn testAllocator(allocator: &mem.Allocator) !void { |
| 379 | 344 | var slice = try allocator.alloc(&i32, 100); |
| 380 | 345 |
std/mem.zig-45| ... | ... | @@ -111,51 +111,6 @@ pub const Allocator = struct { |
| 111 | 111 | } |
| 112 | 112 | }; |
| 113 | 113 | |
| 114 | pub const FixedBufferAllocator = struct { | |
| 115 | allocator: Allocator, | |
| 116 | end_index: usize, | |
| 117 | buffer: []u8, | |
| 118 | ||
| 119 | pub fn init(buffer: []u8) FixedBufferAllocator { | |
| 120 | return FixedBufferAllocator { | |
| 121 | .allocator = Allocator { | |
| 122 | .allocFn = alloc, | |
| 123 | .reallocFn = realloc, | |
| 124 | .freeFn = free, | |
| 125 | }, | |
| 126 | .buffer = buffer, | |
| 127 | .end_index = 0, | |
| 128 | }; | |
| 129 | } | |
| 130 | ||
| 131 | fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { | |
| 132 | const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); | |
| 133 | const addr = @ptrToInt(&self.buffer[self.end_index]); | |
| 134 | const rem = @rem(addr, alignment); | |
| 135 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); | |
| 136 | const adjusted_index = self.end_index + march_forward_bytes; | |
| 137 | const new_end_index = adjusted_index + n; | |
| 138 | if (new_end_index > self.buffer.len) { | |
| 139 | return error.OutOfMemory; | |
| 140 | } | |
| 141 | const result = self.buffer[adjusted_index .. new_end_index]; | |
| 142 | self.end_index = new_end_index; | |
| 143 | return result; | |
| 144 | } | |
| 145 | ||
| 146 | fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | |
| 147 | if (new_size <= old_mem.len) { | |
| 148 | return old_mem[0..new_size]; | |
| 149 | } else { | |
| 150 | const result = try alloc(allocator, new_size, alignment); | |
| 151 | copy(u8, result, old_mem); | |
| 152 | return result; | |
| 153 | } | |
| 154 | } | |
| 155 | ||
| 156 | fn free(allocator: &Allocator, bytes: []u8) void { } | |
| 157 | }; | |
| 158 | ||
| 159 | 114 | /// Copy all of source into dest at position 0. |
| 160 | 115 | /// dest.len must be >= source.len. |
| 161 | 116 | pub fn copy(comptime T: type, dest: []T, source: []const T) void { |
std/os/child_process.zig+2-2| ... | ... | @@ -363,7 +363,7 @@ pub const ChildProcess = struct { |
| 363 | 363 | const dev_null_fd = if (any_ignore) blk: { |
| 364 | 364 | const dev_null_path = "/dev/null"; |
| 365 | 365 | var fixed_buffer_mem: [dev_null_path.len + 1]u8 = undefined; |
| 366 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 366 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 367 | 367 | break :blk try os.posixOpen(&fixed_allocator.allocator, "/dev/null", posix.O_RDWR, 0); |
| 368 | 368 | } else blk: { |
| 369 | 369 | break :blk undefined; |
| ... | ... | @@ -472,7 +472,7 @@ pub const ChildProcess = struct { |
| 472 | 472 | const nul_handle = if (any_ignore) blk: { |
| 473 | 473 | const nul_file_path = "NUL"; |
| 474 | 474 | var fixed_buffer_mem: [nul_file_path.len + 1]u8 = undefined; |
| 475 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 475 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 476 | 476 | break :blk try os.windowsOpen(&fixed_allocator.allocator, "NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, |
| 477 | 477 | windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL); |
| 478 | 478 | } else blk: { |
std/os/index.zig+2-2| ... | ... | @@ -1702,12 +1702,12 @@ pub fn openSelfExe() !os.File { |
| 1702 | 1702 | Os.linux => { |
| 1703 | 1703 | const proc_file_path = "/proc/self/exe"; |
| 1704 | 1704 | var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined; |
| 1705 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1705 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1706 | 1706 | return os.File.openRead(&fixed_allocator.allocator, proc_file_path); |
| 1707 | 1707 | }, |
| 1708 | 1708 | Os.macosx, Os.ios => { |
| 1709 | 1709 | var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined; |
| 1710 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1710 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1711 | 1711 | const self_exe_path = try selfExePath(&fixed_allocator.allocator); |
| 1712 | 1712 | return os.File.openRead(&fixed_allocator.allocator, self_exe_path); |
| 1713 | 1713 | }, |
std/sort.zig+1-1| ... | ... | @@ -1094,7 +1094,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 1094 | 1094 | |
| 1095 | 1095 | fn fuzzTest(rng: &std.rand.Rand) void { |
| 1096 | 1096 | const array_size = rng.range(usize, 0, 1000); |
| 1097 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1097 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1098 | 1098 | var array = fixed_allocator.allocator.alloc(IdAndValue, array_size) catch unreachable; |
| 1099 | 1099 | // populate with random data |
| 1100 | 1100 | for (array) |*item, index| { |
std/zig/parser.zig+2-2| ... | ... | @@ -1060,7 +1060,7 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { |
| 1060 | 1060 | fn testCanonical(source: []const u8) !void { |
| 1061 | 1061 | const needed_alloc_count = x: { |
| 1062 | 1062 | // Try it once with unlimited memory, make sure it works |
| 1063 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1063 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1064 | 1064 | var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); |
| 1065 | 1065 | const result_source = try testParse(source, &failing_allocator.allocator); |
| 1066 | 1066 | if (!mem.eql(u8, result_source, source)) { |
| ... | ... | @@ -1077,7 +1077,7 @@ fn testCanonical(source: []const u8) !void { |
| 1077 | 1077 | |
| 1078 | 1078 | var fail_index: usize = 0; |
| 1079 | 1079 | while (fail_index < needed_alloc_count) : (fail_index += 1) { |
| 1080 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1080 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1081 | 1081 | var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index); |
| 1082 | 1082 | if (testParse(source, &failing_allocator.allocator)) |_| { |
| 1083 | 1083 | return error.NondeterministicMemoryUsage; |