authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-30 01:44:34-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-11-30 01:44:34-05:00
loge35f297aeb993ec956ae80379ddf7f86069e109b
tree45cbb5b3ebbe23a46e27b04aa5898a6c00ec4a61
parentdeda6b514691c3a7ffc7931469886d0e7be2f67e
parentf4666678886c2a7a993ad30b63de4ff25594085a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13666 from ziglang/allocator-interface

std.mem.Allocator: allow shrink to fail

58 files changed, 993 insertions(+), 1303 deletions(-)

doc/docgen.zig+7-7
...@@ -471,7 +471,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {...@@ -471,7 +471,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
471 },471 },
472 Token.Id.Separator => {},472 Token.Id.Separator => {},
473 Token.Id.BracketClose => {473 Token.Id.BracketClose => {
474 try nodes.append(Node{ .SeeAlso = list.toOwnedSlice() });474 try nodes.append(Node{ .SeeAlso = try list.toOwnedSlice() });
475 break;475 break;
476 },476 },
477 else => return parseError(tokenizer, see_also_tok, "invalid see_also token", .{}),477 else => return parseError(tokenizer, see_also_tok, "invalid see_also token", .{}),
...@@ -610,7 +610,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {...@@ -610,7 +610,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
610 .source_token = source_token,610 .source_token = source_token,
611 .just_check_syntax = just_check_syntax,611 .just_check_syntax = just_check_syntax,
612 .mode = mode,612 .mode = mode,
613 .link_objects = link_objects.toOwnedSlice(),613 .link_objects = try link_objects.toOwnedSlice(),
614 .target_str = target_str,614 .target_str = target_str,
615 .link_libc = link_libc,615 .link_libc = link_libc,
616 .backend_stage1 = backend_stage1,616 .backend_stage1 = backend_stage1,
...@@ -707,8 +707,8 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {...@@ -707,8 +707,8 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
707 }707 }
708708
709 return Toc{709 return Toc{
710 .nodes = nodes.toOwnedSlice(),710 .nodes = try nodes.toOwnedSlice(),
711 .toc = toc_buf.toOwnedSlice(),711 .toc = try toc_buf.toOwnedSlice(),
712 .urls = urls,712 .urls = urls,
713 };713 };
714}714}
...@@ -729,7 +729,7 @@ fn urlize(allocator: Allocator, input: []const u8) ![]u8 {...@@ -729,7 +729,7 @@ fn urlize(allocator: Allocator, input: []const u8) ![]u8 {
729 else => {},729 else => {},
730 }730 }
731 }731 }
732 return buf.toOwnedSlice();732 return try buf.toOwnedSlice();
733}733}
734734
735fn escapeHtml(allocator: Allocator, input: []const u8) ![]u8 {735fn escapeHtml(allocator: Allocator, input: []const u8) ![]u8 {
...@@ -738,7 +738,7 @@ fn escapeHtml(allocator: Allocator, input: []const u8) ![]u8 {...@@ -738,7 +738,7 @@ fn escapeHtml(allocator: Allocator, input: []const u8) ![]u8 {
738738
739 const out = buf.writer();739 const out = buf.writer();
740 try writeEscaped(out, input);740 try writeEscaped(out, input);
741 return buf.toOwnedSlice();741 return try buf.toOwnedSlice();
742}742}
743743
744fn writeEscaped(out: anytype, input: []const u8) !void {744fn writeEscaped(out: anytype, input: []const u8) !void {
...@@ -854,7 +854,7 @@ fn termColor(allocator: Allocator, input: []const u8) ![]u8 {...@@ -854,7 +854,7 @@ fn termColor(allocator: Allocator, input: []const u8) ![]u8 {
854 },854 },
855 }855 }
856 }856 }
857 return buf.toOwnedSlice();857 return try buf.toOwnedSlice();
858}858}
859859
860const builtin_types = [_][]const u8{860const builtin_types = [_][]const u8{
lib/std/array_hash_map.zig+1-1
...@@ -1872,7 +1872,7 @@ const IndexHeader = struct {...@@ -1872,7 +1872,7 @@ const IndexHeader = struct {
1872 const len = @as(usize, 1) << @intCast(math.Log2Int(usize), new_bit_index);1872 const len = @as(usize, 1) << @intCast(math.Log2Int(usize), new_bit_index);
1873 const index_size = hash_map.capacityIndexSize(new_bit_index);1873 const index_size = hash_map.capacityIndexSize(new_bit_index);
1874 const nbytes = @sizeOf(IndexHeader) + index_size * len;1874 const nbytes = @sizeOf(IndexHeader) + index_size * len;
1875 const bytes = try allocator.allocAdvanced(u8, @alignOf(IndexHeader), nbytes, .exact);1875 const bytes = try allocator.alignedAlloc(u8, @alignOf(IndexHeader), nbytes);
1876 @memset(bytes.ptr + @sizeOf(IndexHeader), 0xff, bytes.len - @sizeOf(IndexHeader));1876 @memset(bytes.ptr + @sizeOf(IndexHeader), 0xff, bytes.len - @sizeOf(IndexHeader));
1877 const result = @ptrCast(*IndexHeader, bytes.ptr);1877 const result = @ptrCast(*IndexHeader, bytes.ptr);
1878 result.* = .{1878 result.* = .{
lib/std/array_list.zig+141-49
...@@ -47,6 +47,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -47,6 +47,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
4747
48 pub const Slice = if (alignment) |a| ([]align(a) T) else []T;48 pub const Slice = if (alignment) |a| ([]align(a) T) else []T;
4949
50 pub fn SentinelSlice(comptime s: T) type {
51 return if (alignment) |a| ([:s]align(a) T) else [:s]T;
52 }
53
50 /// Deinitialize with `deinit` or use `toOwnedSlice`.54 /// Deinitialize with `deinit` or use `toOwnedSlice`.
51 pub fn init(allocator: Allocator) Self {55 pub fn init(allocator: Allocator) Self {
52 return Self{56 return Self{
...@@ -92,18 +96,31 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -92,18 +96,31 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
92 return result;96 return result;
93 }97 }
9498
95 /// The caller owns the returned memory. Empties this ArrayList.99 /// The caller owns the returned memory. Empties this ArrayList,
96 pub fn toOwnedSlice(self: *Self) Slice {100 /// however its capacity may or may not be cleared and deinit() is
101 /// still required to clean up its memory.
102 pub fn toOwnedSlice(self: *Self) Allocator.Error!Slice {
97 const allocator = self.allocator;103 const allocator = self.allocator;
98 const result = allocator.shrink(self.allocatedSlice(), self.items.len);104
99 self.* = init(allocator);105 const old_memory = self.allocatedSlice();
100 return result;106 if (allocator.resize(old_memory, self.items.len)) {
107 const result = self.items;
108 self.* = init(allocator);
109 return result;
110 }
111
112 const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);
113 mem.copy(T, new_memory, self.items);
114 @memset(@ptrCast([*]u8, self.items.ptr), undefined, self.items.len * @sizeOf(T));
115 self.items.len = 0;
116 return new_memory;
101 }117 }
102118
103 /// The caller owns the returned memory. Empties this ArrayList.119 /// The caller owns the returned memory. Empties this ArrayList.
104 pub fn toOwnedSliceSentinel(self: *Self, comptime sentinel: T) Allocator.Error![:sentinel]T {120 pub fn toOwnedSliceSentinel(self: *Self, comptime sentinel: T) Allocator.Error!SentinelSlice(sentinel) {
105 try self.append(sentinel);121 try self.ensureTotalCapacityPrecise(self.items.len + 1);
106 const result = self.toOwnedSlice();122 self.appendAssumeCapacity(sentinel);
123 const result = try self.toOwnedSlice();
107 return result[0 .. result.len - 1 :sentinel];124 return result[0 .. result.len - 1 :sentinel];
108 }125 }
109126
...@@ -299,17 +316,30 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -299,17 +316,30 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
299 pub fn shrinkAndFree(self: *Self, new_len: usize) void {316 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
300 assert(new_len <= self.items.len);317 assert(new_len <= self.items.len);
301318
302 if (@sizeOf(T) > 0) {319 if (@sizeOf(T) == 0) {
303 self.items = self.allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {320 self.items.len = new_len;
304 error.OutOfMemory => { // no problem, capacity is still correct then.321 return;
305 self.items.len = new_len;322 }
306 return;323
307 },324 const old_memory = self.allocatedSlice();
308 };325 if (self.allocator.resize(old_memory, new_len)) {
309 self.capacity = new_len;326 self.capacity = new_len;
310 } else {
311 self.items.len = new_len;327 self.items.len = new_len;
328 return;
312 }329 }
330
331 const new_memory = self.allocator.alignedAlloc(T, alignment, new_len) catch |e| switch (e) {
332 error.OutOfMemory => {
333 // No problem, capacity is still correct then.
334 self.items.len = new_len;
335 return;
336 },
337 };
338
339 mem.copy(T, new_memory, self.items);
340 self.allocator.free(old_memory);
341 self.items = new_memory;
342 self.capacity = new_memory.len;
313 }343 }
314344
315 /// Reduce length to `new_len`.345 /// Reduce length to `new_len`.
...@@ -334,19 +364,20 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -334,19 +364,20 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
334 /// Modify the array so that it can hold at least `new_capacity` items.364 /// Modify the array so that it can hold at least `new_capacity` items.
335 /// Invalidates pointers if additional memory is needed.365 /// Invalidates pointers if additional memory is needed.
336 pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) Allocator.Error!void {366 pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) Allocator.Error!void {
337 if (@sizeOf(T) > 0) {367 if (@sizeOf(T) == 0) {
338 if (self.capacity >= new_capacity) return;368 self.capacity = math.maxInt(usize);
369 return;
370 }
339371
340 var better_capacity = self.capacity;372 if (self.capacity >= new_capacity) return;
341 while (true) {
342 better_capacity +|= better_capacity / 2 + 8;
343 if (better_capacity >= new_capacity) break;
344 }
345373
346 return self.ensureTotalCapacityPrecise(better_capacity);374 var better_capacity = self.capacity;
347 } else {375 while (true) {
348 self.capacity = math.maxInt(usize);376 better_capacity +|= better_capacity / 2 + 8;
377 if (better_capacity >= new_capacity) break;
349 }378 }
379
380 return self.ensureTotalCapacityPrecise(better_capacity);
350 }381 }
351382
352 /// Modify the array so that it can hold at least `new_capacity` items.383 /// Modify the array so that it can hold at least `new_capacity` items.
...@@ -354,15 +385,27 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -354,15 +385,27 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
354 /// (but not guaranteed) to be equal to `new_capacity`.385 /// (but not guaranteed) to be equal to `new_capacity`.
355 /// Invalidates pointers if additional memory is needed.386 /// Invalidates pointers if additional memory is needed.
356 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {387 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {
357 if (@sizeOf(T) > 0) {388 if (@sizeOf(T) == 0) {
358 if (self.capacity >= new_capacity) return;389 self.capacity = math.maxInt(usize);
390 return;
391 }
359392
360 // TODO This can be optimized to avoid needlessly copying undefined memory.393 if (self.capacity >= new_capacity) return;
361 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), new_capacity);394
395 // Here we avoid copying allocated but unused bytes by
396 // attempting a resize in place, and falling back to allocating
397 // a new buffer and doing our own copy. With a realloc() call,
398 // the allocator implementation would pointlessly copy our
399 // extra capacity.
400 const old_memory = self.allocatedSlice();
401 if (self.allocator.resize(old_memory, new_capacity)) {
402 self.capacity = new_capacity;
403 } else {
404 const new_memory = try self.allocator.alignedAlloc(T, alignment, new_capacity);
405 mem.copy(T, new_memory, self.items);
406 self.allocator.free(old_memory);
362 self.items.ptr = new_memory.ptr;407 self.items.ptr = new_memory.ptr;
363 self.capacity = new_memory.len;408 self.capacity = new_memory.len;
364 } else {
365 self.capacity = math.maxInt(usize);
366 }409 }
367 }410 }
368411
...@@ -381,8 +424,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -381,8 +424,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
381 /// Increase length by 1, returning pointer to the new item.424 /// Increase length by 1, returning pointer to the new item.
382 /// The returned pointer becomes invalid when the list resized.425 /// The returned pointer becomes invalid when the list resized.
383 pub fn addOne(self: *Self) Allocator.Error!*T {426 pub fn addOne(self: *Self) Allocator.Error!*T {
384 const newlen = self.items.len + 1;427 try self.ensureTotalCapacity(self.items.len + 1);
385 try self.ensureTotalCapacity(newlen);
386 return self.addOneAssumeCapacity();428 return self.addOneAssumeCapacity();
387 }429 }
388430
...@@ -392,7 +434,6 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -392,7 +434,6 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
392 /// **Does not** invalidate element pointers.434 /// **Does not** invalidate element pointers.
393 pub fn addOneAssumeCapacity(self: *Self) *T {435 pub fn addOneAssumeCapacity(self: *Self) *T {
394 assert(self.items.len < self.capacity);436 assert(self.items.len < self.capacity);
395
396 self.items.len += 1;437 self.items.len += 1;
397 return &self.items[self.items.len - 1];438 return &self.items[self.items.len - 1];
398 }439 }
...@@ -490,6 +531,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -490,6 +531,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
490531
491 pub const Slice = if (alignment) |a| ([]align(a) T) else []T;532 pub const Slice = if (alignment) |a| ([]align(a) T) else []T;
492533
534 pub fn SentinelSlice(comptime s: T) type {
535 return if (alignment) |a| ([:s]align(a) T) else [:s]T;
536 }
537
493 /// Initialize with capacity to hold at least num elements.538 /// Initialize with capacity to hold at least num elements.
494 /// The resulting capacity is likely to be equal to `num`.539 /// The resulting capacity is likely to be equal to `num`.
495 /// Deinitialize with `deinit` or use `toOwnedSlice`.540 /// Deinitialize with `deinit` or use `toOwnedSlice`.
...@@ -511,17 +556,29 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -511,17 +556,29 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
511 return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator };556 return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator };
512 }557 }
513558
514 /// The caller owns the returned memory. ArrayList becomes empty.559 /// The caller owns the returned memory. Empties this ArrayList,
515 pub fn toOwnedSlice(self: *Self, allocator: Allocator) Slice {560 /// however its capacity may or may not be cleared and deinit() is
516 const result = allocator.shrink(self.allocatedSlice(), self.items.len);561 /// still required to clean up its memory.
517 self.* = Self{};562 pub fn toOwnedSlice(self: *Self, allocator: Allocator) Allocator.Error!Slice {
518 return result;563 const old_memory = self.allocatedSlice();
564 if (allocator.resize(old_memory, self.items.len)) {
565 const result = self.items;
566 self.* = .{};
567 return result;
568 }
569
570 const new_memory = try allocator.alignedAlloc(T, alignment, self.items.len);
571 mem.copy(T, new_memory, self.items);
572 @memset(@ptrCast([*]u8, self.items.ptr), undefined, self.items.len * @sizeOf(T));
573 self.items.len = 0;
574 return new_memory;
519 }575 }
520576
521 /// The caller owns the returned memory. ArrayList becomes empty.577 /// The caller owns the returned memory. ArrayList becomes empty.
522 pub fn toOwnedSliceSentinel(self: *Self, allocator: Allocator, comptime sentinel: T) Allocator.Error![:sentinel]T {578 pub fn toOwnedSliceSentinel(self: *Self, allocator: Allocator, comptime sentinel: T) Allocator.Error!SentinelSlice(sentinel) {
523 try self.append(allocator, sentinel);579 try self.ensureTotalCapacityPrecise(allocator, self.items.len + 1);
524 const result = self.toOwnedSlice(allocator);580 self.appendAssumeCapacity(sentinel);
581 const result = try self.toOwnedSlice(allocator);
525 return result[0 .. result.len - 1 :sentinel];582 return result[0 .. result.len - 1 :sentinel];
526 }583 }
527584
...@@ -701,16 +758,34 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -701,16 +758,34 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
701 }758 }
702759
703 /// Reduce allocated capacity to `new_len`.760 /// Reduce allocated capacity to `new_len`.
761 /// May invalidate element pointers.
704 pub fn shrinkAndFree(self: *Self, allocator: Allocator, new_len: usize) void {762 pub fn shrinkAndFree(self: *Self, allocator: Allocator, new_len: usize) void {
705 assert(new_len <= self.items.len);763 assert(new_len <= self.items.len);
706764
707 self.items = allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {765 if (@sizeOf(T) == 0) {
708 error.OutOfMemory => { // no problem, capacity is still correct then.766 self.items.len = new_len;
767 return;
768 }
769
770 const old_memory = self.allocatedSlice();
771 if (allocator.resize(old_memory, new_len)) {
772 self.capacity = new_len;
773 self.items.len = new_len;
774 return;
775 }
776
777 const new_memory = allocator.alignedAlloc(T, alignment, new_len) catch |e| switch (e) {
778 error.OutOfMemory => {
779 // No problem, capacity is still correct then.
709 self.items.len = new_len;780 self.items.len = new_len;
710 return;781 return;
711 },782 },
712 };783 };
713 self.capacity = new_len;784
785 mem.copy(T, new_memory, self.items);
786 allocator.free(old_memory);
787 self.items = new_memory;
788 self.capacity = new_memory.len;
714 }789 }
715790
716 /// Reduce length to `new_len`.791 /// Reduce length to `new_len`.
...@@ -752,11 +827,28 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -752,11 +827,28 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
752 /// (but not guaranteed) to be equal to `new_capacity`.827 /// (but not guaranteed) to be equal to `new_capacity`.
753 /// Invalidates pointers if additional memory is needed.828 /// Invalidates pointers if additional memory is needed.
754 pub fn ensureTotalCapacityPrecise(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void {829 pub fn ensureTotalCapacityPrecise(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void {
830 if (@sizeOf(T) == 0) {
831 self.capacity = math.maxInt(usize);
832 return;
833 }
834
755 if (self.capacity >= new_capacity) return;835 if (self.capacity >= new_capacity) return;
756836
757 const new_memory = try allocator.reallocAtLeast(self.allocatedSlice(), new_capacity);837 // Here we avoid copying allocated but unused bytes by
758 self.items.ptr = new_memory.ptr;838 // attempting a resize in place, and falling back to allocating
759 self.capacity = new_memory.len;839 // a new buffer and doing our own copy. With a realloc() call,
840 // the allocator implementation would pointlessly copy our
841 // extra capacity.
842 const old_memory = self.allocatedSlice();
843 if (allocator.resize(old_memory, new_capacity)) {
844 self.capacity = new_capacity;
845 } else {
846 const new_memory = try allocator.alignedAlloc(T, alignment, new_capacity);
847 mem.copy(T, new_memory, self.items);
848 allocator.free(old_memory);
849 self.items.ptr = new_memory.ptr;
850 self.capacity = new_memory.len;
851 }
760 }852 }
761853
762 /// Modify the array so that it can hold at least `additional_count` **more** items.854 /// Modify the array so that it can hold at least `additional_count` **more** items.
lib/std/build.zig+1-1
...@@ -2934,7 +2934,7 @@ pub const LibExeObjStep = struct {...@@ -2934,7 +2934,7 @@ pub const LibExeObjStep = struct {
2934 }2934 }
2935 }2935 }
29362936
2937 try zig_args.append(mcpu_buffer.toOwnedSlice());2937 try zig_args.append(try mcpu_buffer.toOwnedSlice());
2938 }2938 }
29392939
2940 if (self.target.dynamic_linker.get()) |dynamic_linker| {2940 if (self.target.dynamic_linker.get()) |dynamic_linker| {
lib/std/child_process.zig+3-3
...@@ -421,8 +421,8 @@ pub const ChildProcess = struct {...@@ -421,8 +421,8 @@ pub const ChildProcess = struct {
421421
422 return ExecResult{422 return ExecResult{
423 .term = try child.wait(),423 .term = try child.wait(),
424 .stdout = stdout.toOwnedSlice(),424 .stdout = try stdout.toOwnedSlice(),
425 .stderr = stderr.toOwnedSlice(),425 .stderr = try stderr.toOwnedSlice(),
426 };426 };
427 }427 }
428428
...@@ -1270,7 +1270,7 @@ pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) !...@@ -1270,7 +1270,7 @@ pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) !
1270 i += 1;1270 i += 1;
1271 result[i] = 0;1271 result[i] = 0;
1272 i += 1;1272 i += 1;
1273 return allocator.shrink(result, i);1273 return try allocator.realloc(result, i);
1274}1274}
12751275
1276pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ![:null]?[*:0]u8 {1276pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ![:null]?[*:0]u8 {
lib/std/debug.zig+1-1
...@@ -1112,7 +1112,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn...@@ -1112,7 +1112,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn
1112 }1112 }
1113 assert(state == .oso_close);1113 assert(state == .oso_close);
11141114
1115 const symbols = allocator.shrink(symbols_buf, symbol_index);1115 const symbols = try allocator.realloc(symbols_buf, symbol_index);
11161116
1117 // Even though lld emits symbols in ascending order, this debug code1117 // Even though lld emits symbols in ascending order, this debug code
1118 // should work for programs linked in any valid way.1118 // should work for programs linked in any valid way.
lib/std/fs/file.zig+2-4
...@@ -954,11 +954,9 @@ pub const File = struct {...@@ -954,11 +954,9 @@ pub const File = struct {
954 };954 };
955955
956 if (optional_sentinel) |sentinel| {956 if (optional_sentinel) |sentinel| {
957 try array_list.append(sentinel);957 return try array_list.toOwnedSliceSentinel(sentinel);
958 const buf = array_list.toOwnedSlice();
959 return buf[0 .. buf.len - 1 :sentinel];
960 } else {958 } else {
961 return array_list.toOwnedSlice();959 return try array_list.toOwnedSlice();
962 }960 }
963 }961 }
964962
lib/std/fs/path.zig+1-1
...@@ -1155,7 +1155,7 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]...@@ -1155,7 +1155,7 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]
1155 }1155 }
1156 if (to_rest.len == 0) {1156 if (to_rest.len == 0) {
1157 // shave off the trailing slash1157 // shave off the trailing slash
1158 return allocator.shrink(result, result_index - 1);1158 return allocator.realloc(result, result_index - 1);
1159 }1159 }
11601160
1161 mem.copy(u8, result[result_index..], to_rest);1161 mem.copy(u8, result[result_index..], to_rest);
lib/std/fs/wasi.zig+3-3
...@@ -160,7 +160,7 @@ pub const PreopenList = struct {...@@ -160,7 +160,7 @@ pub const PreopenList = struct {
160 if (cwd_root) |root| assert(fs.path.isAbsolute(root));160 if (cwd_root) |root| assert(fs.path.isAbsolute(root));
161161
162 // Clear contents if we're being called again162 // Clear contents if we're being called again
163 for (self.toOwnedSlice()) |preopen| {163 for (try self.toOwnedSlice()) |preopen| {
164 switch (preopen.type) {164 switch (preopen.type) {
165 PreopenType.Dir => |path| self.buffer.allocator.free(path),165 PreopenType.Dir => |path| self.buffer.allocator.free(path),
166 }166 }
...@@ -263,8 +263,8 @@ pub const PreopenList = struct {...@@ -263,8 +263,8 @@ pub const PreopenList = struct {
263 }263 }
264264
265 /// The caller owns the returned memory. ArrayList becomes empty.265 /// The caller owns the returned memory. ArrayList becomes empty.
266 pub fn toOwnedSlice(self: *Self) []Preopen {266 pub fn toOwnedSlice(self: *Self) ![]Preopen {
267 return self.buffer.toOwnedSlice();267 return try self.buffer.toOwnedSlice();
268 }268 }
269};269};
270270
lib/std/heap.zig+202-293
...@@ -52,11 +52,12 @@ const CAllocator = struct {...@@ -52,11 +52,12 @@ const CAllocator = struct {
52 return @intToPtr(*[*]u8, @ptrToInt(ptr) - @sizeOf(usize));52 return @intToPtr(*[*]u8, @ptrToInt(ptr) - @sizeOf(usize));
53 }53 }
5454
55 fn alignedAlloc(len: usize, alignment: usize) ?[*]u8 {55 fn alignedAlloc(len: usize, log2_align: u8) ?[*]u8 {
56 const alignment = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_align);
56 if (supports_posix_memalign) {57 if (supports_posix_memalign) {
57 // The posix_memalign only accepts alignment values that are a58 // The posix_memalign only accepts alignment values that are a
58 // multiple of the pointer size59 // multiple of the pointer size
59 const eff_alignment = std.math.max(alignment, @sizeOf(usize));60 const eff_alignment = @max(alignment, @sizeOf(usize));
6061
61 var aligned_ptr: ?*anyopaque = undefined;62 var aligned_ptr: ?*anyopaque = undefined;
62 if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0)63 if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0)
...@@ -99,58 +100,42 @@ const CAllocator = struct {...@@ -99,58 +100,42 @@ const CAllocator = struct {
99 fn alloc(100 fn alloc(
100 _: *anyopaque,101 _: *anyopaque,
101 len: usize,102 len: usize,
102 alignment: u29,103 log2_align: u8,
103 len_align: u29,
104 return_address: usize,104 return_address: usize,
105 ) error{OutOfMemory}![]u8 {105 ) ?[*]u8 {
106 _ = return_address;106 _ = return_address;
107 assert(len > 0);107 assert(len > 0);
108 assert(std.math.isPowerOfTwo(alignment));108 return alignedAlloc(len, log2_align);
109
110 var ptr = alignedAlloc(len, alignment) orelse return error.OutOfMemory;
111 if (len_align == 0) {
112 return ptr[0..len];
113 }
114 const full_len = init: {
115 if (CAllocator.supports_malloc_size) {
116 const s = alignedAllocSize(ptr);
117 assert(s >= len);
118 break :init s;
119 }
120 break :init len;
121 };
122 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];
123 }109 }
124110
125 fn resize(111 fn resize(
126 _: *anyopaque,112 _: *anyopaque,
127 buf: []u8,113 buf: []u8,
128 buf_align: u29,114 log2_buf_align: u8,
129 new_len: usize,115 new_len: usize,
130 len_align: u29,
131 return_address: usize,116 return_address: usize,
132 ) ?usize {117 ) bool {
133 _ = buf_align;118 _ = log2_buf_align;
134 _ = return_address;119 _ = return_address;
135 if (new_len <= buf.len) {120 if (new_len <= buf.len) {
136 return mem.alignAllocLen(buf.len, new_len, len_align);121 return true;
137 }122 }
138 if (CAllocator.supports_malloc_size) {123 if (CAllocator.supports_malloc_size) {
139 const full_len = alignedAllocSize(buf.ptr);124 const full_len = alignedAllocSize(buf.ptr);
140 if (new_len <= full_len) {125 if (new_len <= full_len) {
141 return mem.alignAllocLen(full_len, new_len, len_align);126 return true;
142 }127 }
143 }128 }
144 return null;129 return false;
145 }130 }
146131
147 fn free(132 fn free(
148 _: *anyopaque,133 _: *anyopaque,
149 buf: []u8,134 buf: []u8,
150 buf_align: u29,135 log2_buf_align: u8,
151 return_address: usize,136 return_address: usize,
152 ) void {137 ) void {
153 _ = buf_align;138 _ = log2_buf_align;
154 _ = return_address;139 _ = return_address;
155 alignedFree(buf.ptr);140 alignedFree(buf.ptr);
156 }141 }
...@@ -187,40 +172,35 @@ const raw_c_allocator_vtable = Allocator.VTable{...@@ -187,40 +172,35 @@ const raw_c_allocator_vtable = Allocator.VTable{
187fn rawCAlloc(172fn rawCAlloc(
188 _: *anyopaque,173 _: *anyopaque,
189 len: usize,174 len: usize,
190 ptr_align: u29,175 log2_ptr_align: u8,
191 len_align: u29,
192 ret_addr: usize,176 ret_addr: usize,
193) Allocator.Error![]u8 {177) ?[*]u8 {
194 _ = len_align;
195 _ = ret_addr;178 _ = ret_addr;
196 assert(ptr_align <= @alignOf(std.c.max_align_t));179 assert(log2_ptr_align <= comptime std.math.log2_int(usize, @alignOf(std.c.max_align_t)));
197 const ptr = @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory);180 // TODO: change the language to make @ptrCast also do alignment cast
198 return ptr[0..len];181 const ptr = @alignCast(@alignOf(std.c.max_align_t), c.malloc(len));
182 return @ptrCast(?[*]align(@alignOf(std.c.max_align_t)) u8, ptr);
199}183}
200184
201fn rawCResize(185fn rawCResize(
202 _: *anyopaque,186 _: *anyopaque,
203 buf: []u8,187 buf: []u8,
204 old_align: u29,188 log2_old_align: u8,
205 new_len: usize,189 new_len: usize,
206 len_align: u29,
207 ret_addr: usize,190 ret_addr: usize,
208) ?usize {191) bool {
209 _ = old_align;192 _ = log2_old_align;
210 _ = ret_addr;193 _ = ret_addr;
211 if (new_len <= buf.len) {194 return new_len <= buf.len;
212 return mem.alignAllocLen(buf.len, new_len, len_align);
213 }
214 return null;
215}195}
216196
217fn rawCFree(197fn rawCFree(
218 _: *anyopaque,198 _: *anyopaque,
219 buf: []u8,199 buf: []u8,
220 old_align: u29,200 log2_old_align: u8,
221 ret_addr: usize,201 ret_addr: usize,
222) void {202) void {
223 _ = old_align;203 _ = log2_old_align;
224 _ = ret_addr;204 _ = ret_addr;
225 c.free(buf.ptr);205 c.free(buf.ptr);
226}206}
...@@ -241,8 +221,8 @@ else...@@ -241,8 +221,8 @@ else
241 };221 };
242222
243/// Verifies that the adjusted length will still map to the full length223/// Verifies that the adjusted length will still map to the full length
244pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize {224pub fn alignPageAllocLen(full_len: usize, len: usize) usize {
245 const aligned_len = mem.alignAllocLen(full_len, len, len_align);225 const aligned_len = mem.alignAllocLen(full_len, len);
246 assert(mem.alignForward(aligned_len, mem.page_size) == full_len);226 assert(mem.alignForward(aligned_len, mem.page_size) == full_len);
247 return aligned_len;227 return aligned_len;
248}228}
...@@ -257,115 +237,47 @@ const PageAllocator = struct {...@@ -257,115 +237,47 @@ const PageAllocator = struct {
257 .free = free,237 .free = free,
258 };238 };
259239
260 fn alloc(_: *anyopaque, n: usize, alignment: u29, len_align: u29, ra: usize) error{OutOfMemory}![]u8 {240 fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
261 _ = ra;241 _ = ra;
242 _ = log2_align;
262 assert(n > 0);243 assert(n > 0);
263 if (n > maxInt(usize) - (mem.page_size - 1)) {244 if (n > maxInt(usize) - (mem.page_size - 1)) return null;
264 return error.OutOfMemory;
265 }
266 const aligned_len = mem.alignForward(n, mem.page_size);245 const aligned_len = mem.alignForward(n, mem.page_size);
267246
268 if (builtin.os.tag == .windows) {247 if (builtin.os.tag == .windows) {
269 const w = os.windows;248 const w = os.windows;
270
271 // Although officially it's at least aligned to page boundary,
272 // Windows is known to reserve pages on a 64K boundary. It's
273 // even more likely that the requested alignment is <= 64K than
274 // 4K, so we're just allocating blindly and hoping for the best.
275 // see https://devblogs.microsoft.com/oldnewthing/?p=42223
276 const addr = w.VirtualAlloc(249 const addr = w.VirtualAlloc(
277 null,250 null,
278 aligned_len,251 aligned_len,
279 w.MEM_COMMIT | w.MEM_RESERVE,252 w.MEM_COMMIT | w.MEM_RESERVE,
280 w.PAGE_READWRITE,253 w.PAGE_READWRITE,
281 ) catch return error.OutOfMemory;254 ) catch return null;
282255 return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, addr));
283 // If the allocation is sufficiently aligned, use it.
284 if (mem.isAligned(@ptrToInt(addr), alignment)) {
285 return @ptrCast([*]u8, addr)[0..alignPageAllocLen(aligned_len, n, len_align)];
286 }
287
288 // If it wasn't, actually do an explicitly aligned allocation.
289 w.VirtualFree(addr, 0, w.MEM_RELEASE);
290 const alloc_size = n + alignment - mem.page_size;
291
292 while (true) {
293 // Reserve a range of memory large enough to find a sufficiently
294 // aligned address.
295 const reserved_addr = w.VirtualAlloc(
296 null,
297 alloc_size,
298 w.MEM_RESERVE,
299 w.PAGE_NOACCESS,
300 ) catch return error.OutOfMemory;
301 const aligned_addr = mem.alignForward(@ptrToInt(reserved_addr), alignment);
302
303 // Release the reserved pages (not actually used).
304 w.VirtualFree(reserved_addr, 0, w.MEM_RELEASE);
305
306 // At this point, it is possible that another thread has
307 // obtained some memory space that will cause the next
308 // VirtualAlloc call to fail. To handle this, we will retry
309 // until it succeeds.
310 const ptr = w.VirtualAlloc(
311 @intToPtr(*anyopaque, aligned_addr),
312 aligned_len,
313 w.MEM_COMMIT | w.MEM_RESERVE,
314 w.PAGE_READWRITE,
315 ) catch continue;
316
317 return @ptrCast([*]u8, ptr)[0..alignPageAllocLen(aligned_len, n, len_align)];
318 }
319 }256 }
320257
321 const max_drop_len = alignment - @min(alignment, mem.page_size);
322 const alloc_len = if (max_drop_len <= aligned_len - n)
323 aligned_len
324 else
325 mem.alignForward(aligned_len + max_drop_len, mem.page_size);
326 const hint = @atomicLoad(@TypeOf(next_mmap_addr_hint), &next_mmap_addr_hint, .Unordered);258 const hint = @atomicLoad(@TypeOf(next_mmap_addr_hint), &next_mmap_addr_hint, .Unordered);
327 const slice = os.mmap(259 const slice = os.mmap(
328 hint,260 hint,
329 alloc_len,261 aligned_len,
330 os.PROT.READ | os.PROT.WRITE,262 os.PROT.READ | os.PROT.WRITE,
331 os.MAP.PRIVATE | os.MAP.ANONYMOUS,263 os.MAP.PRIVATE | os.MAP.ANONYMOUS,
332 -1,264 -1,
333 0,265 0,
334 ) catch return error.OutOfMemory;266 ) catch return null;
335 assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size));267 assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size));
336268 const new_hint = @alignCast(mem.page_size, slice.ptr + aligned_len);
337 const result_ptr = mem.alignPointer(slice.ptr, alignment) orelse
338 return error.OutOfMemory;
339
340 // Unmap the extra bytes that were only requested in order to guarantee
341 // that the range of memory we were provided had a proper alignment in
342 // it somewhere. The extra bytes could be at the beginning, or end, or both.
343 const drop_len = @ptrToInt(result_ptr) - @ptrToInt(slice.ptr);
344 if (drop_len != 0) {
345 os.munmap(slice[0..drop_len]);
346 }
347
348 // Unmap extra pages
349 const aligned_buffer_len = alloc_len - drop_len;
350 if (aligned_buffer_len > aligned_len) {
351 os.munmap(@alignCast(mem.page_size, result_ptr[aligned_len..aligned_buffer_len]));
352 }
353
354 const new_hint = @alignCast(mem.page_size, result_ptr + aligned_len);
355 _ = @cmpxchgStrong(@TypeOf(next_mmap_addr_hint), &next_mmap_addr_hint, hint, new_hint, .Monotonic, .Monotonic);269 _ = @cmpxchgStrong(@TypeOf(next_mmap_addr_hint), &next_mmap_addr_hint, hint, new_hint, .Monotonic, .Monotonic);
356270 return slice.ptr;
357 return result_ptr[0..alignPageAllocLen(aligned_len, n, len_align)];
358 }271 }
359272
360 fn resize(273 fn resize(
361 _: *anyopaque,274 _: *anyopaque,
362 buf_unaligned: []u8,275 buf_unaligned: []u8,
363 buf_align: u29,276 log2_buf_align: u8,
364 new_size: usize,277 new_size: usize,
365 len_align: u29,
366 return_address: usize,278 return_address: usize,
367 ) ?usize {279 ) bool {
368 _ = buf_align;280 _ = log2_buf_align;
369 _ = return_address;281 _ = return_address;
370 const new_size_aligned = mem.alignForward(new_size, mem.page_size);282 const new_size_aligned = mem.alignForward(new_size, mem.page_size);
371283
...@@ -384,40 +296,40 @@ const PageAllocator = struct {...@@ -384,40 +296,40 @@ const PageAllocator = struct {
384 w.MEM_DECOMMIT,296 w.MEM_DECOMMIT,
385 );297 );
386 }298 }
387 return alignPageAllocLen(new_size_aligned, new_size, len_align);299 return true;
388 }300 }
389 const old_size_aligned = mem.alignForward(buf_unaligned.len, mem.page_size);301 const old_size_aligned = mem.alignForward(buf_unaligned.len, mem.page_size);
390 if (new_size_aligned <= old_size_aligned) {302 if (new_size_aligned <= old_size_aligned) {
391 return alignPageAllocLen(new_size_aligned, new_size, len_align);303 return true;
392 }304 }
393 return null;305 return false;
394 }306 }
395307
396 const buf_aligned_len = mem.alignForward(buf_unaligned.len, mem.page_size);308 const buf_aligned_len = mem.alignForward(buf_unaligned.len, mem.page_size);
397 if (new_size_aligned == buf_aligned_len)309 if (new_size_aligned == buf_aligned_len)
398 return alignPageAllocLen(new_size_aligned, new_size, len_align);310 return true;
399311
400 if (new_size_aligned < buf_aligned_len) {312 if (new_size_aligned < buf_aligned_len) {
401 const ptr = @alignCast(mem.page_size, buf_unaligned.ptr + new_size_aligned);313 const ptr = @alignCast(mem.page_size, buf_unaligned.ptr + new_size_aligned);
402 // TODO: if the next_mmap_addr_hint is within the unmapped range, update it314 // TODO: if the next_mmap_addr_hint is within the unmapped range, update it
403 os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]);315 os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]);
404 return alignPageAllocLen(new_size_aligned, new_size, len_align);316 return true;
405 }317 }
406318
407 // TODO: call mremap319 // TODO: call mremap
408 // TODO: if the next_mmap_addr_hint is within the remapped range, update it320 // TODO: if the next_mmap_addr_hint is within the remapped range, update it
409 return null;321 return false;
410 }322 }
411323
412 fn free(_: *anyopaque, buf_unaligned: []u8, buf_align: u29, return_address: usize) void {324 fn free(_: *anyopaque, slice: []u8, log2_buf_align: u8, return_address: usize) void {
413 _ = buf_align;325 _ = log2_buf_align;
414 _ = return_address;326 _ = return_address;
415327
416 if (builtin.os.tag == .windows) {328 if (builtin.os.tag == .windows) {
417 os.windows.VirtualFree(buf_unaligned.ptr, 0, os.windows.MEM_RELEASE);329 os.windows.VirtualFree(slice.ptr, 0, os.windows.MEM_RELEASE);
418 } else {330 } else {
419 const buf_aligned_len = mem.alignForward(buf_unaligned.len, mem.page_size);331 const buf_aligned_len = mem.alignForward(slice.len, mem.page_size);
420 const ptr = @alignCast(mem.page_size, buf_unaligned.ptr);332 const ptr = @alignCast(mem.page_size, slice.ptr);
421 os.munmap(ptr[0..buf_aligned_len]);333 os.munmap(ptr[0..buf_aligned_len]);
422 }334 }
423 }335 }
...@@ -478,7 +390,7 @@ const WasmPageAllocator = struct {...@@ -478,7 +390,7 @@ const WasmPageAllocator = struct {
478 // Revisit if this is settled: https://github.com/ziglang/zig/issues/3806390 // Revisit if this is settled: https://github.com/ziglang/zig/issues/3806
479 const not_found = std.math.maxInt(usize);391 const not_found = std.math.maxInt(usize);
480392
481 fn useRecycled(self: FreeBlock, num_pages: usize, alignment: u29) usize {393 fn useRecycled(self: FreeBlock, num_pages: usize, log2_align: u8) usize {
482 @setCold(true);394 @setCold(true);
483 for (self.data) |segment, i| {395 for (self.data) |segment, i| {
484 const spills_into_next = @bitCast(i128, segment) < 0;396 const spills_into_next = @bitCast(i128, segment) < 0;
...@@ -492,7 +404,7 @@ const WasmPageAllocator = struct {...@@ -492,7 +404,7 @@ const WasmPageAllocator = struct {
492 while (j + count < self.totalPages() and self.getBit(j + count) == .free) {404 while (j + count < self.totalPages() and self.getBit(j + count) == .free) {
493 count += 1;405 count += 1;
494 const addr = j * mem.page_size;406 const addr = j * mem.page_size;
495 if (count >= num_pages and mem.isAligned(addr, alignment)) {407 if (count >= num_pages and mem.isAlignedLog2(addr, log2_align)) {
496 self.setBits(j, num_pages, .used);408 self.setBits(j, num_pages, .used);
497 return j;409 return j;
498 }410 }
...@@ -521,31 +433,30 @@ const WasmPageAllocator = struct {...@@ -521,31 +433,30 @@ const WasmPageAllocator = struct {
521 return mem.alignForward(memsize, mem.page_size) / mem.page_size;433 return mem.alignForward(memsize, mem.page_size) / mem.page_size;
522 }434 }
523435
524 fn alloc(_: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) error{OutOfMemory}![]u8 {436 fn alloc(_: *anyopaque, len: usize, log2_align: u8, ra: usize) ?[*]u8 {
525 _ = ra;437 _ = ra;
526 if (len > maxInt(usize) - (mem.page_size - 1)) {438 if (len > maxInt(usize) - (mem.page_size - 1)) return null;
527 return error.OutOfMemory;
528 }
529 const page_count = nPages(len);439 const page_count = nPages(len);
530 const page_idx = try allocPages(page_count, alignment);440 const page_idx = allocPages(page_count, log2_align) catch return null;
531 return @intToPtr([*]u8, page_idx * mem.page_size)[0..alignPageAllocLen(page_count * mem.page_size, len, len_align)];441 return @intToPtr([*]u8, page_idx * mem.page_size);
532 }442 }
533 fn allocPages(page_count: usize, alignment: u29) !usize {443
444 fn allocPages(page_count: usize, log2_align: u8) !usize {
534 {445 {
535 const idx = conventional.useRecycled(page_count, alignment);446 const idx = conventional.useRecycled(page_count, log2_align);
536 if (idx != FreeBlock.not_found) {447 if (idx != FreeBlock.not_found) {
537 return idx;448 return idx;
538 }449 }
539 }450 }
540451
541 const idx = extended.useRecycled(page_count, alignment);452 const idx = extended.useRecycled(page_count, log2_align);
542 if (idx != FreeBlock.not_found) {453 if (idx != FreeBlock.not_found) {
543 return idx + extendedOffset();454 return idx + extendedOffset();
544 }455 }
545456
546 const next_page_idx = @wasmMemorySize(0);457 const next_page_idx = @wasmMemorySize(0);
547 const next_page_addr = next_page_idx * mem.page_size;458 const next_page_addr = next_page_idx * mem.page_size;
548 const aligned_addr = mem.alignForward(next_page_addr, alignment);459 const aligned_addr = mem.alignForwardLog2(next_page_addr, log2_align);
549 const drop_page_count = @divExact(aligned_addr - next_page_addr, mem.page_size);460 const drop_page_count = @divExact(aligned_addr - next_page_addr, mem.page_size);
550 const result = @wasmMemoryGrow(0, @intCast(u32, drop_page_count + page_count));461 const result = @wasmMemoryGrow(0, @intCast(u32, drop_page_count + page_count));
551 if (result <= 0)462 if (result <= 0)
...@@ -573,7 +484,7 @@ const WasmPageAllocator = struct {...@@ -573,7 +484,7 @@ const WasmPageAllocator = struct {
573 // Since this is the first page being freed and we consume it, assume *nothing* is free.484 // Since this is the first page being freed and we consume it, assume *nothing* is free.
574 mem.set(u128, extended.data, PageStatus.none_free);485 mem.set(u128, extended.data, PageStatus.none_free);
575 }486 }
576 const clamped_start = std.math.max(extendedOffset(), start);487 const clamped_start = @max(extendedOffset(), start);
577 extended.recycle(clamped_start - extendedOffset(), new_end - clamped_start);488 extended.recycle(clamped_start - extendedOffset(), new_end - clamped_start);
578 }489 }
579 }490 }
...@@ -581,31 +492,30 @@ const WasmPageAllocator = struct {...@@ -581,31 +492,30 @@ const WasmPageAllocator = struct {
581 fn resize(492 fn resize(
582 _: *anyopaque,493 _: *anyopaque,
583 buf: []u8,494 buf: []u8,
584 buf_align: u29,495 log2_buf_align: u8,
585 new_len: usize,496 new_len: usize,
586 len_align: u29,
587 return_address: usize,497 return_address: usize,
588 ) ?usize {498 ) bool {
589 _ = buf_align;499 _ = log2_buf_align;
590 _ = return_address;500 _ = return_address;
591 const aligned_len = mem.alignForward(buf.len, mem.page_size);501 const aligned_len = mem.alignForward(buf.len, mem.page_size);
592 if (new_len > aligned_len) return null;502 if (new_len > aligned_len) return false;
593 const current_n = nPages(aligned_len);503 const current_n = nPages(aligned_len);
594 const new_n = nPages(new_len);504 const new_n = nPages(new_len);
595 if (new_n != current_n) {505 if (new_n != current_n) {
596 const base = nPages(@ptrToInt(buf.ptr));506 const base = nPages(@ptrToInt(buf.ptr));
597 freePages(base + new_n, base + current_n);507 freePages(base + new_n, base + current_n);
598 }508 }
599 return alignPageAllocLen(new_n * mem.page_size, new_len, len_align);509 return true;
600 }510 }
601511
602 fn free(512 fn free(
603 _: *anyopaque,513 _: *anyopaque,
604 buf: []u8,514 buf: []u8,
605 buf_align: u29,515 log2_buf_align: u8,
606 return_address: usize,516 return_address: usize,
607 ) void {517 ) void {
608 _ = buf_align;518 _ = log2_buf_align;
609 _ = return_address;519 _ = return_address;
610 const aligned_len = mem.alignForward(buf.len, mem.page_size);520 const aligned_len = mem.alignForward(buf.len, mem.page_size);
611 const current_n = nPages(aligned_len);521 const current_n = nPages(aligned_len);
...@@ -627,7 +537,14 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -627,7 +537,14 @@ pub const HeapAllocator = switch (builtin.os.tag) {
627 }537 }
628538
629 pub fn allocator(self: *HeapAllocator) Allocator {539 pub fn allocator(self: *HeapAllocator) Allocator {
630 return Allocator.init(self, alloc, resize, free);540 return .{
541 .ptr = self,
542 .vtable = &.{
543 .alloc = alloc,
544 .resize = resize,
545 .free = free,
546 },
547 };
631 }548 }
632549
633 pub fn deinit(self: *HeapAllocator) void {550 pub fn deinit(self: *HeapAllocator) void {
...@@ -641,48 +558,42 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -641,48 +558,42 @@ pub const HeapAllocator = switch (builtin.os.tag) {
641 }558 }
642559
643 fn alloc(560 fn alloc(
644 self: *HeapAllocator,561 ctx: *anyopaque,
645 n: usize,562 n: usize,
646 ptr_align: u29,563 log2_ptr_align: u8,
647 len_align: u29,
648 return_address: usize,564 return_address: usize,
649 ) error{OutOfMemory}![]u8 {565 ) ?[*]u8 {
650 _ = return_address;566 _ = return_address;
567 const self = @ptrCast(*HeapAllocator, @alignCast(@alignOf(HeapAllocator), ctx));
651568
569 const ptr_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align);
652 const amt = n + ptr_align - 1 + @sizeOf(usize);570 const amt = n + ptr_align - 1 + @sizeOf(usize);
653 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, .SeqCst);571 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, .SeqCst);
654 const heap_handle = optional_heap_handle orelse blk: {572 const heap_handle = optional_heap_handle orelse blk: {
655 const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;573 const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;
656 const hh = os.windows.kernel32.HeapCreate(options, amt, 0) orelse return error.OutOfMemory;574 const hh = os.windows.kernel32.HeapCreate(options, amt, 0) orelse return null;
657 const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, .SeqCst, .SeqCst) orelse break :blk hh;575 const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, .SeqCst, .SeqCst) orelse break :blk hh;
658 os.windows.HeapDestroy(hh);576 os.windows.HeapDestroy(hh);
659 break :blk other_hh.?; // can't be null because of the cmpxchg577 break :blk other_hh.?; // can't be null because of the cmpxchg
660 };578 };
661 const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;579 const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return null;
662 const root_addr = @ptrToInt(ptr);580 const root_addr = @ptrToInt(ptr);
663 const aligned_addr = mem.alignForward(root_addr, ptr_align);581 const aligned_addr = mem.alignForward(root_addr, ptr_align);
664 const return_len = init: {582 const buf = @intToPtr([*]u8, aligned_addr)[0..n];
665 if (len_align == 0) break :init n;
666 const full_len = os.windows.kernel32.HeapSize(heap_handle, 0, ptr);
667 assert(full_len != std.math.maxInt(usize));
668 assert(full_len >= amt);
669 break :init mem.alignBackwardAnyAlign(full_len - (aligned_addr - root_addr) - @sizeOf(usize), len_align);
670 };
671 const buf = @intToPtr([*]u8, aligned_addr)[0..return_len];
672 getRecordPtr(buf).* = root_addr;583 getRecordPtr(buf).* = root_addr;
673 return buf;584 return buf.ptr;
674 }585 }
675586
676 fn resize(587 fn resize(
677 self: *HeapAllocator,588 ctx: *anyopaque,
678 buf: []u8,589 buf: []u8,
679 buf_align: u29,590 log2_buf_align: u8,
680 new_size: usize,591 new_size: usize,
681 len_align: u29,
682 return_address: usize,592 return_address: usize,
683 ) ?usize {593 ) bool {
684 _ = buf_align;594 _ = log2_buf_align;
685 _ = return_address;595 _ = return_address;
596 const self = @ptrCast(*HeapAllocator, @alignCast(@alignOf(HeapAllocator), ctx));
686597
687 const root_addr = getRecordPtr(buf).*;598 const root_addr = getRecordPtr(buf).*;
688 const align_offset = @ptrToInt(buf.ptr) - root_addr;599 const align_offset = @ptrToInt(buf.ptr) - root_addr;
...@@ -692,27 +603,21 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -692,27 +603,21 @@ pub const HeapAllocator = switch (builtin.os.tag) {
692 os.windows.HEAP_REALLOC_IN_PLACE_ONLY,603 os.windows.HEAP_REALLOC_IN_PLACE_ONLY,
693 @intToPtr(*anyopaque, root_addr),604 @intToPtr(*anyopaque, root_addr),
694 amt,605 amt,
695 ) orelse return null;606 ) orelse return false;
696 assert(new_ptr == @intToPtr(*anyopaque, root_addr));607 assert(new_ptr == @intToPtr(*anyopaque, root_addr));
697 const return_len = init: {608 getRecordPtr(buf.ptr[0..new_size]).* = root_addr;
698 if (len_align == 0) break :init new_size;609 return true;
699 const full_len = os.windows.kernel32.HeapSize(self.heap_handle.?, 0, new_ptr);
700 assert(full_len != std.math.maxInt(usize));
701 assert(full_len >= amt);
702 break :init mem.alignBackwardAnyAlign(full_len - align_offset, len_align);
703 };
704 getRecordPtr(buf.ptr[0..return_len]).* = root_addr;
705 return return_len;
706 }610 }
707611
708 fn free(612 fn free(
709 self: *HeapAllocator,613 ctx: *anyopaque,
710 buf: []u8,614 buf: []u8,
711 buf_align: u29,615 log2_buf_align: u8,
712 return_address: usize,616 return_address: usize,
713 ) void {617 ) void {
714 _ = buf_align;618 _ = log2_buf_align;
715 _ = return_address;619 _ = return_address;
620 const self = @ptrCast(*HeapAllocator, @alignCast(@alignOf(HeapAllocator), ctx));
716 os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*anyopaque, getRecordPtr(buf).*));621 os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*anyopaque, getRecordPtr(buf).*));
717 }622 }
718 },623 },
...@@ -742,18 +647,27 @@ pub const FixedBufferAllocator = struct {...@@ -742,18 +647,27 @@ pub const FixedBufferAllocator = struct {
742647
743 /// *WARNING* using this at the same time as the interface returned by `threadSafeAllocator` is not thread safe648 /// *WARNING* using this at the same time as the interface returned by `threadSafeAllocator` is not thread safe
744 pub fn allocator(self: *FixedBufferAllocator) Allocator {649 pub fn allocator(self: *FixedBufferAllocator) Allocator {
745 return Allocator.init(self, alloc, resize, free);650 return .{
651 .ptr = self,
652 .vtable = &.{
653 .alloc = alloc,
654 .resize = resize,
655 .free = free,
656 },
657 };
746 }658 }
747659
748 /// Provides a lock free thread safe `Allocator` interface to the underlying `FixedBufferAllocator`660 /// Provides a lock free thread safe `Allocator` interface to the underlying `FixedBufferAllocator`
749 /// *WARNING* using this at the same time as the interface returned by `allocator` is not thread safe661 /// *WARNING* using this at the same time as the interface returned by `allocator` is not thread safe
750 pub fn threadSafeAllocator(self: *FixedBufferAllocator) Allocator {662 pub fn threadSafeAllocator(self: *FixedBufferAllocator) Allocator {
751 return Allocator.init(663 return .{
752 self,664 .ptr = self,
753 threadSafeAlloc,665 .vtable = &.{
754 Allocator.NoResize(FixedBufferAllocator).noResize,666 .alloc = threadSafeAlloc,
755 Allocator.NoOpFree(FixedBufferAllocator).noOpFree,667 .resize = Allocator.noResize,
756 );668 .free = Allocator.noFree,
669 },
670 };
757 }671 }
758672
759 pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool {673 pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool {
...@@ -771,59 +685,56 @@ pub const FixedBufferAllocator = struct {...@@ -771,59 +685,56 @@ pub const FixedBufferAllocator = struct {
771 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;685 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;
772 }686 }
773687
774 fn alloc(self: *FixedBufferAllocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 {688 fn alloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
775 _ = len_align;689 const self = @ptrCast(*FixedBufferAllocator, @alignCast(@alignOf(FixedBufferAllocator), ctx));
776 _ = ra;690 _ = ra;
777 const adjust_off = mem.alignPointerOffset(self.buffer.ptr + self.end_index, ptr_align) orelse691 const ptr_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align);
778 return error.OutOfMemory;692 const adjust_off = mem.alignPointerOffset(self.buffer.ptr + self.end_index, ptr_align) orelse return null;
779 const adjusted_index = self.end_index + adjust_off;693 const adjusted_index = self.end_index + adjust_off;
780 const new_end_index = adjusted_index + n;694 const new_end_index = adjusted_index + n;
781 if (new_end_index > self.buffer.len) {695 if (new_end_index > self.buffer.len) return null;
782 return error.OutOfMemory;
783 }
784 const result = self.buffer[adjusted_index..new_end_index];
785 self.end_index = new_end_index;696 self.end_index = new_end_index;
786697 return self.buffer.ptr + adjusted_index;
787 return result;
788 }698 }
789699
790 fn resize(700 fn resize(
791 self: *FixedBufferAllocator,701 ctx: *anyopaque,
792 buf: []u8,702 buf: []u8,
793 buf_align: u29,703 log2_buf_align: u8,
794 new_size: usize,704 new_size: usize,
795 len_align: u29,
796 return_address: usize,705 return_address: usize,
797 ) ?usize {706 ) bool {
798 _ = buf_align;707 const self = @ptrCast(*FixedBufferAllocator, @alignCast(@alignOf(FixedBufferAllocator), ctx));
708 _ = log2_buf_align;
799 _ = return_address;709 _ = return_address;
800 assert(self.ownsSlice(buf)); // sanity check710 assert(self.ownsSlice(buf)); // sanity check
801711
802 if (!self.isLastAllocation(buf)) {712 if (!self.isLastAllocation(buf)) {
803 if (new_size > buf.len) return null;713 if (new_size > buf.len) return false;
804 return mem.alignAllocLen(buf.len, new_size, len_align);714 return true;
805 }715 }
806716
807 if (new_size <= buf.len) {717 if (new_size <= buf.len) {
808 const sub = buf.len - new_size;718 const sub = buf.len - new_size;
809 self.end_index -= sub;719 self.end_index -= sub;
810 return mem.alignAllocLen(buf.len - sub, new_size, len_align);720 return true;
811 }721 }
812722
813 const add = new_size - buf.len;723 const add = new_size - buf.len;
814 if (add + self.end_index > self.buffer.len) return null;724 if (add + self.end_index > self.buffer.len) return false;
815725
816 self.end_index += add;726 self.end_index += add;
817 return new_size;727 return true;
818 }728 }
819729
820 fn free(730 fn free(
821 self: *FixedBufferAllocator,731 ctx: *anyopaque,
822 buf: []u8,732 buf: []u8,
823 buf_align: u29,733 log2_buf_align: u8,
824 return_address: usize,734 return_address: usize,
825 ) void {735 ) void {
826 _ = buf_align;736 const self = @ptrCast(*FixedBufferAllocator, @alignCast(@alignOf(FixedBufferAllocator), ctx));
737 _ = log2_buf_align;
827 _ = return_address;738 _ = return_address;
828 assert(self.ownsSlice(buf)); // sanity check739 assert(self.ownsSlice(buf)); // sanity check
829740
...@@ -832,19 +743,18 @@ pub const FixedBufferAllocator = struct {...@@ -832,19 +743,18 @@ pub const FixedBufferAllocator = struct {
832 }743 }
833 }744 }
834745
835 fn threadSafeAlloc(self: *FixedBufferAllocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 {746 fn threadSafeAlloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
836 _ = len_align;747 const self = @ptrCast(*FixedBufferAllocator, @alignCast(@alignOf(FixedBufferAllocator), ctx));
837 _ = ra;748 _ = ra;
749 const ptr_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align);
838 var end_index = @atomicLoad(usize, &self.end_index, .SeqCst);750 var end_index = @atomicLoad(usize, &self.end_index, .SeqCst);
839 while (true) {751 while (true) {
840 const adjust_off = mem.alignPointerOffset(self.buffer.ptr + end_index, ptr_align) orelse752 const adjust_off = mem.alignPointerOffset(self.buffer.ptr + end_index, ptr_align) orelse return null;
841 return error.OutOfMemory;
842 const adjusted_index = end_index + adjust_off;753 const adjusted_index = end_index + adjust_off;
843 const new_end_index = adjusted_index + n;754 const new_end_index = adjusted_index + n;
844 if (new_end_index > self.buffer.len) {755 if (new_end_index > self.buffer.len) return null;
845 return error.OutOfMemory;756 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, .SeqCst, .SeqCst) orelse
846 }757 return self.buffer[adjusted_index..new_end_index].ptr;
847 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, .SeqCst, .SeqCst) orelse return self.buffer[adjusted_index..new_end_index];
848 }758 }
849 }759 }
850760
...@@ -878,48 +788,57 @@ pub fn StackFallbackAllocator(comptime size: usize) type {...@@ -878,48 +788,57 @@ pub fn StackFallbackAllocator(comptime size: usize) type {
878 fallback_allocator: Allocator,788 fallback_allocator: Allocator,
879 fixed_buffer_allocator: FixedBufferAllocator,789 fixed_buffer_allocator: FixedBufferAllocator,
880790
881 /// WARNING: This functions both fetches a `std.mem.Allocator` interface to this allocator *and* resets the internal buffer allocator791 /// This function both fetches a `Allocator` interface to this
792 /// allocator *and* resets the internal buffer allocator.
882 pub fn get(self: *Self) Allocator {793 pub fn get(self: *Self) Allocator {
883 self.fixed_buffer_allocator = FixedBufferAllocator.init(self.buffer[0..]);794 self.fixed_buffer_allocator = FixedBufferAllocator.init(self.buffer[0..]);
884 return Allocator.init(self, alloc, resize, free);795 return .{
796 .ptr = self,
797 .vtable = &.{
798 .alloc = alloc,
799 .resize = resize,
800 .free = free,
801 },
802 };
885 }803 }
886804
887 fn alloc(805 fn alloc(
888 self: *Self,806 ctx: *anyopaque,
889 len: usize,807 len: usize,
890 ptr_align: u29,808 log2_ptr_align: u8,
891 len_align: u29,809 ra: usize,
892 return_address: usize,810 ) ?[*]u8 {
893 ) error{OutOfMemory}![]u8 {811 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
894 return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align, len_align, return_address) catch812 return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, log2_ptr_align, ra) orelse
895 return self.fallback_allocator.rawAlloc(len, ptr_align, len_align, return_address);813 return self.fallback_allocator.rawAlloc(len, log2_ptr_align, ra);
896 }814 }
897815
898 fn resize(816 fn resize(
899 self: *Self,817 ctx: *anyopaque,
900 buf: []u8,818 buf: []u8,
901 buf_align: u29,819 log2_buf_align: u8,
902 new_len: usize,820 new_len: usize,
903 len_align: u29,821 ra: usize,
904 return_address: usize,822 ) bool {
905 ) ?usize {823 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
906 if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {824 if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
907 return FixedBufferAllocator.resize(&self.fixed_buffer_allocator, buf, buf_align, new_len, len_align, return_address);825 return FixedBufferAllocator.resize(&self.fixed_buffer_allocator, buf, log2_buf_align, new_len, ra);
908 } else {826 } else {
909 return self.fallback_allocator.rawResize(buf, buf_align, new_len, len_align, return_address);827 return self.fallback_allocator.rawResize(buf, log2_buf_align, new_len, ra);
910 }828 }
911 }829 }
912830
913 fn free(831 fn free(
914 self: *Self,832 ctx: *anyopaque,
915 buf: []u8,833 buf: []u8,
916 buf_align: u29,834 log2_buf_align: u8,
917 return_address: usize,835 ra: usize,
918 ) void {836 ) void {
837 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
919 if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {838 if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
920 return FixedBufferAllocator.free(&self.fixed_buffer_allocator, buf, buf_align, return_address);839 return FixedBufferAllocator.free(&self.fixed_buffer_allocator, buf, log2_buf_align, ra);
921 } else {840 } else {
922 return self.fallback_allocator.rawFree(buf, buf_align, return_address);841 return self.fallback_allocator.rawFree(buf, log2_buf_align, ra);
923 }842 }
924 }843 }
925 };844 };
...@@ -987,11 +906,7 @@ test "PageAllocator" {...@@ -987,11 +906,7 @@ test "PageAllocator" {
987 }906 }
988907
989 if (builtin.os.tag == .windows) {908 if (builtin.os.tag == .windows) {
990 // Trying really large alignment. As mentionned in the implementation,909 const slice = try allocator.alignedAlloc(u8, mem.page_size, 128);
991 // VirtualAlloc returns 64K aligned addresses. We want to make sure
992 // PageAllocator works beyond that, as it's not tested by
993 // `testAllocatorLargeAlignment`.
994 const slice = try allocator.alignedAlloc(u8, 1 << 20, 128);
995 slice[0] = 0x12;910 slice[0] = 0x12;
996 slice[127] = 0x34;911 slice[127] = 0x34;
997 allocator.free(slice);912 allocator.free(slice);
...@@ -1132,15 +1047,16 @@ pub fn testAllocator(base_allocator: mem.Allocator) !void {...@@ -1132,15 +1047,16 @@ pub fn testAllocator(base_allocator: mem.Allocator) !void {
1132 allocator.destroy(item);1047 allocator.destroy(item);
1133 }1048 }
11341049
1135 slice = allocator.shrink(slice, 50);1050 if (allocator.resize(slice, 50)) {
1136 try testing.expect(slice.len == 50);1051 slice = slice[0..50];
1137 slice = allocator.shrink(slice, 25);1052 if (allocator.resize(slice, 25)) {
1138 try testing.expect(slice.len == 25);1053 slice = slice[0..25];
1139 slice = allocator.shrink(slice, 0);1054 try testing.expect(allocator.resize(slice, 0));
1140 try testing.expect(slice.len == 0);1055 slice = slice[0..0];
1141 slice = try allocator.realloc(slice, 10);1056 slice = try allocator.realloc(slice, 10);
1142 try testing.expect(slice.len == 10);1057 try testing.expect(slice.len == 10);
11431058 }
1059 }
1144 allocator.free(slice);1060 allocator.free(slice);
11451061
1146 // Zero-length allocation1062 // Zero-length allocation
...@@ -1151,7 +1067,7 @@ pub fn testAllocator(base_allocator: mem.Allocator) !void {...@@ -1151,7 +1067,7 @@ pub fn testAllocator(base_allocator: mem.Allocator) !void {
1151 zero_bit_ptr.* = 0;1067 zero_bit_ptr.* = 0;
1152 allocator.destroy(zero_bit_ptr);1068 allocator.destroy(zero_bit_ptr);
11531069
1154 const oversize = try allocator.allocAdvanced(u32, null, 5, .at_least);1070 const oversize = try allocator.alignedAlloc(u32, null, 5);
1155 try testing.expect(oversize.len >= 5);1071 try testing.expect(oversize.len >= 5);
1156 for (oversize) |*item| {1072 for (oversize) |*item| {
1157 item.* = 0xDEADBEEF;1073 item.* = 0xDEADBEEF;
...@@ -1171,21 +1087,18 @@ pub fn testAllocatorAligned(base_allocator: mem.Allocator) !void {...@@ -1171,21 +1087,18 @@ pub fn testAllocatorAligned(base_allocator: mem.Allocator) !void {
1171 // grow1087 // grow
1172 slice = try allocator.realloc(slice, 100);1088 slice = try allocator.realloc(slice, 100);
1173 try testing.expect(slice.len == 100);1089 try testing.expect(slice.len == 100);
1174 // shrink1090 if (allocator.resize(slice, 10)) {
1175 slice = allocator.shrink(slice, 10);1091 slice = slice[0..10];
1176 try testing.expect(slice.len == 10);1092 }
1177 // go to zero1093 try testing.expect(allocator.resize(slice, 0));
1178 slice = allocator.shrink(slice, 0);1094 slice = slice[0..0];
1179 try testing.expect(slice.len == 0);
1180 // realloc from zero1095 // realloc from zero
1181 slice = try allocator.realloc(slice, 100);1096 slice = try allocator.realloc(slice, 100);
1182 try testing.expect(slice.len == 100);1097 try testing.expect(slice.len == 100);
1183 // shrink with shrink1098 if (allocator.resize(slice, 10)) {
1184 slice = allocator.shrink(slice, 10);1099 slice = slice[0..10];
1185 try testing.expect(slice.len == 10);1100 }
1186 // shrink to zero1101 try testing.expect(allocator.resize(slice, 0));
1187 slice = allocator.shrink(slice, 0);
1188 try testing.expect(slice.len == 0);
1189 }1102 }
1190}1103}
11911104
...@@ -1193,27 +1106,24 @@ pub fn testAllocatorLargeAlignment(base_allocator: mem.Allocator) !void {...@@ -1193,27 +1106,24 @@ pub fn testAllocatorLargeAlignment(base_allocator: mem.Allocator) !void {
1193 var validationAllocator = mem.validationWrap(base_allocator);1106 var validationAllocator = mem.validationWrap(base_allocator);
1194 const allocator = validationAllocator.allocator();1107 const allocator = validationAllocator.allocator();
11951108
1196 //Maybe a platform's page_size is actually the same as or1109 const large_align: usize = mem.page_size / 2;
1197 // very near usize?
1198 if (mem.page_size << 2 > maxInt(usize)) return;
1199
1200 const USizeShift = std.meta.Int(.unsigned, std.math.log2(@bitSizeOf(usize)));
1201 const large_align = @as(u29, mem.page_size << 2);
12021110
1203 var align_mask: usize = undefined;1111 var align_mask: usize = undefined;
1204 _ = @shlWithOverflow(usize, ~@as(usize, 0), @as(USizeShift, @ctz(large_align)), &align_mask);1112 _ = @shlWithOverflow(usize, ~@as(usize, 0), @as(Allocator.Log2Align, @ctz(large_align)), &align_mask);
12051113
1206 var slice = try allocator.alignedAlloc(u8, large_align, 500);1114 var slice = try allocator.alignedAlloc(u8, large_align, 500);
1207 try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));1115 try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
12081116
1209 slice = allocator.shrink(slice, 100);1117 if (allocator.resize(slice, 100)) {
1210 try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));1118 slice = slice[0..100];
1119 }
12111120
1212 slice = try allocator.realloc(slice, 5000);1121 slice = try allocator.realloc(slice, 5000);
1213 try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));1122 try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
12141123
1215 slice = allocator.shrink(slice, 10);1124 if (allocator.resize(slice, 10)) {
1216 try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));1125 slice = slice[0..10];
1126 }
12171127
1218 slice = try allocator.realloc(slice, 20000);1128 slice = try allocator.realloc(slice, 20000);
1219 try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));1129 try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
...@@ -1248,8 +1158,7 @@ pub fn testAllocatorAlignedShrink(base_allocator: mem.Allocator) !void {...@@ -1248,8 +1158,7 @@ pub fn testAllocatorAlignedShrink(base_allocator: mem.Allocator) !void {
1248 slice[0] = 0x12;1158 slice[0] = 0x12;
1249 slice[60] = 0x34;1159 slice[60] = 0x34;
12501160
1251 // realloc to a smaller size but with a larger alignment1161 slice = try allocator.reallocAdvanced(slice, alloc_size / 2, 0);
1252 slice = try allocator.reallocAdvanced(slice, mem.page_size * 32, alloc_size / 2, .exact);
1253 try testing.expect(slice[0] == 0x12);1162 try testing.expect(slice[0] == 0x12);
1254 try testing.expect(slice[60] == 0x34);1163 try testing.expect(slice[60] == 0x34);
1255}1164}
lib/std/heap/arena_allocator.zig+40-25
...@@ -24,7 +24,14 @@ pub const ArenaAllocator = struct {...@@ -24,7 +24,14 @@ pub const ArenaAllocator = struct {
24 };24 };
2525
26 pub fn allocator(self: *ArenaAllocator) Allocator {26 pub fn allocator(self: *ArenaAllocator) Allocator {
27 return Allocator.init(self, alloc, resize, free);27 return .{
28 .ptr = self,
29 .vtable = &.{
30 .alloc = alloc,
31 .resize = resize,
32 .free = free,
33 },
34 };
28 }35 }
2936
30 const BufNode = std.SinglyLinkedList([]u8).Node;37 const BufNode = std.SinglyLinkedList([]u8).Node;
...@@ -43,14 +50,16 @@ pub const ArenaAllocator = struct {...@@ -43,14 +50,16 @@ pub const ArenaAllocator = struct {
43 }50 }
44 }51 }
4552
46 fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode {53 fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) ?*BufNode {
47 const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16);54 const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16);
48 const big_enough_len = prev_len + actual_min_size;55 const big_enough_len = prev_len + actual_min_size;
49 const len = big_enough_len + big_enough_len / 2;56 const len = big_enough_len + big_enough_len / 2;
50 const buf = try self.child_allocator.rawAlloc(len, @alignOf(BufNode), 1, @returnAddress());57 const log2_align = comptime std.math.log2_int(usize, @alignOf(BufNode));
51 const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), buf.ptr));58 const ptr = self.child_allocator.rawAlloc(len, log2_align, @returnAddress()) orelse
59 return null;
60 const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), ptr));
52 buf_node.* = BufNode{61 buf_node.* = BufNode{
53 .data = buf,62 .data = ptr[0..len],
54 .next = null,63 .next = null,
55 };64 };
56 self.state.buffer_list.prepend(buf_node);65 self.state.buffer_list.prepend(buf_node);
...@@ -58,11 +67,15 @@ pub const ArenaAllocator = struct {...@@ -58,11 +67,15 @@ pub const ArenaAllocator = struct {
58 return buf_node;67 return buf_node;
59 }68 }
6069
61 fn alloc(self: *ArenaAllocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 {70 fn alloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
62 _ = len_align;71 const self = @ptrCast(*ArenaAllocator, @alignCast(@alignOf(ArenaAllocator), ctx));
63 _ = ra;72 _ = ra;
6473
65 var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + ptr_align);74 const ptr_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align);
75 var cur_node = if (self.state.buffer_list.first) |first_node|
76 first_node
77 else
78 (self.createNode(0, n + ptr_align) orelse return null);
66 while (true) {79 while (true) {
67 const cur_buf = cur_node.data[@sizeOf(BufNode)..];80 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
68 const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index;81 const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index;
...@@ -73,46 +86,48 @@ pub const ArenaAllocator = struct {...@@ -73,46 +86,48 @@ pub const ArenaAllocator = struct {
73 if (new_end_index <= cur_buf.len) {86 if (new_end_index <= cur_buf.len) {
74 const result = cur_buf[adjusted_index..new_end_index];87 const result = cur_buf[adjusted_index..new_end_index];
75 self.state.end_index = new_end_index;88 self.state.end_index = new_end_index;
76 return result;89 return result.ptr;
77 }90 }
7891
79 const bigger_buf_size = @sizeOf(BufNode) + new_end_index;92 const bigger_buf_size = @sizeOf(BufNode) + new_end_index;
80 // Try to grow the buffer in-place93 if (self.child_allocator.resize(cur_node.data, bigger_buf_size)) {
81 cur_node.data = self.child_allocator.resize(cur_node.data, bigger_buf_size) orelse {94 cur_node.data.len = bigger_buf_size;
95 } else {
82 // Allocate a new node if that's not possible96 // Allocate a new node if that's not possible
83 cur_node = try self.createNode(cur_buf.len, n + ptr_align);97 cur_node = self.createNode(cur_buf.len, n + ptr_align) orelse return null;
84 continue;98 }
85 };
86 }99 }
87 }100 }
88101
89 fn resize(self: *ArenaAllocator, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) ?usize {102 fn resize(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, new_len: usize, ret_addr: usize) bool {
90 _ = buf_align;103 const self = @ptrCast(*ArenaAllocator, @alignCast(@alignOf(ArenaAllocator), ctx));
91 _ = len_align;104 _ = log2_buf_align;
92 _ = ret_addr;105 _ = ret_addr;
93106
94 const cur_node = self.state.buffer_list.first orelse return null;107 const cur_node = self.state.buffer_list.first orelse return false;
95 const cur_buf = cur_node.data[@sizeOf(BufNode)..];108 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
96 if (@ptrToInt(cur_buf.ptr) + self.state.end_index != @ptrToInt(buf.ptr) + buf.len) {109 if (@ptrToInt(cur_buf.ptr) + self.state.end_index != @ptrToInt(buf.ptr) + buf.len) {
97 if (new_len > buf.len) return null;110 if (new_len > buf.len) return false;
98 return new_len;111 return true;
99 }112 }
100113
101 if (buf.len >= new_len) {114 if (buf.len >= new_len) {
102 self.state.end_index -= buf.len - new_len;115 self.state.end_index -= buf.len - new_len;
103 return new_len;116 return true;
104 } else if (cur_buf.len - self.state.end_index >= new_len - buf.len) {117 } else if (cur_buf.len - self.state.end_index >= new_len - buf.len) {
105 self.state.end_index += new_len - buf.len;118 self.state.end_index += new_len - buf.len;
106 return new_len;119 return true;
107 } else {120 } else {
108 return null;121 return false;
109 }122 }
110 }123 }
111124
112 fn free(self: *ArenaAllocator, buf: []u8, buf_align: u29, ret_addr: usize) void {125 fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ret_addr: usize) void {
113 _ = buf_align;126 _ = log2_buf_align;
114 _ = ret_addr;127 _ = ret_addr;
115128
129 const self = @ptrCast(*ArenaAllocator, @alignCast(@alignOf(ArenaAllocator), ctx));
130
116 const cur_node = self.state.buffer_list.first orelse return;131 const cur_node = self.state.buffer_list.first orelse return;
117 const cur_buf = cur_node.data[@sizeOf(BufNode)..];132 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
118133
lib/std/heap/general_purpose_allocator.zig+81-57
...@@ -199,7 +199,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -199,7 +199,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
199 requested_size: if (config.enable_memory_limit) usize else void,199 requested_size: if (config.enable_memory_limit) usize else void,
200 stack_addresses: [trace_n][stack_n]usize,200 stack_addresses: [trace_n][stack_n]usize,
201 freed: if (config.retain_metadata) bool else void,201 freed: if (config.retain_metadata) bool else void,
202 ptr_align: if (config.never_unmap and config.retain_metadata) u29 else void,202 log2_ptr_align: if (config.never_unmap and config.retain_metadata) u8 else void,
203203
204 const trace_n = if (config.retain_metadata) traces_per_slot else 1;204 const trace_n = if (config.retain_metadata) traces_per_slot else 1;
205205
...@@ -271,7 +271,14 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -271,7 +271,14 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
271 };271 };
272272
273 pub fn allocator(self: *Self) Allocator {273 pub fn allocator(self: *Self) Allocator {
274 return Allocator.init(self, alloc, resize, free);274 return .{
275 .ptr = self,
276 .vtable = &.{
277 .alloc = alloc,
278 .resize = resize,
279 .free = free,
280 },
281 };
275 }282 }
276283
277 fn bucketStackTrace(284 fn bucketStackTrace(
...@@ -379,7 +386,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -379,7 +386,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
379 var it = self.large_allocations.iterator();386 var it = self.large_allocations.iterator();
380 while (it.next()) |large| {387 while (it.next()) |large| {
381 if (large.value_ptr.freed) {388 if (large.value_ptr.freed) {
382 self.backing_allocator.rawFree(large.value_ptr.bytes, large.value_ptr.ptr_align, @returnAddress());389 self.backing_allocator.rawFree(large.value_ptr.bytes, large.value_ptr.log2_ptr_align, @returnAddress());
383 }390 }
384 }391 }
385 }392 }
...@@ -504,11 +511,10 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -504,11 +511,10 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
504 fn resizeLarge(511 fn resizeLarge(
505 self: *Self,512 self: *Self,
506 old_mem: []u8,513 old_mem: []u8,
507 old_align: u29,514 log2_old_align: u8,
508 new_size: usize,515 new_size: usize,
509 len_align: u29,
510 ret_addr: usize,516 ret_addr: usize,
511 ) ?usize {517 ) bool {
512 const entry = self.large_allocations.getEntry(@ptrToInt(old_mem.ptr)) orelse {518 const entry = self.large_allocations.getEntry(@ptrToInt(old_mem.ptr)) orelse {
513 if (config.safety) {519 if (config.safety) {
514 @panic("Invalid free");520 @panic("Invalid free");
...@@ -541,24 +547,26 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -541,24 +547,26 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
541 });547 });
542 }548 }
543549
544 // Do memory limit accounting with requested sizes rather than what backing_allocator returns550 // Do memory limit accounting with requested sizes rather than what
545 // because if we want to return error.OutOfMemory, we have to leave allocation untouched, and551 // backing_allocator returns because if we want to return
546 // that is impossible to guarantee after calling backing_allocator.rawResize.552 // error.OutOfMemory, we have to leave allocation untouched, and
553 // that is impossible to guarantee after calling
554 // backing_allocator.rawResize.
547 const prev_req_bytes = self.total_requested_bytes;555 const prev_req_bytes = self.total_requested_bytes;
548 if (config.enable_memory_limit) {556 if (config.enable_memory_limit) {
549 const new_req_bytes = prev_req_bytes + new_size - entry.value_ptr.requested_size;557 const new_req_bytes = prev_req_bytes + new_size - entry.value_ptr.requested_size;
550 if (new_req_bytes > prev_req_bytes and new_req_bytes > self.requested_memory_limit) {558 if (new_req_bytes > prev_req_bytes and new_req_bytes > self.requested_memory_limit) {
551 return null;559 return false;
552 }560 }
553 self.total_requested_bytes = new_req_bytes;561 self.total_requested_bytes = new_req_bytes;
554 }562 }
555563
556 const result_len = self.backing_allocator.rawResize(old_mem, old_align, new_size, len_align, ret_addr) orelse {564 if (!self.backing_allocator.rawResize(old_mem, log2_old_align, new_size, ret_addr)) {
557 if (config.enable_memory_limit) {565 if (config.enable_memory_limit) {
558 self.total_requested_bytes = prev_req_bytes;566 self.total_requested_bytes = prev_req_bytes;
559 }567 }
560 return null;568 return false;
561 };569 }
562570
563 if (config.enable_memory_limit) {571 if (config.enable_memory_limit) {
564 entry.value_ptr.requested_size = new_size;572 entry.value_ptr.requested_size = new_size;
...@@ -569,9 +577,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -569,9 +577,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
569 old_mem.len, old_mem.ptr, new_size,577 old_mem.len, old_mem.ptr, new_size,
570 });578 });
571 }579 }
572 entry.value_ptr.bytes = old_mem.ptr[0..result_len];580 entry.value_ptr.bytes = old_mem.ptr[0..new_size];
573 entry.value_ptr.captureStackTrace(ret_addr, .alloc);581 entry.value_ptr.captureStackTrace(ret_addr, .alloc);
574 return result_len;582 return true;
575 }583 }
576584
577 /// This function assumes the object is in the large object storage regardless585 /// This function assumes the object is in the large object storage regardless
...@@ -579,7 +587,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -579,7 +587,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
579 fn freeLarge(587 fn freeLarge(
580 self: *Self,588 self: *Self,
581 old_mem: []u8,589 old_mem: []u8,
582 old_align: u29,590 log2_old_align: u8,
583 ret_addr: usize,591 ret_addr: usize,
584 ) void {592 ) void {
585 const entry = self.large_allocations.getEntry(@ptrToInt(old_mem.ptr)) orelse {593 const entry = self.large_allocations.getEntry(@ptrToInt(old_mem.ptr)) orelse {
...@@ -615,7 +623,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -615,7 +623,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
615 }623 }
616624
617 if (!config.never_unmap) {625 if (!config.never_unmap) {
618 self.backing_allocator.rawFree(old_mem, old_align, ret_addr);626 self.backing_allocator.rawFree(old_mem, log2_old_align, ret_addr);
619 }627 }
620628
621 if (config.enable_memory_limit) {629 if (config.enable_memory_limit) {
...@@ -639,21 +647,22 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -639,21 +647,22 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
639 }647 }
640648
641 fn resize(649 fn resize(
642 self: *Self,650 ctx: *anyopaque,
643 old_mem: []u8,651 old_mem: []u8,
644 old_align: u29,652 log2_old_align_u8: u8,
645 new_size: usize,653 new_size: usize,
646 len_align: u29,
647 ret_addr: usize,654 ret_addr: usize,
648 ) ?usize {655 ) bool {
656 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
657 const log2_old_align = @intCast(Allocator.Log2Align, log2_old_align_u8);
649 self.mutex.lock();658 self.mutex.lock();
650 defer self.mutex.unlock();659 defer self.mutex.unlock();
651660
652 assert(old_mem.len != 0);661 assert(old_mem.len != 0);
653662
654 const aligned_size = math.max(old_mem.len, old_align);663 const aligned_size = @max(old_mem.len, @as(usize, 1) << log2_old_align);
655 if (aligned_size > largest_bucket_object_size) {664 if (aligned_size > largest_bucket_object_size) {
656 return self.resizeLarge(old_mem, old_align, new_size, len_align, ret_addr);665 return self.resizeLarge(old_mem, log2_old_align, new_size, ret_addr);
657 }666 }
658 const size_class_hint = math.ceilPowerOfTwoAssert(usize, aligned_size);667 const size_class_hint = math.ceilPowerOfTwoAssert(usize, aligned_size);
659668
...@@ -678,7 +687,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -678,7 +687,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
678 }687 }
679 }688 }
680 }689 }
681 return self.resizeLarge(old_mem, old_align, new_size, len_align, ret_addr);690 return self.resizeLarge(old_mem, log2_old_align, new_size, ret_addr);
682 };691 };
683 const byte_offset = @ptrToInt(old_mem.ptr) - @ptrToInt(bucket.page);692 const byte_offset = @ptrToInt(old_mem.ptr) - @ptrToInt(bucket.page);
684 const slot_index = @intCast(SlotIndex, byte_offset / size_class);693 const slot_index = @intCast(SlotIndex, byte_offset / size_class);
...@@ -700,12 +709,12 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -700,12 +709,12 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
700 if (config.enable_memory_limit) {709 if (config.enable_memory_limit) {
701 const new_req_bytes = prev_req_bytes + new_size - old_mem.len;710 const new_req_bytes = prev_req_bytes + new_size - old_mem.len;
702 if (new_req_bytes > prev_req_bytes and new_req_bytes > self.requested_memory_limit) {711 if (new_req_bytes > prev_req_bytes and new_req_bytes > self.requested_memory_limit) {
703 return null;712 return false;
704 }713 }
705 self.total_requested_bytes = new_req_bytes;714 self.total_requested_bytes = new_req_bytes;
706 }715 }
707716
708 const new_aligned_size = math.max(new_size, old_align);717 const new_aligned_size = @max(new_size, @as(usize, 1) << log2_old_align);
709 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);718 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
710 if (new_size_class <= size_class) {719 if (new_size_class <= size_class) {
711 if (old_mem.len > new_size) {720 if (old_mem.len > new_size) {
...@@ -716,29 +725,31 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -716,29 +725,31 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
716 old_mem.len, old_mem.ptr, new_size,725 old_mem.len, old_mem.ptr, new_size,
717 });726 });
718 }727 }
719 return new_size;728 return true;
720 }729 }
721730
722 if (config.enable_memory_limit) {731 if (config.enable_memory_limit) {
723 self.total_requested_bytes = prev_req_bytes;732 self.total_requested_bytes = prev_req_bytes;
724 }733 }
725 return null;734 return false;
726 }735 }
727736
728 fn free(737 fn free(
729 self: *Self,738 ctx: *anyopaque,
730 old_mem: []u8,739 old_mem: []u8,
731 old_align: u29,740 log2_old_align_u8: u8,
732 ret_addr: usize,741 ret_addr: usize,
733 ) void {742 ) void {
743 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
744 const log2_old_align = @intCast(Allocator.Log2Align, log2_old_align_u8);
734 self.mutex.lock();745 self.mutex.lock();
735 defer self.mutex.unlock();746 defer self.mutex.unlock();
736747
737 assert(old_mem.len != 0);748 assert(old_mem.len != 0);
738749
739 const aligned_size = math.max(old_mem.len, old_align);750 const aligned_size = @max(old_mem.len, @as(usize, 1) << log2_old_align);
740 if (aligned_size > largest_bucket_object_size) {751 if (aligned_size > largest_bucket_object_size) {
741 self.freeLarge(old_mem, old_align, ret_addr);752 self.freeLarge(old_mem, log2_old_align, ret_addr);
742 return;753 return;
743 }754 }
744 const size_class_hint = math.ceilPowerOfTwoAssert(usize, aligned_size);755 const size_class_hint = math.ceilPowerOfTwoAssert(usize, aligned_size);
...@@ -764,7 +775,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -764,7 +775,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
764 }775 }
765 }776 }
766 }777 }
767 self.freeLarge(old_mem, old_align, ret_addr);778 self.freeLarge(old_mem, log2_old_align, ret_addr);
768 return;779 return;
769 };780 };
770 const byte_offset = @ptrToInt(old_mem.ptr) - @ptrToInt(bucket.page);781 const byte_offset = @ptrToInt(old_mem.ptr) - @ptrToInt(bucket.page);
...@@ -846,18 +857,26 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -846,18 +857,26 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
846 return true;857 return true;
847 }858 }
848859
849 fn alloc(self: *Self, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Error![]u8 {860 fn alloc(ctx: *anyopaque, len: usize, log2_ptr_align: u8, ret_addr: usize) ?[*]u8 {
861 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
850 self.mutex.lock();862 self.mutex.lock();
851 defer self.mutex.unlock();863 defer self.mutex.unlock();
864 if (!self.isAllocationAllowed(len)) return null;
865 return allocInner(self, len, @intCast(Allocator.Log2Align, log2_ptr_align), ret_addr) catch return null;
866 }
852867
853 if (!self.isAllocationAllowed(len)) {868 fn allocInner(
854 return error.OutOfMemory;869 self: *Self,
855 }870 len: usize,
856871 log2_ptr_align: Allocator.Log2Align,
857 const new_aligned_size = math.max(len, ptr_align);872 ret_addr: usize,
873 ) Allocator.Error![*]u8 {
874 const new_aligned_size = @max(len, @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align));
858 if (new_aligned_size > largest_bucket_object_size) {875 if (new_aligned_size > largest_bucket_object_size) {
859 try self.large_allocations.ensureUnusedCapacity(self.backing_allocator, 1);876 try self.large_allocations.ensureUnusedCapacity(self.backing_allocator, 1);
860 const slice = try self.backing_allocator.rawAlloc(len, ptr_align, len_align, ret_addr);877 const ptr = self.backing_allocator.rawAlloc(len, log2_ptr_align, ret_addr) orelse
878 return error.OutOfMemory;
879 const slice = ptr[0..len];
861880
862 const gop = self.large_allocations.getOrPutAssumeCapacity(@ptrToInt(slice.ptr));881 const gop = self.large_allocations.getOrPutAssumeCapacity(@ptrToInt(slice.ptr));
863 if (config.retain_metadata and !config.never_unmap) {882 if (config.retain_metadata and !config.never_unmap) {
...@@ -873,14 +892,14 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -873,14 +892,14 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
873 if (config.retain_metadata) {892 if (config.retain_metadata) {
874 gop.value_ptr.freed = false;893 gop.value_ptr.freed = false;
875 if (config.never_unmap) {894 if (config.never_unmap) {
876 gop.value_ptr.ptr_align = ptr_align;895 gop.value_ptr.log2_ptr_align = log2_ptr_align;
877 }896 }
878 }897 }
879898
880 if (config.verbose_log) {899 if (config.verbose_log) {
881 log.info("large alloc {d} bytes at {*}", .{ slice.len, slice.ptr });900 log.info("large alloc {d} bytes at {*}", .{ slice.len, slice.ptr });
882 }901 }
883 return slice;902 return slice.ptr;
884 }903 }
885904
886 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);905 const new_size_class = math.ceilPowerOfTwoAssert(usize, new_aligned_size);
...@@ -888,15 +907,15 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -888,15 +907,15 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
888 if (config.verbose_log) {907 if (config.verbose_log) {
889 log.info("small alloc {d} bytes at {*}", .{ len, ptr });908 log.info("small alloc {d} bytes at {*}", .{ len, ptr });
890 }909 }
891 return ptr[0..len];910 return ptr;
892 }911 }
893912
894 fn createBucket(self: *Self, size_class: usize, bucket_index: usize) Error!*BucketHeader {913 fn createBucket(self: *Self, size_class: usize, bucket_index: usize) Error!*BucketHeader {
895 const page = try self.backing_allocator.allocAdvanced(u8, page_size, page_size, .exact);914 const page = try self.backing_allocator.alignedAlloc(u8, page_size, page_size);
896 errdefer self.backing_allocator.free(page);915 errdefer self.backing_allocator.free(page);
897916
898 const bucket_size = bucketSize(size_class);917 const bucket_size = bucketSize(size_class);
899 const bucket_bytes = try self.backing_allocator.allocAdvanced(u8, @alignOf(BucketHeader), bucket_size, .exact);918 const bucket_bytes = try self.backing_allocator.alignedAlloc(u8, @alignOf(BucketHeader), bucket_size);
900 const ptr = @ptrCast(*BucketHeader, bucket_bytes.ptr);919 const ptr = @ptrCast(*BucketHeader, bucket_bytes.ptr);
901 ptr.* = BucketHeader{920 ptr.* = BucketHeader{
902 .prev = ptr,921 .prev = ptr,
...@@ -1011,13 +1030,15 @@ test "shrink" {...@@ -1011,13 +1030,15 @@ test "shrink" {
10111030
1012 mem.set(u8, slice, 0x11);1031 mem.set(u8, slice, 0x11);
10131032
1014 slice = allocator.shrink(slice, 17);1033 try std.testing.expect(allocator.resize(slice, 17));
1034 slice = slice[0..17];
10151035
1016 for (slice) |b| {1036 for (slice) |b| {
1017 try std.testing.expect(b == 0x11);1037 try std.testing.expect(b == 0x11);
1018 }1038 }
10191039
1020 slice = allocator.shrink(slice, 16);1040 try std.testing.expect(allocator.resize(slice, 16));
1041 slice = slice[0..16];
10211042
1022 for (slice) |b| {1043 for (slice) |b| {
1023 try std.testing.expect(b == 0x11);1044 try std.testing.expect(b == 0x11);
...@@ -1069,11 +1090,13 @@ test "shrink large object to large object" {...@@ -1069,11 +1090,13 @@ test "shrink large object to large object" {
1069 slice[0] = 0x12;1090 slice[0] = 0x12;
1070 slice[60] = 0x34;1091 slice[60] = 0x34;
10711092
1072 slice = allocator.resize(slice, page_size * 2 + 1) orelse return;1093 if (!allocator.resize(slice, page_size * 2 + 1)) return;
1094 slice = slice.ptr[0 .. page_size * 2 + 1];
1073 try std.testing.expect(slice[0] == 0x12);1095 try std.testing.expect(slice[0] == 0x12);
1074 try std.testing.expect(slice[60] == 0x34);1096 try std.testing.expect(slice[60] == 0x34);
10751097
1076 slice = allocator.shrink(slice, page_size * 2 + 1);1098 try std.testing.expect(allocator.resize(slice, page_size * 2 + 1));
1099 slice = slice[0 .. page_size * 2 + 1];
1077 try std.testing.expect(slice[0] == 0x12);1100 try std.testing.expect(slice[0] == 0x12);
1078 try std.testing.expect(slice[60] == 0x34);1101 try std.testing.expect(slice[60] == 0x34);
10791102
...@@ -1113,7 +1136,7 @@ test "shrink large object to large object with larger alignment" {...@@ -1113,7 +1136,7 @@ test "shrink large object to large object with larger alignment" {
1113 slice[0] = 0x12;1136 slice[0] = 0x12;
1114 slice[60] = 0x34;1137 slice[60] = 0x34;
11151138
1116 slice = try allocator.reallocAdvanced(slice, big_alignment, alloc_size / 2, .exact);1139 slice = try allocator.reallocAdvanced(slice, big_alignment, alloc_size / 2);
1117 try std.testing.expect(slice[0] == 0x12);1140 try std.testing.expect(slice[0] == 0x12);
1118 try std.testing.expect(slice[60] == 0x34);1141 try std.testing.expect(slice[60] == 0x34);
1119}1142}
...@@ -1182,15 +1205,15 @@ test "realloc large object to larger alignment" {...@@ -1182,15 +1205,15 @@ test "realloc large object to larger alignment" {
1182 slice[0] = 0x12;1205 slice[0] = 0x12;
1183 slice[16] = 0x34;1206 slice[16] = 0x34;
11841207
1185 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100, .exact);1208 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100);
1186 try std.testing.expect(slice[0] == 0x12);1209 try std.testing.expect(slice[0] == 0x12);
1187 try std.testing.expect(slice[16] == 0x34);1210 try std.testing.expect(slice[16] == 0x34);
11881211
1189 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25, .exact);1212 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25);
1190 try std.testing.expect(slice[0] == 0x12);1213 try std.testing.expect(slice[0] == 0x12);
1191 try std.testing.expect(slice[16] == 0x34);1214 try std.testing.expect(slice[16] == 0x34);
11921215
1193 slice = try allocator.reallocAdvanced(slice, big_alignment, page_size * 2 + 100, .exact);1216 slice = try allocator.reallocAdvanced(slice, big_alignment, page_size * 2 + 100);
1194 try std.testing.expect(slice[0] == 0x12);1217 try std.testing.expect(slice[0] == 0x12);
1195 try std.testing.expect(slice[16] == 0x34);1218 try std.testing.expect(slice[16] == 0x34);
1196}1219}
...@@ -1208,7 +1231,8 @@ test "large object shrinks to small but allocation fails during shrink" {...@@ -1208,7 +1231,8 @@ test "large object shrinks to small but allocation fails during shrink" {
12081231
1209 // Next allocation will fail in the backing allocator of the GeneralPurposeAllocator1232 // Next allocation will fail in the backing allocator of the GeneralPurposeAllocator
12101233
1211 slice = allocator.shrink(slice, 4);1234 try std.testing.expect(allocator.resize(slice, 4));
1235 slice = slice[0..4];
1212 try std.testing.expect(slice[0] == 0x12);1236 try std.testing.expect(slice[0] == 0x12);
1213 try std.testing.expect(slice[3] == 0x34);1237 try std.testing.expect(slice[3] == 0x34);
1214}1238}
...@@ -1296,10 +1320,10 @@ test "bug 9995 fix, large allocs count requested size not backing size" {...@@ -1296,10 +1320,10 @@ test "bug 9995 fix, large allocs count requested size not backing size" {
1296 var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){};1320 var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){};
1297 const allocator = gpa.allocator();1321 const allocator = gpa.allocator();
12981322
1299 var buf = try allocator.allocAdvanced(u8, 1, page_size + 1, .at_least);1323 var buf = try allocator.alignedAlloc(u8, 1, page_size + 1);
1300 try std.testing.expect(gpa.total_requested_bytes == page_size + 1);1324 try std.testing.expect(gpa.total_requested_bytes == page_size + 1);
1301 buf = try allocator.reallocAtLeast(buf, 1);1325 buf = try allocator.realloc(buf, 1);
1302 try std.testing.expect(gpa.total_requested_bytes == 1);1326 try std.testing.expect(gpa.total_requested_bytes == 1);
1303 buf = try allocator.reallocAtLeast(buf, 2);1327 buf = try allocator.realloc(buf, 2);
1304 try std.testing.expect(gpa.total_requested_bytes == 2);1328 try std.testing.expect(gpa.total_requested_bytes == 2);
1305}1329}
lib/std/heap/log_to_writer_allocator.zig+29-21
...@@ -18,60 +18,68 @@ pub fn LogToWriterAllocator(comptime Writer: type) type {...@@ -18,60 +18,68 @@ pub fn LogToWriterAllocator(comptime Writer: type) type {
18 }18 }
1919
20 pub fn allocator(self: *Self) Allocator {20 pub fn allocator(self: *Self) Allocator {
21 return Allocator.init(self, alloc, resize, free);21 return .{
22 .ptr = self,
23 .vtable = &.{
24 .alloc = alloc,
25 .resize = resize,
26 .free = free,
27 },
28 };
22 }29 }
2330
24 fn alloc(31 fn alloc(
25 self: *Self,32 ctx: *anyopaque,
26 len: usize,33 len: usize,
27 ptr_align: u29,34 log2_ptr_align: u8,
28 len_align: u29,
29 ra: usize,35 ra: usize,
30 ) error{OutOfMemory}![]u8 {36 ) ?[*]u8 {
37 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
31 self.writer.print("alloc : {}", .{len}) catch {};38 self.writer.print("alloc : {}", .{len}) catch {};
32 const result = self.parent_allocator.rawAlloc(len, ptr_align, len_align, ra);39 const result = self.parent_allocator.rawAlloc(len, log2_ptr_align, ra);
33 if (result) |_| {40 if (result != null) {
34 self.writer.print(" success!\n", .{}) catch {};41 self.writer.print(" success!\n", .{}) catch {};
35 } else |_| {42 } else {
36 self.writer.print(" failure!\n", .{}) catch {};43 self.writer.print(" failure!\n", .{}) catch {};
37 }44 }
38 return result;45 return result;
39 }46 }
4047
41 fn resize(48 fn resize(
42 self: *Self,49 ctx: *anyopaque,
43 buf: []u8,50 buf: []u8,
44 buf_align: u29,51 log2_buf_align: u8,
45 new_len: usize,52 new_len: usize,
46 len_align: u29,
47 ra: usize,53 ra: usize,
48 ) ?usize {54 ) bool {
55 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
49 if (new_len <= buf.len) {56 if (new_len <= buf.len) {
50 self.writer.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};57 self.writer.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
51 } else {58 } else {
52 self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {};59 self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
53 }60 }
5461
55 if (self.parent_allocator.rawResize(buf, buf_align, new_len, len_align, ra)) |resized_len| {62 if (self.parent_allocator.rawResize(buf, log2_buf_align, new_len, ra)) {
56 if (new_len > buf.len) {63 if (new_len > buf.len) {
57 self.writer.print(" success!\n", .{}) catch {};64 self.writer.print(" success!\n", .{}) catch {};
58 }65 }
59 return resized_len;66 return true;
60 }67 }
6168
62 std.debug.assert(new_len > buf.len);69 std.debug.assert(new_len > buf.len);
63 self.writer.print(" failure!\n", .{}) catch {};70 self.writer.print(" failure!\n", .{}) catch {};
64 return null;71 return false;
65 }72 }
6673
67 fn free(74 fn free(
68 self: *Self,75 ctx: *anyopaque,
69 buf: []u8,76 buf: []u8,
70 buf_align: u29,77 log2_buf_align: u8,
71 ra: usize,78 ra: usize,
72 ) void {79 ) void {
80 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
73 self.writer.print("free : {}\n", .{buf.len}) catch {};81 self.writer.print("free : {}\n", .{buf.len}) catch {};
74 self.parent_allocator.rawFree(buf, buf_align, ra);82 self.parent_allocator.rawFree(buf, log2_buf_align, ra);
75 }83 }
76 };84 };
77}85}
...@@ -95,9 +103,9 @@ test "LogToWriterAllocator" {...@@ -95,9 +103,9 @@ test "LogToWriterAllocator" {
95 const allocator = allocator_state.allocator();103 const allocator = allocator_state.allocator();
96104
97 var a = try allocator.alloc(u8, 10);105 var a = try allocator.alloc(u8, 10);
98 a = allocator.shrink(a, 5);106 try std.testing.expect(allocator.resize(a, 5));
99 try std.testing.expect(a.len == 5);107 a = a[0..5];
100 try std.testing.expect(allocator.resize(a, 20) == null);108 try std.testing.expect(!allocator.resize(a, 20));
101 allocator.free(a);109 allocator.free(a);
102110
103 try std.testing.expectEqualSlices(u8,111 try std.testing.expectEqualSlices(u8,
lib/std/heap/logging_allocator.zig+36-28
...@@ -33,7 +33,14 @@ pub fn ScopedLoggingAllocator(...@@ -33,7 +33,14 @@ pub fn ScopedLoggingAllocator(
33 }33 }
3434
35 pub fn allocator(self: *Self) Allocator {35 pub fn allocator(self: *Self) Allocator {
36 return Allocator.init(self, alloc, resize, free);36 return .{
37 .ptr = self,
38 .vtable = &.{
39 .alloc = alloc,
40 .resize = resize,
41 .free = free,
42 },
43 };
37 }44 }
3845
39 // This function is required as the `std.log.log` function is not public46 // This function is required as the `std.log.log` function is not public
...@@ -47,71 +54,72 @@ pub fn ScopedLoggingAllocator(...@@ -47,71 +54,72 @@ pub fn ScopedLoggingAllocator(
47 }54 }
4855
49 fn alloc(56 fn alloc(
50 self: *Self,57 ctx: *anyopaque,
51 len: usize,58 len: usize,
52 ptr_align: u29,59 log2_ptr_align: u8,
53 len_align: u29,
54 ra: usize,60 ra: usize,
55 ) error{OutOfMemory}![]u8 {61 ) ?[*]u8 {
56 const result = self.parent_allocator.rawAlloc(len, ptr_align, len_align, ra);62 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
57 if (result) |_| {63 const result = self.parent_allocator.rawAlloc(len, log2_ptr_align, ra);
64 if (result != null) {
58 logHelper(65 logHelper(
59 success_log_level,66 success_log_level,
60 "alloc - success - len: {}, ptr_align: {}, len_align: {}",67 "alloc - success - len: {}, ptr_align: {}",
61 .{ len, ptr_align, len_align },68 .{ len, log2_ptr_align },
62 );69 );
63 } else |err| {70 } else {
64 logHelper(71 logHelper(
65 failure_log_level,72 failure_log_level,
66 "alloc - failure: {s} - len: {}, ptr_align: {}, len_align: {}",73 "alloc - failure: OutOfMemory - len: {}, ptr_align: {}",
67 .{ @errorName(err), len, ptr_align, len_align },74 .{ len, log2_ptr_align },
68 );75 );
69 }76 }
70 return result;77 return result;
71 }78 }
7279
73 fn resize(80 fn resize(
74 self: *Self,81 ctx: *anyopaque,
75 buf: []u8,82 buf: []u8,
76 buf_align: u29,83 log2_buf_align: u8,
77 new_len: usize,84 new_len: usize,
78 len_align: u29,
79 ra: usize,85 ra: usize,
80 ) ?usize {86 ) bool {
81 if (self.parent_allocator.rawResize(buf, buf_align, new_len, len_align, ra)) |resized_len| {87 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
88 if (self.parent_allocator.rawResize(buf, log2_buf_align, new_len, ra)) {
82 if (new_len <= buf.len) {89 if (new_len <= buf.len) {
83 logHelper(90 logHelper(
84 success_log_level,91 success_log_level,
85 "shrink - success - {} to {}, len_align: {}, buf_align: {}",92 "shrink - success - {} to {}, buf_align: {}",
86 .{ buf.len, new_len, len_align, buf_align },93 .{ buf.len, new_len, log2_buf_align },
87 );94 );
88 } else {95 } else {
89 logHelper(96 logHelper(
90 success_log_level,97 success_log_level,
91 "expand - success - {} to {}, len_align: {}, buf_align: {}",98 "expand - success - {} to {}, buf_align: {}",
92 .{ buf.len, new_len, len_align, buf_align },99 .{ buf.len, new_len, log2_buf_align },
93 );100 );
94 }101 }
95102
96 return resized_len;103 return true;
97 }104 }
98105
99 std.debug.assert(new_len > buf.len);106 std.debug.assert(new_len > buf.len);
100 logHelper(107 logHelper(
101 failure_log_level,108 failure_log_level,
102 "expand - failure - {} to {}, len_align: {}, buf_align: {}",109 "expand - failure - {} to {}, buf_align: {}",
103 .{ buf.len, new_len, len_align, buf_align },110 .{ buf.len, new_len, log2_buf_align },
104 );111 );
105 return null;112 return false;
106 }113 }
107114
108 fn free(115 fn free(
109 self: *Self,116 ctx: *anyopaque,
110 buf: []u8,117 buf: []u8,
111 buf_align: u29,118 log2_buf_align: u8,
112 ra: usize,119 ra: usize,
113 ) void {120 ) void {
114 self.parent_allocator.rawFree(buf, buf_align, ra);121 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
122 self.parent_allocator.rawFree(buf, log2_buf_align, ra);
115 logHelper(success_log_level, "free - len: {}", .{buf.len});123 logHelper(success_log_level, "free - len: {}", .{buf.len});
116 }124 }
117 };125 };
lib/std/io/reader.zig+2-2
...@@ -176,11 +176,11 @@ pub fn Reader(...@@ -176,11 +176,11 @@ pub fn Reader(
176 error.EndOfStream => if (array_list.items.len == 0) {176 error.EndOfStream => if (array_list.items.len == 0) {
177 return null;177 return null;
178 } else {178 } else {
179 return array_list.toOwnedSlice();179 return try array_list.toOwnedSlice();
180 },180 },
181 else => |e| return e,181 else => |e| return e,
182 };182 };
183 return array_list.toOwnedSlice();183 return try array_list.toOwnedSlice();
184 }184 }
185185
186 /// Reads from the stream until specified byte is found. If the buffer is not186 /// Reads from the stream until specified byte is found. If the buffer is not
lib/std/json.zig+2-4
...@@ -1668,12 +1668,10 @@ fn parseInternal(...@@ -1668,12 +1668,10 @@ fn parseInternal(
16681668
1669 if (ptrInfo.sentinel) |some| {1669 if (ptrInfo.sentinel) |some| {
1670 const sentinel_value = @ptrCast(*align(1) const ptrInfo.child, some).*;1670 const sentinel_value = @ptrCast(*align(1) const ptrInfo.child, some).*;
1671 try arraylist.append(sentinel_value);1671 return try arraylist.toOwnedSliceSentinel(sentinel_value);
1672 const output = arraylist.toOwnedSlice();
1673 return output[0 .. output.len - 1 :sentinel_value];
1674 }1672 }
16751673
1676 return arraylist.toOwnedSlice();1674 return try arraylist.toOwnedSlice();
1677 },1675 },
1678 .String => |stringToken| {1676 .String => |stringToken| {
1679 if (ptrInfo.child != u8) return error.UnexpectedToken;1677 if (ptrInfo.child != u8) return error.UnexpectedToken;
lib/std/math/big/int.zig+29-1
...@@ -2148,7 +2148,7 @@ pub const Const = struct {...@@ -2148,7 +2148,7 @@ pub const Const = struct {
2148 const limbs = try allocator.alloc(Limb, calcToStringLimbsBufferLen(self.limbs.len, base));2148 const limbs = try allocator.alloc(Limb, calcToStringLimbsBufferLen(self.limbs.len, base));
2149 defer allocator.free(limbs);2149 defer allocator.free(limbs);
21502150
2151 return allocator.shrink(string, self.toString(string, base, case, limbs));2151 return allocator.realloc(string, self.toString(string, base, case, limbs));
2152 }2152 }
21532153
2154 /// Converts self to a string in the requested base.2154 /// Converts self to a string in the requested base.
...@@ -2376,6 +2376,34 @@ pub const Const = struct {...@@ -2376,6 +2376,34 @@ pub const Const = struct {
2376 pub fn eq(a: Const, b: Const) bool {2376 pub fn eq(a: Const, b: Const) bool {
2377 return order(a, b) == .eq;2377 return order(a, b) == .eq;
2378 }2378 }
2379
2380 pub fn clz(a: Const, bits: Limb) Limb {
2381 // Limbs are stored in little-endian order but we need
2382 // to iterate big-endian.
2383 var total_limb_lz: Limb = 0;
2384 var i: usize = a.limbs.len;
2385 const bits_per_limb = @sizeOf(Limb) * 8;
2386 while (i != 0) {
2387 i -= 1;
2388 const limb = a.limbs[i];
2389 const this_limb_lz = @clz(limb);
2390 total_limb_lz += this_limb_lz;
2391 if (this_limb_lz != bits_per_limb) break;
2392 }
2393 const total_limb_bits = a.limbs.len * bits_per_limb;
2394 return total_limb_lz + bits - total_limb_bits;
2395 }
2396
2397 pub fn ctz(a: Const) Limb {
2398 // Limbs are stored in little-endian order.
2399 var result: Limb = 0;
2400 for (a.limbs) |limb| {
2401 const limb_tz = @ctz(limb);
2402 result += limb_tz;
2403 if (limb_tz != @sizeOf(Limb) * 8) break;
2404 }
2405 return result;
2406 }
2379};2407};
23802408
2381/// An arbitrary-precision big integer along with an allocator which manages the memory.2409/// An arbitrary-precision big integer along with an allocator which manages the memory.
lib/std/mem.zig+50-57
...@@ -47,7 +47,14 @@ pub fn ValidationAllocator(comptime T: type) type {...@@ -47,7 +47,14 @@ pub fn ValidationAllocator(comptime T: type) type {
47 }47 }
4848
49 pub fn allocator(self: *Self) Allocator {49 pub fn allocator(self: *Self) Allocator {
50 return Allocator.init(self, alloc, resize, free);50 return .{
51 .ptr = self,
52 .vtable = &.{
53 .alloc = alloc,
54 .resize = resize,
55 .free = free,
56 },
57 };
51 }58 }
5259
53 fn getUnderlyingAllocatorPtr(self: *Self) Allocator {60 fn getUnderlyingAllocatorPtr(self: *Self) Allocator {
...@@ -56,72 +63,48 @@ pub fn ValidationAllocator(comptime T: type) type {...@@ -56,72 +63,48 @@ pub fn ValidationAllocator(comptime T: type) type {
56 }63 }
5764
58 pub fn alloc(65 pub fn alloc(
59 self: *Self,66 ctx: *anyopaque,
60 n: usize,67 n: usize,
61 ptr_align: u29,68 log2_ptr_align: u8,
62 len_align: u29,
63 ret_addr: usize,69 ret_addr: usize,
64 ) Allocator.Error![]u8 {70 ) ?[*]u8 {
65 assert(n > 0);71 assert(n > 0);
66 assert(mem.isValidAlign(ptr_align));72 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
67 if (len_align != 0) {
68 assert(mem.isAlignedAnyAlign(n, len_align));
69 assert(n >= len_align);
70 }
71
72 const underlying = self.getUnderlyingAllocatorPtr();73 const underlying = self.getUnderlyingAllocatorPtr();
73 const result = try underlying.rawAlloc(n, ptr_align, len_align, ret_addr);74 const result = underlying.rawAlloc(n, log2_ptr_align, ret_addr) orelse
74 assert(mem.isAligned(@ptrToInt(result.ptr), ptr_align));75 return null;
75 if (len_align == 0) {76 assert(mem.isAlignedLog2(@ptrToInt(result), log2_ptr_align));
76 assert(result.len == n);
77 } else {
78 assert(result.len >= n);
79 assert(mem.isAlignedAnyAlign(result.len, len_align));
80 }
81 return result;77 return result;
82 }78 }
8379
84 pub fn resize(80 pub fn resize(
85 self: *Self,81 ctx: *anyopaque,
86 buf: []u8,82 buf: []u8,
87 buf_align: u29,83 log2_buf_align: u8,
88 new_len: usize,84 new_len: usize,
89 len_align: u29,
90 ret_addr: usize,85 ret_addr: usize,
91 ) ?usize {86 ) bool {
87 const self = @ptrCast(*Self, @alignCast(@alignOf(Self), ctx));
92 assert(buf.len > 0);88 assert(buf.len > 0);
93 if (len_align != 0) {
94 assert(mem.isAlignedAnyAlign(new_len, len_align));
95 assert(new_len >= len_align);
96 }
97 const underlying = self.getUnderlyingAllocatorPtr();89 const underlying = self.getUnderlyingAllocatorPtr();
98 const result = underlying.rawResize(buf, buf_align, new_len, len_align, ret_addr) orelse return null;90 return underlying.rawResize(buf, log2_buf_align, new_len, ret_addr);
99 if (len_align == 0) {
100 assert(result == new_len);
101 } else {
102 assert(result >= new_len);
103 assert(mem.isAlignedAnyAlign(result, len_align));
104 }
105 return result;
106 }91 }
10792
108 pub fn free(93 pub fn free(
109 self: *Self,94 ctx: *anyopaque,
110 buf: []u8,95 buf: []u8,
111 buf_align: u29,96 log2_buf_align: u8,
112 ret_addr: usize,97 ret_addr: usize,
113 ) void {98 ) void {
114 _ = self;99 _ = ctx;
115 _ = buf_align;100 _ = log2_buf_align;
116 _ = ret_addr;101 _ = ret_addr;
117 assert(buf.len > 0);102 assert(buf.len > 0);
118 }103 }
119104
120 pub usingnamespace if (T == Allocator or !@hasDecl(T, "reset")) struct {} else struct {105 pub fn reset(self: *Self) void {
121 pub fn reset(self: *Self) void {106 self.underlying_allocator.reset();
122 self.underlying_allocator.reset();107 }
123 }
124 };
125 };108 };
126}109}
127110
...@@ -151,16 +134,15 @@ const fail_allocator = Allocator{...@@ -151,16 +134,15 @@ const fail_allocator = Allocator{
151134
152const failAllocator_vtable = Allocator.VTable{135const failAllocator_vtable = Allocator.VTable{
153 .alloc = failAllocatorAlloc,136 .alloc = failAllocatorAlloc,
154 .resize = Allocator.NoResize(anyopaque).noResize,137 .resize = Allocator.noResize,
155 .free = Allocator.NoOpFree(anyopaque).noOpFree,138 .free = Allocator.noFree,
156};139};
157140
158fn failAllocatorAlloc(_: *anyopaque, n: usize, alignment: u29, len_align: u29, ra: usize) Allocator.Error![]u8 {141fn failAllocatorAlloc(_: *anyopaque, n: usize, log2_alignment: u8, ra: usize) ?[*]u8 {
159 _ = n;142 _ = n;
160 _ = alignment;143 _ = log2_alignment;
161 _ = len_align;
162 _ = ra;144 _ = ra;
163 return error.OutOfMemory;145 return null;
164}146}
165147
166test "Allocator basics" {148test "Allocator basics" {
...@@ -188,7 +170,8 @@ test "Allocator.resize" {...@@ -188,7 +170,8 @@ test "Allocator.resize" {
188 defer testing.allocator.free(values);170 defer testing.allocator.free(values);
189171
190 for (values) |*v, i| v.* = @intCast(T, i);172 for (values) |*v, i| v.* = @intCast(T, i);
191 values = testing.allocator.resize(values, values.len + 10) orelse return error.OutOfMemory;173 if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory;
174 values = values.ptr[0 .. values.len + 10];
192 try testing.expect(values.len == 110);175 try testing.expect(values.len == 110);
193 }176 }
194177
...@@ -203,7 +186,8 @@ test "Allocator.resize" {...@@ -203,7 +186,8 @@ test "Allocator.resize" {
203 defer testing.allocator.free(values);186 defer testing.allocator.free(values);
204187
205 for (values) |*v, i| v.* = @intToFloat(T, i);188 for (values) |*v, i| v.* = @intToFloat(T, i);
206 values = testing.allocator.resize(values, values.len + 10) orelse return error.OutOfMemory;189 if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory;
190 values = values.ptr[0 .. values.len + 10];
207 try testing.expect(values.len == 110);191 try testing.expect(values.len == 110);
208 }192 }
209}193}
...@@ -3108,7 +3092,7 @@ pub fn nativeToBig(comptime T: type, x: T) T {...@@ -3108,7 +3092,7 @@ pub fn nativeToBig(comptime T: type, x: T) T {
3108/// - The aligned pointer would not fit the address space,3092/// - The aligned pointer would not fit the address space,
3109/// - The delta required to align the pointer is not a multiple of the pointee's3093/// - The delta required to align the pointer is not a multiple of the pointee's
3110/// type.3094/// type.
3111pub fn alignPointerOffset(ptr: anytype, align_to: u29) ?usize {3095pub fn alignPointerOffset(ptr: anytype, align_to: usize) ?usize {
3112 assert(align_to != 0 and @popCount(align_to) == 1);3096 assert(align_to != 0 and @popCount(align_to) == 1);
31133097
3114 const T = @TypeOf(ptr);3098 const T = @TypeOf(ptr);
...@@ -3140,7 +3124,7 @@ pub fn alignPointerOffset(ptr: anytype, align_to: u29) ?usize {...@@ -3140,7 +3124,7 @@ pub fn alignPointerOffset(ptr: anytype, align_to: u29) ?usize {
3140/// - The aligned pointer would not fit the address space,3124/// - The aligned pointer would not fit the address space,
3141/// - The delta required to align the pointer is not a multiple of the pointee's3125/// - The delta required to align the pointer is not a multiple of the pointee's
3142/// type.3126/// type.
3143pub fn alignPointer(ptr: anytype, align_to: u29) ?@TypeOf(ptr) {3127pub fn alignPointer(ptr: anytype, align_to: usize) ?@TypeOf(ptr) {
3144 const adjust_off = alignPointerOffset(ptr, align_to) orelse return null;3128 const adjust_off = alignPointerOffset(ptr, align_to) orelse return null;
3145 const T = @TypeOf(ptr);3129 const T = @TypeOf(ptr);
3146 // Avoid the use of intToPtr to avoid losing the pointer provenance info.3130 // Avoid the use of intToPtr to avoid losing the pointer provenance info.
...@@ -3149,7 +3133,7 @@ pub fn alignPointer(ptr: anytype, align_to: u29) ?@TypeOf(ptr) {...@@ -3149,7 +3133,7 @@ pub fn alignPointer(ptr: anytype, align_to: u29) ?@TypeOf(ptr) {
31493133
3150test "alignPointer" {3134test "alignPointer" {
3151 const S = struct {3135 const S = struct {
3152 fn checkAlign(comptime T: type, base: usize, align_to: u29, expected: usize) !void {3136 fn checkAlign(comptime T: type, base: usize, align_to: usize, expected: usize) !void {
3153 var ptr = @intToPtr(T, base);3137 var ptr = @intToPtr(T, base);
3154 var aligned = alignPointer(ptr, align_to);3138 var aligned = alignPointer(ptr, align_to);
3155 try testing.expectEqual(expected, @ptrToInt(aligned));3139 try testing.expectEqual(expected, @ptrToInt(aligned));
...@@ -3566,6 +3550,11 @@ pub fn alignForward(addr: usize, alignment: usize) usize {...@@ -3566,6 +3550,11 @@ pub fn alignForward(addr: usize, alignment: usize) usize {
3566 return alignForwardGeneric(usize, addr, alignment);3550 return alignForwardGeneric(usize, addr, alignment);
3567}3551}
35683552
3553pub fn alignForwardLog2(addr: usize, log2_alignment: u8) usize {
3554 const alignment = @as(usize, 1) << @intCast(math.Log2Int(usize), log2_alignment);
3555 return alignForward(addr, alignment);
3556}
3557
3569/// Round an address up to the next (or current) aligned address.3558/// Round an address up to the next (or current) aligned address.
3570/// The alignment must be a power of 2 and greater than 0.3559/// The alignment must be a power of 2 and greater than 0.
3571/// Asserts that rounding up the address does not cause integer overflow.3560/// Asserts that rounding up the address does not cause integer overflow.
...@@ -3626,7 +3615,7 @@ pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {...@@ -3626,7 +3615,7 @@ pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {
36263615
3627/// Returns whether `alignment` is a valid alignment, meaning it is3616/// Returns whether `alignment` is a valid alignment, meaning it is
3628/// a positive power of 2.3617/// a positive power of 2.
3629pub fn isValidAlign(alignment: u29) bool {3618pub fn isValidAlign(alignment: usize) bool {
3630 return @popCount(alignment) == 1;3619 return @popCount(alignment) == 1;
3631}3620}
36323621
...@@ -3637,6 +3626,10 @@ pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool {...@@ -3637,6 +3626,10 @@ pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool {
3637 return 0 == @mod(i, alignment);3626 return 0 == @mod(i, alignment);
3638}3627}
36393628
3629pub fn isAlignedLog2(addr: usize, log2_alignment: u8) bool {
3630 return @ctz(addr) >= log2_alignment;
3631}
3632
3640/// Given an address and an alignment, return true if the address is a multiple of the alignment3633/// Given an address and an alignment, return true if the address is a multiple of the alignment
3641/// The alignment must be a power of 2 and greater than 0.3634/// The alignment must be a power of 2 and greater than 0.
3642pub fn isAligned(addr: usize, alignment: usize) bool {3635pub fn isAligned(addr: usize, alignment: usize) bool {
...@@ -3670,7 +3663,7 @@ test "freeing empty string with null-terminated sentinel" {...@@ -3670,7 +3663,7 @@ test "freeing empty string with null-terminated sentinel" {
36703663
3671/// Returns a slice with the given new alignment,3664/// Returns a slice with the given new alignment,
3672/// all other pointer attributes copied from `AttributeSource`.3665/// all other pointer attributes copied from `AttributeSource`.
3673fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) type {3666fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: usize) type {
3674 const info = @typeInfo(AttributeSource).Pointer;3667 const info = @typeInfo(AttributeSource).Pointer;
3675 return @Type(.{3668 return @Type(.{
3676 .Pointer = .{3669 .Pointer = .{
lib/std/mem/Allocator.zig+127-526
...@@ -8,167 +8,101 @@ const Allocator = @This();...@@ -8,167 +8,101 @@ const Allocator = @This();
8const builtin = @import("builtin");8const builtin = @import("builtin");
99
10pub const Error = error{OutOfMemory};10pub const Error = error{OutOfMemory};
11pub const Log2Align = math.Log2Int(usize);
1112
12// The type erased pointer to the allocator implementation13// The type erased pointer to the allocator implementation
13ptr: *anyopaque,14ptr: *anyopaque,
14vtable: *const VTable,15vtable: *const VTable,
1516
16pub const VTable = struct {17pub const VTable = struct {
17 /// Attempt to allocate at least `len` bytes aligned to `ptr_align`.18 /// Attempt to allocate exactly `len` bytes aligned to `1 << ptr_align`.
18 ///19 ///
19 /// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,20 /// `ret_addr` is optionally provided as the first return address of the
20 /// otherwise, the length must be aligned to `len_align`.21 /// allocation call stack. If the value is `0` it means no return address
22 /// has been provided.
23 alloc: std.meta.FnPtr(fn (ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8),
24
25 /// Attempt to expand or shrink memory in place. `buf.len` must equal the
26 /// length requested from the most recent successful call to `alloc` or
27 /// `resize`. `buf_align` must equal the same value that was passed as the
28 /// `ptr_align` parameter to the original `alloc` call.
21 ///29 ///
22 /// `len` must be greater than or equal to `len_align` and must be aligned by `len_align`.30 /// A result of `true` indicates the resize was successful and the
31 /// allocation now has the same address but a size of `new_len`. `false`
32 /// indicates the resize could not be completed without moving the
33 /// allocation to a different address.
23 ///34 ///
24 /// `ret_addr` is optionally provided as the first return address of the allocation call stack.35 /// `new_len` must be greater than zero.
25 /// If the value is `0` it means no return address has been provided.
26 alloc: std.meta.FnPtr(fn (ptr: *anyopaque, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Error![]u8),
27
28 /// Attempt to expand or shrink memory in place. `buf.len` must equal the most recent
29 /// length returned by `alloc` or `resize`. `buf_align` must equal the same value
30 /// that was passed as the `ptr_align` parameter to the original `alloc` call.
31 ///36 ///
32 /// `null` can only be returned if `new_len` is greater than `buf.len`.37 /// `ret_addr` is optionally provided as the first return address of the
33 /// If `buf` cannot be expanded to accomodate `new_len`, then the allocation MUST be38 /// allocation call stack. If the value is `0` it means no return address
34 /// unmodified and `null` MUST be returned.39 /// has been provided.
40 resize: std.meta.FnPtr(fn (ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool),
41
42 /// Free and invalidate a buffer.
35 ///43 ///
36 /// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,44 /// `buf.len` must equal the most recent length returned by `alloc` or
37 /// otherwise, the length must be aligned to `len_align`. Note that `len_align` does *not*45 /// given to a successful `resize` call.
38 /// provide a way to modify the alignment of a pointer. Rather it provides an API for
39 /// accepting more bytes of memory from the allocator than requested.
40 ///46 ///
41 /// `new_len` must be greater than zero, greater than or equal to `len_align` and must be aligned by `len_align`.47 /// `buf_align` must equal the same value that was passed as the
48 /// `ptr_align` parameter to the original `alloc` call.
42 ///49 ///
43 /// `ret_addr` is optionally provided as the first return address of the allocation call stack.50 /// `ret_addr` is optionally provided as the first return address of the
44 /// If the value is `0` it means no return address has been provided.51 /// allocation call stack. If the value is `0` it means no return address
45 resize: std.meta.FnPtr(fn (ptr: *anyopaque, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) ?usize),52 /// has been provided.
4653 free: std.meta.FnPtr(fn (ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void),
47 /// Free and invalidate a buffer. `buf.len` must equal the most recent length returned by `alloc` or `resize`.
48 /// `buf_align` must equal the same value that was passed as the `ptr_align` parameter to the original `alloc` call.
49 ///
50 /// `ret_addr` is optionally provided as the first return address of the allocation call stack.
51 /// If the value is `0` it means no return address has been provided.
52 free: std.meta.FnPtr(fn (ptr: *anyopaque, buf: []u8, buf_align: u29, ret_addr: usize) void),
53};54};
5455
55pub fn init(56pub fn noResize(
56 pointer: anytype,57 self: *anyopaque,
57 comptime allocFn: fn (ptr: @TypeOf(pointer), len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Error![]u8,58 buf: []u8,
58 comptime resizeFn: fn (ptr: @TypeOf(pointer), buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) ?usize,59 log2_buf_align: u8,
59 comptime freeFn: fn (ptr: @TypeOf(pointer), buf: []u8, buf_align: u29, ret_addr: usize) void,60 new_len: usize,
60) Allocator {61 ret_addr: usize,
61 const Ptr = @TypeOf(pointer);62) bool {
62 const ptr_info = @typeInfo(Ptr);63 _ = self;
6364 _ = buf;
64 assert(ptr_info == .Pointer); // Must be a pointer65 _ = log2_buf_align;
65 assert(ptr_info.Pointer.size == .One); // Must be a single-item pointer66 _ = new_len;
6667 _ = ret_addr;
67 const alignment = ptr_info.Pointer.alignment;68 return false;
6869}
69 const gen = struct {70
70 fn allocImpl(ptr: *anyopaque, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Error![]u8 {71pub fn noFree(
71 const self = @ptrCast(Ptr, @alignCast(alignment, ptr));72 self: *anyopaque,
72 return @call(.{ .modifier = .always_inline }, allocFn, .{ self, len, ptr_align, len_align, ret_addr });73 buf: []u8,
73 }74 log2_buf_align: u8,
74 fn resizeImpl(ptr: *anyopaque, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) ?usize {75 ret_addr: usize,
75 assert(new_len != 0);76) void {
76 const self = @ptrCast(Ptr, @alignCast(alignment, ptr));77 _ = self;
77 return @call(.{ .modifier = .always_inline }, resizeFn, .{ self, buf, buf_align, new_len, len_align, ret_addr });78 _ = buf;
78 }79 _ = log2_buf_align;
79 fn freeImpl(ptr: *anyopaque, buf: []u8, buf_align: u29, ret_addr: usize) void {80 _ = ret_addr;
80 const self = @ptrCast(Ptr, @alignCast(alignment, ptr));
81 @call(.{ .modifier = .always_inline }, freeFn, .{ self, buf, buf_align, ret_addr });
82 }
83
84 const vtable = VTable{
85 .alloc = allocImpl,
86 .resize = resizeImpl,
87 .free = freeImpl,
88 };
89 };
90
91 return .{
92 .ptr = pointer,
93 .vtable = &gen.vtable,
94 };
95}
96
97/// Set resizeFn to `NoResize(AllocatorType).noResize` if in-place resize is not supported.
98pub fn NoResize(comptime AllocatorType: type) type {
99 return struct {
100 pub fn noResize(
101 self: *AllocatorType,
102 buf: []u8,
103 buf_align: u29,
104 new_len: usize,
105 len_align: u29,
106 ret_addr: usize,
107 ) ?usize {
108 _ = self;
109 _ = buf_align;
110 _ = len_align;
111 _ = ret_addr;
112 return if (new_len > buf.len) null else new_len;
113 }
114 };
115}
116
117/// Set freeFn to `NoOpFree(AllocatorType).noOpFree` if free is a no-op.
118pub fn NoOpFree(comptime AllocatorType: type) type {
119 return struct {
120 pub fn noOpFree(
121 self: *AllocatorType,
122 buf: []u8,
123 buf_align: u29,
124 ret_addr: usize,
125 ) void {
126 _ = self;
127 _ = buf;
128 _ = buf_align;
129 _ = ret_addr;
130 }
131 };
132}
133
134/// Set freeFn to `PanicFree(AllocatorType).panicFree` if free is not a supported operation.
135pub fn PanicFree(comptime AllocatorType: type) type {
136 return struct {
137 pub fn panicFree(
138 self: *AllocatorType,
139 buf: []u8,
140 buf_align: u29,
141 ret_addr: usize,
142 ) void {
143 _ = self;
144 _ = buf;
145 _ = buf_align;
146 _ = ret_addr;
147 @panic("free is not a supported operation for the allocator: " ++ @typeName(AllocatorType));
148 }
149 };
150}81}
15182
152/// This function is not intended to be called except from within the implementation of an Allocator83/// This function is not intended to be called except from within the
153pub inline fn rawAlloc(self: Allocator, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Error![]u8 {84/// implementation of an Allocator
154 return self.vtable.alloc(self.ptr, len, ptr_align, len_align, ret_addr);85pub inline fn rawAlloc(self: Allocator, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
86 return self.vtable.alloc(self.ptr, len, ptr_align, ret_addr);
155}87}
15688
157/// This function is not intended to be called except from within the implementation of an Allocator89/// This function is not intended to be called except from within the
158pub inline fn rawResize(self: Allocator, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) ?usize {90/// implementation of an Allocator
159 return self.vtable.resize(self.ptr, buf, buf_align, new_len, len_align, ret_addr);91pub inline fn rawResize(self: Allocator, buf: []u8, log2_buf_align: u8, new_len: usize, ret_addr: usize) bool {
92 return self.vtable.resize(self.ptr, buf, log2_buf_align, new_len, ret_addr);
160}93}
16194
162/// This function is not intended to be called except from within the implementation of an Allocator95/// This function is not intended to be called except from within the
163pub inline fn rawFree(self: Allocator, buf: []u8, buf_align: u29, ret_addr: usize) void {96/// implementation of an Allocator
164 return self.vtable.free(self.ptr, buf, buf_align, ret_addr);97pub inline fn rawFree(self: Allocator, buf: []u8, log2_buf_align: u8, ret_addr: usize) void {
98 return self.vtable.free(self.ptr, buf, log2_buf_align, ret_addr);
165}99}
166100
167/// Returns a pointer to undefined memory.101/// Returns a pointer to undefined memory.
168/// Call `destroy` with the result to free the memory.102/// Call `destroy` with the result to free the memory.
169pub fn create(self: Allocator, comptime T: type) Error!*T {103pub fn create(self: Allocator, comptime T: type) Error!*T {
170 if (@sizeOf(T) == 0) return @intToPtr(*T, std.math.maxInt(usize));104 if (@sizeOf(T) == 0) return @intToPtr(*T, math.maxInt(usize));
171 const slice = try self.allocAdvancedWithRetAddr(T, null, 1, .exact, @returnAddress());105 const slice = try self.allocAdvancedWithRetAddr(T, null, 1, @returnAddress());
172 return &slice[0];106 return &slice[0];
173}107}
174108
...@@ -179,7 +113,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void {...@@ -179,7 +113,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void {
179 const T = info.child;113 const T = info.child;
180 if (@sizeOf(T) == 0) return;114 if (@sizeOf(T) == 0) return;
181 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));115 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
182 self.rawFree(non_const_ptr[0..@sizeOf(T)], info.alignment, @returnAddress());116 self.rawFree(non_const_ptr[0..@sizeOf(T)], math.log2(info.alignment), @returnAddress());
183}117}
184118
185/// Allocates an array of `n` items of type `T` and sets all the119/// Allocates an array of `n` items of type `T` and sets all the
...@@ -191,7 +125,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void {...@@ -191,7 +125,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void {
191///125///
192/// For allocating a single item, see `create`.126/// For allocating a single item, see `create`.
193pub fn alloc(self: Allocator, comptime T: type, n: usize) Error![]T {127pub fn alloc(self: Allocator, comptime T: type, n: usize) Error![]T {
194 return self.allocAdvancedWithRetAddr(T, null, n, .exact, @returnAddress());128 return self.allocAdvancedWithRetAddr(T, null, n, @returnAddress());
195}129}
196130
197pub fn allocWithOptions(131pub fn allocWithOptions(
...@@ -215,11 +149,11 @@ pub fn allocWithOptionsRetAddr(...@@ -215,11 +149,11 @@ pub fn allocWithOptionsRetAddr(
215 return_address: usize,149 return_address: usize,
216) Error!AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {150) Error!AllocWithOptionsPayload(Elem, optional_alignment, optional_sentinel) {
217 if (optional_sentinel) |sentinel| {151 if (optional_sentinel) |sentinel| {
218 const ptr = try self.allocAdvancedWithRetAddr(Elem, optional_alignment, n + 1, .exact, return_address);152 const ptr = try self.allocAdvancedWithRetAddr(Elem, optional_alignment, n + 1, return_address);
219 ptr[n] = sentinel;153 ptr[n] = sentinel;
220 return ptr[0..n :sentinel];154 return ptr[0..n :sentinel];
221 } else {155 } else {
222 return self.allocAdvancedWithRetAddr(Elem, optional_alignment, n, .exact, return_address);156 return self.allocAdvancedWithRetAddr(Elem, optional_alignment, n, return_address);
223 }157 }
224}158}
225159
...@@ -255,231 +189,108 @@ pub fn alignedAlloc(...@@ -255,231 +189,108 @@ pub fn alignedAlloc(
255 comptime alignment: ?u29,189 comptime alignment: ?u29,
256 n: usize,190 n: usize,
257) Error![]align(alignment orelse @alignOf(T)) T {191) Error![]align(alignment orelse @alignOf(T)) T {
258 return self.allocAdvancedWithRetAddr(T, alignment, n, .exact, @returnAddress());192 return self.allocAdvancedWithRetAddr(T, alignment, n, @returnAddress());
259}
260
261pub fn allocAdvanced(
262 self: Allocator,
263 comptime T: type,
264 /// null means naturally aligned
265 comptime alignment: ?u29,
266 n: usize,
267 exact: Exact,
268) Error![]align(alignment orelse @alignOf(T)) T {
269 return self.allocAdvancedWithRetAddr(T, alignment, n, exact, @returnAddress());
270}193}
271194
272pub const Exact = enum { exact, at_least };
273
274pub fn allocAdvancedWithRetAddr(195pub fn allocAdvancedWithRetAddr(
275 self: Allocator,196 self: Allocator,
276 comptime T: type,197 comptime T: type,
277 /// null means naturally aligned198 /// null means naturally aligned
278 comptime alignment: ?u29,199 comptime alignment: ?u29,
279 n: usize,200 n: usize,
280 exact: Exact,
281 return_address: usize,201 return_address: usize,
282) Error![]align(alignment orelse @alignOf(T)) T {202) Error![]align(alignment orelse @alignOf(T)) T {
283 const a = if (alignment) |a| blk: {203 const a = if (alignment) |a| blk: {
284 if (a == @alignOf(T)) return allocAdvancedWithRetAddr(self, T, null, n, exact, return_address);204 if (a == @alignOf(T)) return allocAdvancedWithRetAddr(self, T, null, n, return_address);
285 break :blk a;205 break :blk a;
286 } else @alignOf(T);206 } else @alignOf(T);
287207
208 // The Zig Allocator interface is not intended to solve allocations beyond
209 // the minimum OS page size. For these use cases, the caller must use OS
210 // APIs directly.
211 comptime assert(a <= mem.page_size);
212
288 if (n == 0) {213 if (n == 0) {
289 const ptr = comptime std.mem.alignBackward(std.math.maxInt(usize), a);214 const ptr = comptime std.mem.alignBackward(math.maxInt(usize), a);
290 return @intToPtr([*]align(a) T, ptr)[0..0];215 return @intToPtr([*]align(a) T, ptr)[0..0];
291 }216 }
292217
293 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;218 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
294 // TODO The `if (alignment == null)` blocks are workarounds for zig not being able to219 const byte_ptr = self.rawAlloc(byte_count, log2a(a), return_address) orelse return Error.OutOfMemory;
295 // access certain type information about T without creating a circular dependency in async
296 // functions that heap-allocate their own frame with @Frame(func).
297 const size_of_T: usize = if (alignment == null) @divExact(byte_count, n) else @sizeOf(T);
298 const len_align: u29 = switch (exact) {
299 .exact => 0,
300 .at_least => math.cast(u29, size_of_T) orelse 0,
301 };
302 const byte_slice = try self.rawAlloc(byte_count, a, len_align, return_address);
303 switch (exact) {
304 .exact => assert(byte_slice.len == byte_count),
305 .at_least => assert(byte_slice.len >= byte_count),
306 }
307 // TODO: https://github.com/ziglang/zig/issues/4298220 // TODO: https://github.com/ziglang/zig/issues/4298
308 @memset(byte_slice.ptr, undefined, byte_slice.len);221 @memset(byte_ptr, undefined, byte_count);
309 if (alignment == null) {222 const byte_slice = byte_ptr[0..byte_count];
310 // This if block is a workaround (see comment above)223 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));
311 return @intToPtr([*]T, @ptrToInt(byte_slice.ptr))[0..@divExact(byte_slice.len, @sizeOf(T))];
312 } else {
313 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));
314 }
315}224}
316225
317/// Increases or decreases the size of an allocation. It is guaranteed to not move the pointer.226/// Requests to modify the size of an allocation. It is guaranteed to not move
318pub fn resize(self: Allocator, old_mem: anytype, new_n: usize) ?@TypeOf(old_mem) {227/// the pointer, however the allocator implementation may refuse the resize
228/// request by returning `false`.
229pub fn resize(self: Allocator, old_mem: anytype, new_n: usize) bool {
319 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;230 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
320 const T = Slice.child;231 const T = Slice.child;
321 if (new_n == 0) {232 if (new_n == 0) {
322 self.free(old_mem);233 self.free(old_mem);
323 return &[0]T{};234 return true;
235 }
236 if (old_mem.len == 0) {
237 return false;
324 }238 }
325 const old_byte_slice = mem.sliceAsBytes(old_mem);239 const old_byte_slice = mem.sliceAsBytes(old_mem);
326 const new_byte_count = math.mul(usize, @sizeOf(T), new_n) catch return null;240 // I would like to use saturating multiplication here, but LLVM cannot lower it
327 const rc = self.rawResize(old_byte_slice, Slice.alignment, new_byte_count, 0, @returnAddress()) orelse return null;241 // on WebAssembly: https://github.com/ziglang/zig/issues/9660
328 assert(rc == new_byte_count);242 //const new_byte_count = new_n *| @sizeOf(T);
329 const new_byte_slice = old_byte_slice.ptr[0..new_byte_count];243 const new_byte_count = math.mul(usize, @sizeOf(T), new_n) catch return false;
330 return mem.bytesAsSlice(T, new_byte_slice);244 return self.rawResize(old_byte_slice, log2a(Slice.alignment), new_byte_count, @returnAddress());
331}245}
332246
333/// This function requests a new byte size for an existing allocation,247/// This function requests a new byte size for an existing allocation, which
334/// which can be larger, smaller, or the same size as the old memory248/// can be larger, smaller, or the same size as the old memory allocation.
335/// allocation.
336/// This function is preferred over `shrink`, because it can fail, even
337/// when shrinking. This gives the allocator a chance to perform a
338/// cheap shrink operation if possible, or otherwise return OutOfMemory,
339/// indicating that the caller should keep their capacity, for example
340/// in `std.ArrayList.shrink`.
341/// If you need guaranteed success, call `shrink`.
342/// If `new_n` is 0, this is the same as `free` and it always succeeds.249/// If `new_n` is 0, this is the same as `free` and it always succeeds.
343pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) t: {250pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) t: {
344 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;251 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
345 break :t Error![]align(Slice.alignment) Slice.child;252 break :t Error![]align(Slice.alignment) Slice.child;
346} {253} {
347 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;254 return self.reallocAdvanced(old_mem, new_n, @returnAddress());
348 return self.reallocAdvancedWithRetAddr(old_mem, old_alignment, new_n, .exact, @returnAddress());
349}255}
350256
351pub fn reallocAtLeast(self: Allocator, old_mem: anytype, new_n: usize) t: {
352 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
353 break :t Error![]align(Slice.alignment) Slice.child;
354} {
355 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
356 return self.reallocAdvancedWithRetAddr(old_mem, old_alignment, new_n, .at_least, @returnAddress());
357}
358
359/// This is the same as `realloc`, except caller may additionally request
360/// a new alignment, which can be larger, smaller, or the same as the old
361/// allocation.
362pub fn reallocAdvanced(257pub fn reallocAdvanced(
363 self: Allocator,258 self: Allocator,
364 old_mem: anytype,259 old_mem: anytype,
365 comptime new_alignment: u29,
366 new_n: usize,
367 exact: Exact,
368) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
369 return self.reallocAdvancedWithRetAddr(old_mem, new_alignment, new_n, exact, @returnAddress());
370}
371
372pub fn reallocAdvancedWithRetAddr(
373 self: Allocator,
374 old_mem: anytype,
375 comptime new_alignment: u29,
376 new_n: usize,260 new_n: usize,
377 exact: Exact,
378 return_address: usize,261 return_address: usize,
379) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {262) t: {
263 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
264 break :t Error![]align(Slice.alignment) Slice.child;
265} {
380 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;266 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
381 const T = Slice.child;267 const T = Slice.child;
382 if (old_mem.len == 0) {268 if (old_mem.len == 0) {
383 return self.allocAdvancedWithRetAddr(T, new_alignment, new_n, exact, return_address);269 return self.allocAdvancedWithRetAddr(T, Slice.alignment, new_n, return_address);
384 }270 }
385 if (new_n == 0) {271 if (new_n == 0) {
386 self.free(old_mem);272 self.free(old_mem);
387 const ptr = comptime std.mem.alignBackward(std.math.maxInt(usize), new_alignment);273 const ptr = comptime std.mem.alignBackward(math.maxInt(usize), Slice.alignment);
388 return @intToPtr([*]align(new_alignment) T, ptr)[0..0];274 return @intToPtr([*]align(Slice.alignment) T, ptr)[0..0];
389 }275 }
390276
391 const old_byte_slice = mem.sliceAsBytes(old_mem);277 const old_byte_slice = mem.sliceAsBytes(old_mem);
392 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;278 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;
393 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure279 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure
394 const len_align: u29 = switch (exact) {280 if (mem.isAligned(@ptrToInt(old_byte_slice.ptr), Slice.alignment)) {
395 .exact => 0,281 if (self.rawResize(old_byte_slice, log2a(Slice.alignment), byte_count, return_address)) {
396 .at_least => math.cast(u29, @as(usize, @sizeOf(T))) orelse 0,282 return mem.bytesAsSlice(T, @alignCast(Slice.alignment, old_byte_slice.ptr[0..byte_count]));
397 };
398
399 if (mem.isAligned(@ptrToInt(old_byte_slice.ptr), new_alignment)) {
400 if (byte_count <= old_byte_slice.len) {
401 const shrunk_len = self.shrinkBytes(old_byte_slice, Slice.alignment, byte_count, len_align, return_address);
402 return mem.bytesAsSlice(T, @alignCast(new_alignment, old_byte_slice.ptr[0..shrunk_len]));
403 }
404
405 if (self.rawResize(old_byte_slice, Slice.alignment, byte_count, len_align, return_address)) |resized_len| {
406 // TODO: https://github.com/ziglang/zig/issues/4298
407 @memset(old_byte_slice.ptr + byte_count, undefined, resized_len - byte_count);
408 return mem.bytesAsSlice(T, @alignCast(new_alignment, old_byte_slice.ptr[0..resized_len]));
409 }283 }
410 }284 }
411285
412 if (byte_count <= old_byte_slice.len and new_alignment <= Slice.alignment) {286 const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse
413 return error.OutOfMemory;287 return error.OutOfMemory;
414 }288 @memcpy(new_mem, old_byte_slice.ptr, @min(byte_count, old_byte_slice.len));
415
416 const new_mem = try self.rawAlloc(byte_count, new_alignment, len_align, return_address);
417 @memcpy(new_mem.ptr, old_byte_slice.ptr, math.min(byte_count, old_byte_slice.len));
418 // TODO https://github.com/ziglang/zig/issues/4298289 // TODO https://github.com/ziglang/zig/issues/4298
419 @memset(old_byte_slice.ptr, undefined, old_byte_slice.len);290 @memset(old_byte_slice.ptr, undefined, old_byte_slice.len);
420 self.rawFree(old_byte_slice, Slice.alignment, return_address);291 self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address);
421
422 return mem.bytesAsSlice(T, @alignCast(new_alignment, new_mem));
423}
424292
425/// Prefer calling realloc to shrink if you can tolerate failure, such as293 return mem.bytesAsSlice(T, @alignCast(Slice.alignment, new_mem[0..byte_count]));
426/// in an ArrayList data structure with a storage capacity.
427/// Shrink always succeeds, and `new_n` must be <= `old_mem.len`.
428/// Returned slice has same alignment as old_mem.
429/// Shrinking to 0 is the same as calling `free`.
430pub fn shrink(self: Allocator, old_mem: anytype, new_n: usize) t: {
431 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
432 break :t []align(Slice.alignment) Slice.child;
433} {
434 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
435 return self.alignedShrinkWithRetAddr(old_mem, old_alignment, new_n, @returnAddress());
436}
437
438/// This is the same as `shrink`, except caller may additionally request
439/// a new alignment, which must be smaller or the same as the old
440/// allocation.
441pub fn alignedShrink(
442 self: Allocator,
443 old_mem: anytype,
444 comptime new_alignment: u29,
445 new_n: usize,
446) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
447 return self.alignedShrinkWithRetAddr(old_mem, new_alignment, new_n, @returnAddress());
448}
449
450/// This is the same as `alignedShrink`, except caller may additionally pass
451/// the return address of the first stack frame, which may be relevant for
452/// allocators which collect stack traces.
453pub fn alignedShrinkWithRetAddr(
454 self: Allocator,
455 old_mem: anytype,
456 comptime new_alignment: u29,
457 new_n: usize,
458 return_address: usize,
459) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
460 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
461 const T = Slice.child;
462
463 if (new_n == old_mem.len)
464 return old_mem;
465 if (new_n == 0) {
466 self.free(old_mem);
467 const ptr = comptime std.mem.alignBackward(std.math.maxInt(usize), new_alignment);
468 return @intToPtr([*]align(new_alignment) T, ptr)[0..0];
469 }
470
471 assert(new_n < old_mem.len);
472 assert(new_alignment <= Slice.alignment);
473
474 // Here we skip the overflow checking on the multiplication because
475 // new_n <= old_mem.len and the multiplication didn't overflow for that operation.
476 const byte_count = @sizeOf(T) * new_n;
477
478 const old_byte_slice = mem.sliceAsBytes(old_mem);
479 // TODO: https://github.com/ziglang/zig/issues/4298
480 @memset(old_byte_slice.ptr + byte_count, undefined, old_byte_slice.len - byte_count);
481 _ = self.shrinkBytes(old_byte_slice, Slice.alignment, byte_count, 0, return_address);
482 return old_mem[0..new_n];
483}294}
484295
485/// Free an array allocated with `alloc`. To free a single item,296/// Free an array allocated with `alloc`. To free a single item,
...@@ -492,7 +303,7 @@ pub fn free(self: Allocator, memory: anytype) void {...@@ -492,7 +303,7 @@ pub fn free(self: Allocator, memory: anytype) void {
492 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));303 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));
493 // TODO: https://github.com/ziglang/zig/issues/4298304 // TODO: https://github.com/ziglang/zig/issues/4298
494 @memset(non_const_ptr, undefined, bytes_len);305 @memset(non_const_ptr, undefined, bytes_len);
495 self.rawFree(non_const_ptr[0..bytes_len], Slice.alignment, @returnAddress());306 self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress());
496}307}
497308
498/// Copies `m` to newly allocated memory. Caller owns the memory.309/// Copies `m` to newly allocated memory. Caller owns the memory.
...@@ -510,226 +321,16 @@ pub fn dupeZ(allocator: Allocator, comptime T: type, m: []const T) ![:0]T {...@@ -510,226 +321,16 @@ pub fn dupeZ(allocator: Allocator, comptime T: type, m: []const T) ![:0]T {
510 return new_buf[0..m.len :0];321 return new_buf[0..m.len :0];
511}322}
512323
513/// This function allows a runtime `alignment` value. Callers should generally prefer324/// TODO replace callsites with `@log2` after this proposal is implemented:
514/// to call the `alloc*` functions.325/// https://github.com/ziglang/zig/issues/13642
515pub fn allocBytes(326inline fn log2a(x: anytype) switch (@typeInfo(@TypeOf(x))) {
516 self: Allocator,327 .Int => math.Log2Int(@TypeOf(x)),
517 /// Must be >= 1.328 .ComptimeInt => comptime_int,
518 /// Must be a power of 2.329 else => @compileError("int please"),
519 /// Returned slice's pointer will have this alignment.330} {
520 alignment: u29,331 switch (@typeInfo(@TypeOf(x))) {
521 byte_count: usize,332 .Int => return math.log2_int(@TypeOf(x), x),
522 /// 0 indicates the length of the slice returned MUST match `byte_count` exactly333 .ComptimeInt => return math.log2(x),
523 /// non-zero means the length of the returned slice must be aligned by `len_align`334 else => @compileError("bad"),
524 /// `byte_count` must be aligned by `len_align`
525 len_align: u29,
526 return_address: usize,
527) Error![]u8 {
528 const new_mem = try self.rawAlloc(byte_count, alignment, len_align, return_address);
529 // TODO: https://github.com/ziglang/zig/issues/4298
530 @memset(new_mem.ptr, undefined, new_mem.len);
531 return new_mem;
532}
533
534test "allocBytes" {
535 const number_of_bytes: usize = 10;
536 var runtime_alignment: u29 = 2;
537
538 {
539 const new_mem = try std.testing.allocator.allocBytes(runtime_alignment, number_of_bytes, 0, @returnAddress());
540 defer std.testing.allocator.free(new_mem);
541
542 try std.testing.expectEqual(number_of_bytes, new_mem.len);
543 try std.testing.expect(mem.isAligned(@ptrToInt(new_mem.ptr), runtime_alignment));
544 }
545
546 runtime_alignment = 8;
547
548 {
549 const new_mem = try std.testing.allocator.allocBytes(runtime_alignment, number_of_bytes, 0, @returnAddress());
550 defer std.testing.allocator.free(new_mem);
551
552 try std.testing.expectEqual(number_of_bytes, new_mem.len);
553 try std.testing.expect(mem.isAligned(@ptrToInt(new_mem.ptr), runtime_alignment));
554 }
555}
556
557test "allocBytes non-zero len_align" {
558 const number_of_bytes: usize = 10;
559 var runtime_alignment: u29 = 1;
560 var len_align: u29 = 2;
561
562 {
563 const new_mem = try std.testing.allocator.allocBytes(runtime_alignment, number_of_bytes, len_align, @returnAddress());
564 defer std.testing.allocator.free(new_mem);
565
566 try std.testing.expect(new_mem.len >= number_of_bytes);
567 try std.testing.expect(new_mem.len % len_align == 0);
568 try std.testing.expect(mem.isAligned(@ptrToInt(new_mem.ptr), runtime_alignment));
569 }
570
571 runtime_alignment = 16;
572 len_align = 5;
573
574 {
575 const new_mem = try std.testing.allocator.allocBytes(runtime_alignment, number_of_bytes, len_align, @returnAddress());
576 defer std.testing.allocator.free(new_mem);
577
578 try std.testing.expect(new_mem.len >= number_of_bytes);
579 try std.testing.expect(new_mem.len % len_align == 0);
580 try std.testing.expect(mem.isAligned(@ptrToInt(new_mem.ptr), runtime_alignment));
581 }
582}
583
584/// Realloc is used to modify the size or alignment of an existing allocation,
585/// as well as to provide the allocator with an opportunity to move an allocation
586/// to a better location.
587/// The returned slice will have its pointer aligned at least to `new_alignment` bytes.
588///
589/// This function allows a runtime `alignment` value. Callers should generally prefer
590/// to call the `realloc*` functions.
591///
592/// If the size/alignment is greater than the previous allocation, and the requested new
593/// allocation could not be granted this function returns `error.OutOfMemory`.
594/// When the size/alignment is less than or equal to the previous allocation,
595/// this function returns `error.OutOfMemory` when the allocator decides the client
596/// would be better off keeping the extra alignment/size.
597/// Clients will call `resizeFn` when they require the allocator to track a new alignment/size,
598/// and so this function should only return success when the allocator considers
599/// the reallocation desirable from the allocator's perspective.
600///
601/// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle
602/// reallocation failure, even when `new_n` <= `old_mem.len`. A `FixedBufferAllocator`
603/// would always return `error.OutOfMemory` for `reallocFn` when the size/alignment
604/// is less than or equal to the old allocation, because it cannot reclaim the memory,
605/// and thus the `std.ArrayList` would be better off retaining its capacity.
606pub fn reallocBytes(
607 self: Allocator,
608 /// Must be the same as what was returned from most recent call to `allocFn` or `resizeFn`.
609 /// If `old_mem.len == 0` then this is a new allocation and `new_byte_count` must be >= 1.
610 old_mem: []u8,
611 /// If `old_mem.len == 0` then this is `undefined`, otherwise:
612 /// Must be the same as what was passed to `allocFn`.
613 /// Must be >= 1.
614 /// Must be a power of 2.
615 old_alignment: u29,
616 /// If `new_byte_count` is 0 then this is a free and it is required that `old_mem.len != 0`.
617 new_byte_count: usize,
618 /// Must be >= 1.
619 /// Must be a power of 2.
620 /// Returned slice's pointer will have this alignment.
621 new_alignment: u29,
622 /// 0 indicates the length of the slice returned MUST match `new_byte_count` exactly
623 /// non-zero means the length of the returned slice must be aligned by `len_align`
624 /// `new_byte_count` must be aligned by `len_align`
625 len_align: u29,
626 return_address: usize,
627) Error![]u8 {
628 if (old_mem.len == 0) {
629 return self.allocBytes(new_alignment, new_byte_count, len_align, return_address);
630 }
631 if (new_byte_count == 0) {
632 // TODO https://github.com/ziglang/zig/issues/4298
633 @memset(old_mem.ptr, undefined, old_mem.len);
634 self.rawFree(old_mem, old_alignment, return_address);
635 return &[0]u8{};
636 }
637
638 if (mem.isAligned(@ptrToInt(old_mem.ptr), new_alignment)) {
639 if (new_byte_count <= old_mem.len) {
640 const shrunk_len = self.shrinkBytes(old_mem, old_alignment, new_byte_count, len_align, return_address);
641 return old_mem.ptr[0..shrunk_len];
642 }
643
644 if (self.rawResize(old_mem, old_alignment, new_byte_count, len_align, return_address)) |resized_len| {
645 assert(resized_len >= new_byte_count);
646 // TODO: https://github.com/ziglang/zig/issues/4298
647 @memset(old_mem.ptr + new_byte_count, undefined, resized_len - new_byte_count);
648 return old_mem.ptr[0..resized_len];
649 }
650 }
651
652 if (new_byte_count <= old_mem.len and new_alignment <= old_alignment) {
653 return error.OutOfMemory;
654 }
655
656 const new_mem = try self.rawAlloc(new_byte_count, new_alignment, len_align, return_address);
657 @memcpy(new_mem.ptr, old_mem.ptr, math.min(new_byte_count, old_mem.len));
658
659 // TODO https://github.com/ziglang/zig/issues/4298
660 @memset(old_mem.ptr, undefined, old_mem.len);
661 self.rawFree(old_mem, old_alignment, return_address);
662
663 return new_mem;
664}
665
666test "reallocBytes" {
667 var new_mem: []u8 = &.{};
668
669 var new_byte_count: usize = 16;
670 var runtime_alignment: u29 = 4;
671
672 // `new_mem.len == 0`, this is a new allocation
673 {
674 new_mem = try std.testing.allocator.reallocBytes(new_mem, undefined, new_byte_count, runtime_alignment, 0, @returnAddress());
675 try std.testing.expectEqual(new_byte_count, new_mem.len);
676 try std.testing.expect(mem.isAligned(@ptrToInt(new_mem.ptr), runtime_alignment));
677 }
678
679 // `new_byte_count < new_mem.len`, this is a shrink, alignment is unmodified
680 new_byte_count = 14;
681 {
682 new_mem = try std.testing.allocator.reallocBytes(new_mem, runtime_alignment, new_byte_count, runtime_alignment, 0, @returnAddress());
683 try std.testing.expectEqual(new_byte_count, new_mem.len);
684 try std.testing.expect(mem.isAligned(@ptrToInt(new_mem.ptr), runtime_alignment));
685 }
686
687 // `new_byte_count < new_mem.len`, this is a shrink, alignment is decreased from 4 to 2
688 runtime_alignment = 2;
689 new_byte_count = 12;
690 {
691 new_mem = try std.testing.allocator.reallocBytes(new_mem, 4, new_byte_count, runtime_alignment, 0, @returnAddress());
692 try std.testing.expectEqual(new_byte_count, new_mem.len);
693 try std.testing.expect(mem.isAligned(@ptrToInt(new_mem.ptr), runtime_alignment));
694 }
695
696 // `new_byte_count > new_mem.len`, this is a growth, alignment is increased from 2 to 8
697 runtime_alignment = 8;
698 new_byte_count = 32;
699 {
700 new_mem = try std.testing.allocator.reallocBytes(new_mem, 2, new_byte_count, runtime_alignment, 0, @returnAddress());
701 try std.testing.expectEqual(new_byte_count, new_mem.len);
702 try std.testing.expect(mem.isAligned(@ptrToInt(new_mem.ptr), runtime_alignment));
703 }
704
705 // `new_byte_count == 0`, this is a free
706 new_byte_count = 0;
707 {
708 new_mem = try std.testing.allocator.reallocBytes(new_mem, runtime_alignment, new_byte_count, runtime_alignment, 0, @returnAddress());
709 try std.testing.expectEqual(new_byte_count, new_mem.len);
710 }335 }
711}336}
712
713/// Call `vtable.resize`, but caller guarantees that `new_len` <= `buf.len` meaning
714/// than a `null` return value should be impossible.
715/// This function allows a runtime `buf_align` value. Callers should generally prefer
716/// to call `shrink`.
717pub fn shrinkBytes(
718 self: Allocator,
719 /// Must be the same as what was returned from most recent call to `allocFn` or `resizeFn`.
720 buf: []u8,
721 /// Must be the same as what was passed to `allocFn`.
722 /// Must be >= 1.
723 /// Must be a power of 2.
724 buf_align: u29,
725 /// Must be >= 1.
726 new_len: usize,
727 /// 0 indicates the length of the slice returned MUST match `new_len` exactly
728 /// non-zero means the length of the returned slice must be aligned by `len_align`
729 /// `new_len` must be aligned by `len_align`
730 len_align: u29,
731 return_address: usize,
732) usize {
733 assert(new_len <= buf.len);
734 return self.rawResize(buf, buf_align, new_len, len_align, return_address) orelse unreachable;
735}
lib/std/meta/trailer_flags.zig+1-1
...@@ -144,7 +144,7 @@ test "TrailerFlags" {...@@ -144,7 +144,7 @@ test "TrailerFlags" {
144 .b = true,144 .b = true,
145 .c = true,145 .c = true,
146 });146 });
147 const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact);147 const slice = try testing.allocator.alignedAlloc(u8, 8, flags.sizeInBytes());
148 defer testing.allocator.free(slice);148 defer testing.allocator.free(slice);
149149
150 flags.set(slice.ptr, .b, false);150 flags.set(slice.ptr, .b, false);
lib/std/multi_array_list.zig+2-4
...@@ -288,11 +288,10 @@ pub fn MultiArrayList(comptime S: type) type {...@@ -288,11 +288,10 @@ pub fn MultiArrayList(comptime S: type) type {
288 assert(new_len <= self.capacity);288 assert(new_len <= self.capacity);
289 assert(new_len <= self.len);289 assert(new_len <= self.len);
290290
291 const other_bytes = gpa.allocAdvanced(291 const other_bytes = gpa.alignedAlloc(
292 u8,292 u8,
293 @alignOf(S),293 @alignOf(S),
294 capacityInBytes(new_len),294 capacityInBytes(new_len),
295 .exact,
296 ) catch {295 ) catch {
297 const self_slice = self.slice();296 const self_slice = self.slice();
298 inline for (fields) |field_info, i| {297 inline for (fields) |field_info, i| {
...@@ -360,11 +359,10 @@ pub fn MultiArrayList(comptime S: type) type {...@@ -360,11 +359,10 @@ pub fn MultiArrayList(comptime S: type) type {
360 /// `new_capacity` must be greater or equal to `len`.359 /// `new_capacity` must be greater or equal to `len`.
361 pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) !void {360 pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) !void {
362 assert(new_capacity >= self.len);361 assert(new_capacity >= self.len);
363 const new_bytes = try gpa.allocAdvanced(362 const new_bytes = try gpa.alignedAlloc(
364 u8,363 u8,
365 @alignOf(S),364 @alignOf(S),
366 capacityInBytes(new_capacity),365 capacityInBytes(new_capacity),
367 .exact,
368 );366 );
369 if (self.len == 0) {367 if (self.len == 0) {
370 gpa.free(self.allocatedBytes());368 gpa.free(self.allocatedBytes());
lib/std/net.zig+1-1
...@@ -825,7 +825,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A...@@ -825,7 +825,7 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) !*A
825825
826 result.addrs = try arena.alloc(Address, lookup_addrs.items.len);826 result.addrs = try arena.alloc(Address, lookup_addrs.items.len);
827 if (canon.items.len != 0) {827 if (canon.items.len != 0) {
828 result.canon_name = canon.toOwnedSlice();828 result.canon_name = try canon.toOwnedSlice();
829 }829 }
830830
831 for (lookup_addrs.items) |lookup_addr, i| {831 for (lookup_addrs.items) |lookup_addr, i| {
lib/std/pdb.zig+3-3
...@@ -478,7 +478,7 @@ fn readSparseBitVector(stream: anytype, allocator: mem.Allocator) ![]u32 {...@@ -478,7 +478,7 @@ fn readSparseBitVector(stream: anytype, allocator: mem.Allocator) ![]u32 {
478 if (bit_i == std.math.maxInt(u5)) break;478 if (bit_i == std.math.maxInt(u5)) break;
479 }479 }
480 }480 }
481 return list.toOwnedSlice();481 return try list.toOwnedSlice();
482}482}
483483
484pub const Pdb = struct {484pub const Pdb = struct {
...@@ -615,8 +615,8 @@ pub const Pdb = struct {...@@ -615,8 +615,8 @@ pub const Pdb = struct {
615 return error.InvalidDebugInfo;615 return error.InvalidDebugInfo;
616 }616 }
617617
618 self.modules = modules.toOwnedSlice();618 self.modules = try modules.toOwnedSlice();
619 self.sect_contribs = sect_contribs.toOwnedSlice();619 self.sect_contribs = try sect_contribs.toOwnedSlice();
620 }620 }
621621
622 pub fn parseInfoStream(self: *Pdb) !void {622 pub fn parseInfoStream(self: *Pdb) !void {
lib/std/segmented_list.zig+44-23
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("std.zig");1const std = @import("std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const testing = std.testing;3const testing = std.testing;
4const mem = std.mem;
4const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
56
6// Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box7// Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box
...@@ -177,24 +178,32 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -177,24 +178,32 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
177 return self.growCapacity(allocator, new_capacity);178 return self.growCapacity(allocator, new_capacity);
178 }179 }
179180
180 /// Only grows capacity, or retains current capacity181 /// Only grows capacity, or retains current capacity.
181 pub fn growCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void {182 pub fn growCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void {
182 const new_cap_shelf_count = shelfCount(new_capacity);183 const new_cap_shelf_count = shelfCount(new_capacity);
183 const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len);184 const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len);
184 if (new_cap_shelf_count > old_shelf_count) {185 if (new_cap_shelf_count <= old_shelf_count) return;
185 self.dynamic_segments = try allocator.realloc(self.dynamic_segments, new_cap_shelf_count);186
186 var i = old_shelf_count;187 const new_dynamic_segments = try allocator.alloc([*]T, new_cap_shelf_count);
187 errdefer {188 errdefer allocator.free(new_dynamic_segments);
188 self.freeShelves(allocator, i, old_shelf_count);189
189 self.dynamic_segments = allocator.shrink(self.dynamic_segments, old_shelf_count);190 var i: ShelfIndex = 0;
190 }191 while (i < old_shelf_count) : (i += 1) {
191 while (i < new_cap_shelf_count) : (i += 1) {192 new_dynamic_segments[i] = self.dynamic_segments[i];
192 self.dynamic_segments[i] = (try allocator.alloc(T, shelfSize(i))).ptr;
193 }
194 }193 }
194 errdefer while (i > old_shelf_count) : (i -= 1) {
195 allocator.free(new_dynamic_segments[i][0..shelfSize(i)]);
196 };
197 while (i < new_cap_shelf_count) : (i += 1) {
198 new_dynamic_segments[i] = (try allocator.alloc(T, shelfSize(i))).ptr;
199 }
200
201 allocator.free(self.dynamic_segments);
202 self.dynamic_segments = new_dynamic_segments;
195 }203 }
196204
197 /// Only shrinks capacity or retains current capacity205 /// Only shrinks capacity or retains current capacity.
206 /// It may fail to reduce the capacity in which case the capacity will remain unchanged.
198 pub fn shrinkCapacity(self: *Self, allocator: Allocator, new_capacity: usize) void {207 pub fn shrinkCapacity(self: *Self, allocator: Allocator, new_capacity: usize) void {
199 if (new_capacity <= prealloc_item_count) {208 if (new_capacity <= prealloc_item_count) {
200 const len = @intCast(ShelfIndex, self.dynamic_segments.len);209 const len = @intCast(ShelfIndex, self.dynamic_segments.len);
...@@ -207,12 +216,24 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -207,12 +216,24 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
207 const new_cap_shelf_count = shelfCount(new_capacity);216 const new_cap_shelf_count = shelfCount(new_capacity);
208 const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len);217 const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len);
209 assert(new_cap_shelf_count <= old_shelf_count);218 assert(new_cap_shelf_count <= old_shelf_count);
210 if (new_cap_shelf_count == old_shelf_count) {219 if (new_cap_shelf_count == old_shelf_count) return;
211 return;
212 }
213220
221 // freeShelves() must be called before resizing the dynamic
222 // segments, but we don't know if resizing the dynamic segments
223 // will work until we try it. So we must allocate a fresh memory
224 // buffer in order to reduce capacity.
225 const new_dynamic_segments = allocator.alloc([*]T, new_cap_shelf_count) catch return;
214 self.freeShelves(allocator, old_shelf_count, new_cap_shelf_count);226 self.freeShelves(allocator, old_shelf_count, new_cap_shelf_count);
215 self.dynamic_segments = allocator.shrink(self.dynamic_segments, new_cap_shelf_count);227 if (allocator.resize(self.dynamic_segments, new_cap_shelf_count)) {
228 // We didn't need the new memory allocation after all.
229 self.dynamic_segments = self.dynamic_segments[0..new_cap_shelf_count];
230 allocator.free(new_dynamic_segments);
231 } else {
232 // Good thing we allocated that new memory slice.
233 mem.copy([*]T, new_dynamic_segments, self.dynamic_segments[0..new_cap_shelf_count]);
234 allocator.free(self.dynamic_segments);
235 self.dynamic_segments = new_dynamic_segments;
236 }
216 }237 }
217238
218 pub fn shrink(self: *Self, new_len: usize) void {239 pub fn shrink(self: *Self, new_len: usize) void {
...@@ -227,10 +248,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -227,10 +248,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
227248
228 var i = start;249 var i = start;
229 if (end <= prealloc_item_count) {250 if (end <= prealloc_item_count) {
230 std.mem.copy(T, dest[i - start ..], self.prealloc_segment[i..end]);251 mem.copy(T, dest[i - start ..], self.prealloc_segment[i..end]);
231 return;252 return;
232 } else if (i < prealloc_item_count) {253 } else if (i < prealloc_item_count) {
233 std.mem.copy(T, dest[i - start ..], self.prealloc_segment[i..]);254 mem.copy(T, dest[i - start ..], self.prealloc_segment[i..]);
234 i = prealloc_item_count;255 i = prealloc_item_count;
235 }256 }
236257
...@@ -239,7 +260,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -239,7 +260,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
239 const copy_start = boxIndex(i, shelf_index);260 const copy_start = boxIndex(i, shelf_index);
240 const copy_end = std.math.min(shelfSize(shelf_index), copy_start + end - i);261 const copy_end = std.math.min(shelfSize(shelf_index), copy_start + end - i);
241262
242 std.mem.copy(263 mem.copy(
243 T,264 T,
244 dest[i - start ..],265 dest[i - start ..],
245 self.dynamic_segments[shelf_index][copy_start..copy_end],266 self.dynamic_segments[shelf_index][copy_start..copy_end],
...@@ -480,13 +501,13 @@ fn testSegmentedList(comptime prealloc: usize) !void {...@@ -480,13 +501,13 @@ fn testSegmentedList(comptime prealloc: usize) !void {
480 control[@intCast(usize, i)] = i + 1;501 control[@intCast(usize, i)] = i + 1;
481 }502 }
482503
483 std.mem.set(i32, dest[0..], 0);504 mem.set(i32, dest[0..], 0);
484 list.writeToSlice(dest[0..], 0);505 list.writeToSlice(dest[0..], 0);
485 try testing.expect(std.mem.eql(i32, control[0..], dest[0..]));506 try testing.expect(mem.eql(i32, control[0..], dest[0..]));
486507
487 std.mem.set(i32, dest[0..], 0);508 mem.set(i32, dest[0..], 0);
488 list.writeToSlice(dest[50..], 50);509 list.writeToSlice(dest[50..], 50);
489 try testing.expect(std.mem.eql(i32, control[50..], dest[50..]));510 try testing.expect(mem.eql(i32, control[50..], dest[50..]));
490 }511 }
491512
492 try list.setCapacity(testing.allocator, 0);513 try list.setCapacity(testing.allocator, 0);
lib/std/testing/failing_allocator.zig+30-20
...@@ -47,16 +47,23 @@ pub const FailingAllocator = struct {...@@ -47,16 +47,23 @@ pub const FailingAllocator = struct {
47 }47 }
4848
49 pub fn allocator(self: *FailingAllocator) mem.Allocator {49 pub fn allocator(self: *FailingAllocator) mem.Allocator {
50 return mem.Allocator.init(self, alloc, resize, free);50 return .{
51 .ptr = self,
52 .vtable = &.{
53 .alloc = alloc,
54 .resize = resize,
55 .free = free,
56 },
57 };
51 }58 }
5259
53 fn alloc(60 fn alloc(
54 self: *FailingAllocator,61 ctx: *anyopaque,
55 len: usize,62 len: usize,
56 ptr_align: u29,63 log2_ptr_align: u8,
57 len_align: u29,
58 return_address: usize,64 return_address: usize,
59 ) error{OutOfMemory}![]u8 {65 ) ?[*]u8 {
66 const self = @ptrCast(*FailingAllocator, @alignCast(@alignOf(FailingAllocator), ctx));
60 if (self.index == self.fail_index) {67 if (self.index == self.fail_index) {
61 if (!self.has_induced_failure) {68 if (!self.has_induced_failure) {
62 mem.set(usize, &self.stack_addresses, 0);69 mem.set(usize, &self.stack_addresses, 0);
...@@ -67,39 +74,42 @@ pub const FailingAllocator = struct {...@@ -67,39 +74,42 @@ pub const FailingAllocator = struct {
67 std.debug.captureStackTrace(return_address, &stack_trace);74 std.debug.captureStackTrace(return_address, &stack_trace);
68 self.has_induced_failure = true;75 self.has_induced_failure = true;
69 }76 }
70 return error.OutOfMemory;77 return null;
71 }78 }
72 const result = try self.internal_allocator.rawAlloc(len, ptr_align, len_align, return_address);79 const result = self.internal_allocator.rawAlloc(len, log2_ptr_align, return_address) orelse
73 self.allocated_bytes += result.len;80 return null;
81 self.allocated_bytes += len;
74 self.allocations += 1;82 self.allocations += 1;
75 self.index += 1;83 self.index += 1;
76 return result;84 return result;
77 }85 }
7886
79 fn resize(87 fn resize(
80 self: *FailingAllocator,88 ctx: *anyopaque,
81 old_mem: []u8,89 old_mem: []u8,
82 old_align: u29,90 log2_old_align: u8,
83 new_len: usize,91 new_len: usize,
84 len_align: u29,
85 ra: usize,92 ra: usize,
86 ) ?usize {93 ) bool {
87 const r = self.internal_allocator.rawResize(old_mem, old_align, new_len, len_align, ra) orelse return null;94 const self = @ptrCast(*FailingAllocator, @alignCast(@alignOf(FailingAllocator), ctx));
88 if (r < old_mem.len) {95 if (!self.internal_allocator.rawResize(old_mem, log2_old_align, new_len, ra))
89 self.freed_bytes += old_mem.len - r;96 return false;
97 if (new_len < old_mem.len) {
98 self.freed_bytes += old_mem.len - new_len;
90 } else {99 } else {
91 self.allocated_bytes += r - old_mem.len;100 self.allocated_bytes += new_len - old_mem.len;
92 }101 }
93 return r;102 return true;
94 }103 }
95104
96 fn free(105 fn free(
97 self: *FailingAllocator,106 ctx: *anyopaque,
98 old_mem: []u8,107 old_mem: []u8,
99 old_align: u29,108 log2_old_align: u8,
100 ra: usize,109 ra: usize,
101 ) void {110 ) void {
102 self.internal_allocator.rawFree(old_mem, old_align, ra);111 const self = @ptrCast(*FailingAllocator, @alignCast(@alignOf(FailingAllocator), ctx));
112 self.internal_allocator.rawFree(old_mem, log2_old_align, ra);
103 self.deallocations += 1;113 self.deallocations += 1;
104 self.freed_bytes += old_mem.len;114 self.freed_bytes += old_mem.len;
105 }115 }
lib/std/unicode.zig+2-9
...@@ -611,12 +611,7 @@ pub fn utf16leToUtf8AllocZ(allocator: mem.Allocator, utf16le: []const u16) ![:0]...@@ -611,12 +611,7 @@ pub fn utf16leToUtf8AllocZ(allocator: mem.Allocator, utf16le: []const u16) ![:0]
611 assert((utf8Encode(codepoint, result.items[out_index..]) catch unreachable) == utf8_len);611 assert((utf8Encode(codepoint, result.items[out_index..]) catch unreachable) == utf8_len);
612 out_index += utf8_len;612 out_index += utf8_len;
613 }613 }
614614 return result.toOwnedSliceSentinel(0);
615 const len = result.items.len;
616
617 try result.append(0);
618
619 return result.toOwnedSlice()[0..len :0];
620}615}
621616
622/// Asserts that the output buffer is big enough.617/// Asserts that the output buffer is big enough.
...@@ -714,9 +709,7 @@ pub fn utf8ToUtf16LeWithNull(allocator: mem.Allocator, utf8: []const u8) ![:0]u1...@@ -714,9 +709,7 @@ pub fn utf8ToUtf16LeWithNull(allocator: mem.Allocator, utf8: []const u8) ![:0]u1
714 }709 }
715 }710 }
716711
717 const len = result.items.len;712 return result.toOwnedSliceSentinel(0);
718 try result.append(0);
719 return result.toOwnedSlice()[0..len :0];
720}713}
721714
722/// Returns index of next character. If exact fit, returned index equals output slice length.715/// Returns index of next character. If exact fit, returned index equals output slice length.
lib/std/zig/parse.zig+2-2
...@@ -72,8 +72,8 @@ pub fn parse(gpa: Allocator, source: [:0]const u8) Allocator.Error!Ast {...@@ -72,8 +72,8 @@ pub fn parse(gpa: Allocator, source: [:0]const u8) Allocator.Error!Ast {
72 .source = source,72 .source = source,
73 .tokens = tokens.toOwnedSlice(),73 .tokens = tokens.toOwnedSlice(),
74 .nodes = parser.nodes.toOwnedSlice(),74 .nodes = parser.nodes.toOwnedSlice(),
75 .extra_data = parser.extra_data.toOwnedSlice(gpa),75 .extra_data = try parser.extra_data.toOwnedSlice(gpa),
76 .errors = parser.errors.toOwnedSlice(gpa),76 .errors = try parser.errors.toOwnedSlice(gpa),
77 };77 };
78}78}
7979
src/AstGen.zig+2-2
...@@ -199,8 +199,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {...@@ -199,8 +199,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
199199
200 return Zir{200 return Zir{
201 .instructions = astgen.instructions.toOwnedSlice(),201 .instructions = astgen.instructions.toOwnedSlice(),
202 .string_bytes = astgen.string_bytes.toOwnedSlice(gpa),202 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
203 .extra = astgen.extra.toOwnedSlice(gpa),203 .extra = try astgen.extra.toOwnedSlice(gpa),
204 };204 };
205}205}
206206
src/Autodoc.zig+12-12
...@@ -146,46 +146,46 @@ pub fn generateZirData(self: *Autodoc) !void {...@@ -146,46 +146,46 @@ pub fn generateZirData(self: *Autodoc) !void {
146 .c_ulonglong_type,146 .c_ulonglong_type,
147 .c_longdouble_type,147 .c_longdouble_type,
148 => .{148 => .{
149 .Int = .{ .name = tmpbuf.toOwnedSlice() },149 .Int = .{ .name = try tmpbuf.toOwnedSlice() },
150 },150 },
151 .f16_type,151 .f16_type,
152 .f32_type,152 .f32_type,
153 .f64_type,153 .f64_type,
154 .f128_type,154 .f128_type,
155 => .{155 => .{
156 .Float = .{ .name = tmpbuf.toOwnedSlice() },156 .Float = .{ .name = try tmpbuf.toOwnedSlice() },
157 },157 },
158 .comptime_int_type => .{158 .comptime_int_type => .{
159 .ComptimeInt = .{ .name = tmpbuf.toOwnedSlice() },159 .ComptimeInt = .{ .name = try tmpbuf.toOwnedSlice() },
160 },160 },
161 .comptime_float_type => .{161 .comptime_float_type => .{
162 .ComptimeFloat = .{ .name = tmpbuf.toOwnedSlice() },162 .ComptimeFloat = .{ .name = try tmpbuf.toOwnedSlice() },
163 },163 },
164164
165 .anyopaque_type => .{165 .anyopaque_type => .{
166 .ComptimeExpr = .{ .name = tmpbuf.toOwnedSlice() },166 .ComptimeExpr = .{ .name = try tmpbuf.toOwnedSlice() },
167 },167 },
168 .bool_type => .{168 .bool_type => .{
169 .Bool = .{ .name = tmpbuf.toOwnedSlice() },169 .Bool = .{ .name = try tmpbuf.toOwnedSlice() },
170 },170 },
171171
172 .noreturn_type => .{172 .noreturn_type => .{
173 .NoReturn = .{ .name = tmpbuf.toOwnedSlice() },173 .NoReturn = .{ .name = try tmpbuf.toOwnedSlice() },
174 },174 },
175 .void_type => .{175 .void_type => .{
176 .Void = .{ .name = tmpbuf.toOwnedSlice() },176 .Void = .{ .name = try tmpbuf.toOwnedSlice() },
177 },177 },
178 .type_info_type => .{178 .type_info_type => .{
179 .ComptimeExpr = .{ .name = tmpbuf.toOwnedSlice() },179 .ComptimeExpr = .{ .name = try tmpbuf.toOwnedSlice() },
180 },180 },
181 .type_type => .{181 .type_type => .{
182 .Type = .{ .name = tmpbuf.toOwnedSlice() },182 .Type = .{ .name = try tmpbuf.toOwnedSlice() },
183 },183 },
184 .anyerror_type => .{184 .anyerror_type => .{
185 .ErrorSet = .{ .name = tmpbuf.toOwnedSlice() },185 .ErrorSet = .{ .name = try tmpbuf.toOwnedSlice() },
186 },186 },
187 .calling_convention_inline, .calling_convention_c, .calling_convention_type => .{187 .calling_convention_inline, .calling_convention_c, .calling_convention_type => .{
188 .EnumLiteral = .{ .name = tmpbuf.toOwnedSlice() },188 .EnumLiteral = .{ .name = try tmpbuf.toOwnedSlice() },
189 },189 },
190 },190 },
191 );191 );
src/Compilation.zig+2-2
...@@ -5052,7 +5052,7 @@ fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []con...@@ -5052,7 +5052,7 @@ fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []con
5052 while (lines.next()) |line| {5052 while (lines.next()) |line| {
5053 if (mem.startsWith(u8, line, prefix ++ ":")) {5053 if (mem.startsWith(u8, line, prefix ++ ":")) {
5054 if (current_err) |err| {5054 if (current_err) |err| {
5055 err.context_lines = context_lines.toOwnedSlice();5055 err.context_lines = try context_lines.toOwnedSlice();
5056 }5056 }
50575057
5058 var split = std.mem.split(u8, line, "error: ");5058 var split = std.mem.split(u8, line, "error: ");
...@@ -5078,7 +5078,7 @@ fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []con...@@ -5078,7 +5078,7 @@ fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []con
5078 }5078 }
50795079
5080 if (current_err) |err| {5080 if (current_err) |err| {
5081 err.context_lines = context_lines.toOwnedSlice();5081 err.context_lines = try context_lines.toOwnedSlice();
5082 }5082 }
5083}5083}
50845084
src/Liveness.zig+2-2
...@@ -79,7 +79,7 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {...@@ -79,7 +79,7 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
79 return Liveness{79 return Liveness{
80 .tomb_bits = a.tomb_bits,80 .tomb_bits = a.tomb_bits,
81 .special = a.special,81 .special = a.special,
82 .extra = a.extra.toOwnedSlice(gpa),82 .extra = try a.extra.toOwnedSlice(gpa),
83 };83 };
84}84}
8585
...@@ -594,7 +594,7 @@ pub fn getSwitchBr(l: Liveness, gpa: Allocator, inst: Air.Inst.Index, cases_len:...@@ -594,7 +594,7 @@ pub fn getSwitchBr(l: Liveness, gpa: Allocator, inst: Air.Inst.Index, cases_len:
594 deaths.appendAssumeCapacity(else_deaths);594 deaths.appendAssumeCapacity(else_deaths);
595 }595 }
596 return SwitchBrTable{596 return SwitchBrTable{
597 .deaths = deaths.toOwnedSlice(),597 .deaths = try deaths.toOwnedSlice(),
598 };598 };
599}599}
600600
src/Module.zig+34-26
...@@ -53,12 +53,12 @@ local_zir_cache: Compilation.Directory,...@@ -53,12 +53,12 @@ local_zir_cache: Compilation.Directory,
53/// map of Decl indexes to details about them being exported.53/// map of Decl indexes to details about them being exported.
54/// The Export memory is owned by the `export_owners` table; the slice itself54/// The Export memory is owned by the `export_owners` table; the slice itself
55/// is owned by this table. The slice is guaranteed to not be empty.55/// is owned by this table. The slice is guaranteed to not be empty.
56decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, []*Export) = .{},56decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{},
57/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl57/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
58/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that58/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
59/// is performing the export of another Decl.59/// is performing the export of another Decl.
60/// This table owns the Export memory.60/// This table owns the Export memory.
61export_owners: std.AutoArrayHashMapUnmanaged(Decl.Index, []*Export) = .{},61export_owners: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{},
62/// The set of all the Zig source files in the Module. We keep track of this in order62/// The set of all the Zig source files in the Module. We keep track of this in order
63/// to iterate over it and check which source files have been modified on the file system when63/// to iterate over it and check which source files have been modified on the file system when
64/// an update is requested, as well as to cache `@import` results.64/// an update is requested, as well as to cache `@import` results.
...@@ -80,7 +80,7 @@ embed_table: std.StringHashMapUnmanaged(*EmbedFile) = .{},...@@ -80,7 +80,7 @@ embed_table: std.StringHashMapUnmanaged(*EmbedFile) = .{},
80/// This table uses an optional index so that when a Decl is destroyed, the string literal80/// This table uses an optional index so that when a Decl is destroyed, the string literal
81/// is still reclaimable by a future Decl.81/// is still reclaimable by a future Decl.
82string_literal_table: std.HashMapUnmanaged(StringLiteralContext.Key, Decl.OptionalIndex, StringLiteralContext, std.hash_map.default_max_load_percentage) = .{},82string_literal_table: std.HashMapUnmanaged(StringLiteralContext.Key, Decl.OptionalIndex, StringLiteralContext, std.hash_map.default_max_load_percentage) = .{},
83string_literal_bytes: std.ArrayListUnmanaged(u8) = .{},83string_literal_bytes: ArrayListUnmanaged(u8) = .{},
8484
85/// The set of all the generic function instantiations. This is used so that when a generic85/// The set of all the generic function instantiations. This is used so that when a generic
86/// function is called twice with the same comptime parameter arguments, both calls dispatch86/// function is called twice with the same comptime parameter arguments, both calls dispatch
...@@ -163,7 +163,7 @@ test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},...@@ -163,7 +163,7 @@ test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
163/// multi-threaded contention on an atomic counter.163/// multi-threaded contention on an atomic counter.
164allocated_decls: std.SegmentedList(Decl, 0) = .{},164allocated_decls: std.SegmentedList(Decl, 0) = .{},
165/// When a Decl object is freed from `allocated_decls`, it is pushed into this stack.165/// When a Decl object is freed from `allocated_decls`, it is pushed into this stack.
166decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},166decls_free_list: ArrayListUnmanaged(Decl.Index) = .{},
167167
168global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{},168global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{},
169169
...@@ -173,7 +173,7 @@ reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct {...@@ -173,7 +173,7 @@ reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct {
173}) = .{},173}) = .{},
174174
175pub const StringLiteralContext = struct {175pub const StringLiteralContext = struct {
176 bytes: *std.ArrayListUnmanaged(u8),176 bytes: *ArrayListUnmanaged(u8),
177177
178 pub const Key = struct {178 pub const Key = struct {
179 index: u32,179 index: u32,
...@@ -192,7 +192,7 @@ pub const StringLiteralContext = struct {...@@ -192,7 +192,7 @@ pub const StringLiteralContext = struct {
192};192};
193193
194pub const StringLiteralAdapter = struct {194pub const StringLiteralAdapter = struct {
195 bytes: *std.ArrayListUnmanaged(u8),195 bytes: *ArrayListUnmanaged(u8),
196196
197 pub fn eql(self: @This(), a_slice: []const u8, b: StringLiteralContext.Key) bool {197 pub fn eql(self: @This(), a_slice: []const u8, b: StringLiteralContext.Key) bool {
198 const b_slice = self.bytes.items[b.index..][0..b.len];198 const b_slice = self.bytes.items[b.index..][0..b.len];
...@@ -1896,11 +1896,11 @@ pub const File = struct {...@@ -1896,11 +1896,11 @@ pub const File = struct {
18961896
1897 /// Used by change detection algorithm, after astgen, contains the1897 /// Used by change detection algorithm, after astgen, contains the
1898 /// set of decls that existed in the previous ZIR but not in the new one.1898 /// set of decls that existed in the previous ZIR but not in the new one.
1899 deleted_decls: std.ArrayListUnmanaged(Decl.Index) = .{},1899 deleted_decls: ArrayListUnmanaged(Decl.Index) = .{},
1900 /// Used by change detection algorithm, after astgen, contains the1900 /// Used by change detection algorithm, after astgen, contains the
1901 /// set of decls that existed both in the previous ZIR and in the new one,1901 /// set of decls that existed both in the previous ZIR and in the new one,
1902 /// but their source code has been modified.1902 /// but their source code has been modified.
1903 outdated_decls: std.ArrayListUnmanaged(Decl.Index) = .{},1903 outdated_decls: ArrayListUnmanaged(Decl.Index) = .{},
19041904
1905 /// The most recent successful ZIR for this file, with no errors.1905 /// The most recent successful ZIR for this file, with no errors.
1906 /// This is only populated when a previously successful ZIR1906 /// This is only populated when a previously successful ZIR
...@@ -3438,12 +3438,12 @@ pub fn deinit(mod: *Module) void {...@@ -3438,12 +3438,12 @@ pub fn deinit(mod: *Module) void {
34383438
3439 mod.compile_log_decls.deinit(gpa);3439 mod.compile_log_decls.deinit(gpa);
34403440
3441 for (mod.decl_exports.values()) |export_list| {3441 for (mod.decl_exports.values()) |*export_list| {
3442 gpa.free(export_list);3442 export_list.deinit(gpa);
3443 }3443 }
3444 mod.decl_exports.deinit(gpa);3444 mod.decl_exports.deinit(gpa);
34453445
3446 for (mod.export_owners.values()) |value| {3446 for (mod.export_owners.values()) |*value| {
3447 freeExportList(gpa, value);3447 freeExportList(gpa, value);
3448 }3448 }
3449 mod.export_owners.deinit(gpa);3449 mod.export_owners.deinit(gpa);
...@@ -3533,13 +3533,13 @@ pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool {...@@ -3533,13 +3533,13 @@ pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool {
3533 return decl_index == decl.src_namespace.getDeclIndex();3533 return decl_index == decl.src_namespace.getDeclIndex();
3534}3534}
35353535
3536fn freeExportList(gpa: Allocator, export_list: []*Export) void {3536fn freeExportList(gpa: Allocator, export_list: *ArrayListUnmanaged(*Export)) void {
3537 for (export_list) |exp| {3537 for (export_list.items) |exp| {
3538 gpa.free(exp.options.name);3538 gpa.free(exp.options.name);
3539 if (exp.options.section) |s| gpa.free(s);3539 if (exp.options.section) |s| gpa.free(s);
3540 gpa.destroy(exp);3540 gpa.destroy(exp);
3541 }3541 }
3542 gpa.free(export_list);3542 export_list.deinit(gpa);
3543}3543}
35443544
3545const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;3545const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
...@@ -3822,7 +3822,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3822,7 +3822,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3822 .byte_abs = token_starts[parse_err.token] + extra_offset,3822 .byte_abs = token_starts[parse_err.token] + extra_offset,
3823 },3823 },
3824 },3824 },
3825 .msg = msg.toOwnedSlice(),3825 .msg = try msg.toOwnedSlice(),
3826 };3826 };
3827 if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) {3827 if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) {
3828 const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len);3828 const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len);
...@@ -3845,7 +3845,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3845,7 +3845,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3845 .parent_decl_node = 0,3845 .parent_decl_node = 0,
3846 .lazy = .{ .token_abs = note.token },3846 .lazy = .{ .token_abs = note.token },
3847 },3847 },
3848 .msg = msg.toOwnedSlice(),3848 .msg = try msg.toOwnedSlice(),
3849 };3849 };
3850 }3850 }
38513851
...@@ -3981,7 +3981,7 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {...@@ -3981,7 +3981,7 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
3981 // Walk the Decl graph, updating ZIR indexes, strings, and populating3981 // Walk the Decl graph, updating ZIR indexes, strings, and populating
3982 // the deleted and outdated lists.3982 // the deleted and outdated lists.
39833983
3984 var decl_stack: std.ArrayListUnmanaged(Decl.Index) = .{};3984 var decl_stack: ArrayListUnmanaged(Decl.Index) = .{};
3985 defer decl_stack.deinit(gpa);3985 defer decl_stack.deinit(gpa);
39863986
3987 const root_decl = file.root_decl.unwrap().?;3987 const root_decl = file.root_decl.unwrap().?;
...@@ -4146,7 +4146,7 @@ pub fn mapOldZirToNew(...@@ -4146,7 +4146,7 @@ pub fn mapOldZirToNew(
4146 old_inst: Zir.Inst.Index,4146 old_inst: Zir.Inst.Index,
4147 new_inst: Zir.Inst.Index,4147 new_inst: Zir.Inst.Index,
4148 };4148 };
4149 var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{};4149 var match_stack: ArrayListUnmanaged(MatchedZirDecl) = .{};
4150 defer match_stack.deinit(gpa);4150 defer match_stack.deinit(gpa);
41514151
4152 // Main struct inst is always the same4152 // Main struct inst is always the same
...@@ -5488,12 +5488,12 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {...@@ -5488,12 +5488,12 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
5488/// Delete all the Export objects that are caused by this Decl. Re-analysis of5488/// Delete all the Export objects that are caused by this Decl. Re-analysis of
5489/// this Decl will cause them to be re-created (or not).5489/// this Decl will cause them to be re-created (or not).
5490fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {5490fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
5491 const kv = mod.export_owners.fetchSwapRemove(decl_index) orelse return;5491 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;
54925492
5493 for (kv.value) |exp| {5493 for (export_owners.items) |exp| {
5494 if (mod.decl_exports.getPtr(exp.exported_decl)) |value_ptr| {5494 if (mod.decl_exports.getPtr(exp.exported_decl)) |value_ptr| {
5495 // Remove exports with owner_decl matching the regenerating decl.5495 // Remove exports with owner_decl matching the regenerating decl.
5496 const list = value_ptr.*;5496 const list = value_ptr.items;
5497 var i: usize = 0;5497 var i: usize = 0;
5498 var new_len = list.len;5498 var new_len = list.len;
5499 while (i < new_len) {5499 while (i < new_len) {
...@@ -5504,7 +5504,7 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {...@@ -5504,7 +5504,7 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
5504 i += 1;5504 i += 1;
5505 }5505 }
5506 }5506 }
5507 value_ptr.* = mod.gpa.shrink(list, new_len);5507 value_ptr.shrinkAndFree(mod.gpa, new_len);
5508 if (new_len == 0) {5508 if (new_len == 0) {
5509 assert(mod.decl_exports.swapRemove(exp.exported_decl));5509 assert(mod.decl_exports.swapRemove(exp.exported_decl));
5510 }5510 }
...@@ -5527,7 +5527,7 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {...@@ -5527,7 +5527,7 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
5527 mod.gpa.free(exp.options.name);5527 mod.gpa.free(exp.options.name);
5528 mod.gpa.destroy(exp);5528 mod.gpa.destroy(exp);
5529 }5529 }
5530 mod.gpa.free(kv.value);5530 export_owners.deinit(mod.gpa);
5531}5531}
55325532
5533pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {5533pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
...@@ -5745,8 +5745,8 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {...@@ -5745,8 +5745,8 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
57455745
5746 return Air{5746 return Air{
5747 .instructions = sema.air_instructions.toOwnedSlice(),5747 .instructions = sema.air_instructions.toOwnedSlice(),
5748 .extra = sema.air_extra.toOwnedSlice(gpa),5748 .extra = try sema.air_extra.toOwnedSlice(gpa),
5749 .values = sema.air_values.toOwnedSlice(gpa),5749 .values = try sema.air_values.toOwnedSlice(gpa),
5750 };5750 };
5751}5751}
57525752
...@@ -6415,7 +6415,7 @@ pub fn processExports(mod: *Module) !void {...@@ -6415,7 +6415,7 @@ pub fn processExports(mod: *Module) !void {
6415 var it = mod.decl_exports.iterator();6415 var it = mod.decl_exports.iterator();
6416 while (it.next()) |entry| {6416 while (it.next()) |entry| {
6417 const exported_decl = entry.key_ptr.*;6417 const exported_decl = entry.key_ptr.*;
6418 const exports = entry.value_ptr.*;6418 const exports = entry.value_ptr.items;
6419 for (exports) |new_export| {6419 for (exports) |new_export| {
6420 const gop = try symbol_exports.getOrPut(gpa, new_export.options.name);6420 const gop = try symbol_exports.getOrPut(gpa, new_export.options.name);
6421 if (gop.found_existing) {6421 if (gop.found_existing) {
...@@ -6695,3 +6695,11 @@ pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u...@@ -6695,3 +6695,11 @@ pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u
6695pub fn wantDllExports(mod: Module) bool {6695pub fn wantDllExports(mod: Module) bool {
6696 return mod.comp.bin_file.options.dll_export_fns and mod.getTarget().os.tag == .windows;6696 return mod.comp.bin_file.options.dll_export_fns and mod.getTarget().os.tag == .windows;
6697}6697}
6698
6699pub fn getDeclExports(mod: Module, decl_index: Decl.Index) []const *Export {
6700 if (mod.decl_exports.get(decl_index)) |l| {
6701 return l.items;
6702 } else {
6703 return &[0]*Export{};
6704 }
6705}
src/Sema.zig+11-12
...@@ -2244,7 +2244,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {...@@ -2244,7 +2244,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
2244 .hidden = cur_reference_trace - max_references,2244 .hidden = cur_reference_trace - max_references,
2245 });2245 });
2246 }2246 }
2247 err_msg.reference_trace = reference_stack.toOwnedSlice();2247 err_msg.reference_trace = try reference_stack.toOwnedSlice();
2248 }2248 }
2249 if (sema.owner_func) |func| {2249 if (sema.owner_func) |func| {
2250 func.state = .sema_failure;2250 func.state = .sema_failure;
...@@ -5500,20 +5500,18 @@ pub fn analyzeExport(...@@ -5500,20 +5500,18 @@ pub fn analyzeExport(
5500 // Add to export_owners table.5500 // Add to export_owners table.
5501 const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index);5501 const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index);
5502 if (!eo_gop.found_existing) {5502 if (!eo_gop.found_existing) {
5503 eo_gop.value_ptr.* = &[0]*Export{};5503 eo_gop.value_ptr.* = .{};
5504 }5504 }
5505 eo_gop.value_ptr.* = try gpa.realloc(eo_gop.value_ptr.*, eo_gop.value_ptr.len + 1);5505 try eo_gop.value_ptr.append(gpa, new_export);
5506 eo_gop.value_ptr.*[eo_gop.value_ptr.len - 1] = new_export;5506 errdefer _ = eo_gop.value_ptr.pop();
5507 errdefer eo_gop.value_ptr.* = gpa.shrink(eo_gop.value_ptr.*, eo_gop.value_ptr.len - 1);
55085507
5509 // Add to exported_decl table.5508 // Add to exported_decl table.
5510 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index);5509 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index);
5511 if (!de_gop.found_existing) {5510 if (!de_gop.found_existing) {
5512 de_gop.value_ptr.* = &[0]*Export{};5511 de_gop.value_ptr.* = .{};
5513 }5512 }
5514 de_gop.value_ptr.* = try gpa.realloc(de_gop.value_ptr.*, de_gop.value_ptr.len + 1);5513 try de_gop.value_ptr.append(gpa, new_export);
5515 de_gop.value_ptr.*[de_gop.value_ptr.len - 1] = new_export;5514 errdefer _ = de_gop.value_ptr.pop();
5516 errdefer de_gop.value_ptr.* = gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);
5517}5515}
55185516
5519fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {5517fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
...@@ -10762,7 +10760,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10762,7 +10760,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10762 .payload = undefined,10760 .payload = undefined,
10763 },10761 },
10764 } });10762 } });
10765 var cond_body = case_block.instructions.toOwnedSlice(gpa);10763 var cond_body = try case_block.instructions.toOwnedSlice(gpa);
10766 defer gpa.free(cond_body);10764 defer gpa.free(cond_body);
1076710765
10768 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);10766 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
...@@ -10800,7 +10798,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10800,7 +10798,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10800 sema.air_extra.appendSliceAssumeCapacity(cond_body);10798 sema.air_extra.appendSliceAssumeCapacity(cond_body);
10801 }10799 }
10802 gpa.free(prev_then_body);10800 gpa.free(prev_then_body);
10803 prev_then_body = case_block.instructions.toOwnedSlice(gpa);10801 prev_then_body = try case_block.instructions.toOwnedSlice(gpa);
10804 prev_cond_br = new_cond_br;10802 prev_cond_br = new_cond_br;
10805 }10803 }
10806 }10804 }
...@@ -16318,7 +16316,7 @@ fn zirCondbr(...@@ -16318,7 +16316,7 @@ fn zirCondbr(
16318 defer sub_block.instructions.deinit(gpa);16316 defer sub_block.instructions.deinit(gpa);
1631916317
16320 try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);16318 try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
16321 const true_instructions = sub_block.instructions.toOwnedSlice(gpa);16319 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
16322 defer gpa.free(true_instructions);16320 defer gpa.free(true_instructions);
1632316321
16324 const err_cond = blk: {16322 const err_cond = blk: {
...@@ -19301,6 +19299,7 @@ fn zirBitCount(...@@ -19301,6 +19299,7 @@ fn zirBitCount(
19301 .Int => {19299 .Int => {
19302 if (try sema.resolveMaybeUndefVal(operand)) |val| {19300 if (try sema.resolveMaybeUndefVal(operand)) |val| {
19303 if (val.isUndef()) return sema.addConstUndef(result_scalar_ty);19301 if (val.isUndef()) return sema.addConstUndef(result_scalar_ty);
19302 try sema.resolveLazyValue(val);
19304 return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, target));19303 return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, target));
19305 } else {19304 } else {
19306 try sema.requireRuntimeBlock(block, src, operand_src);19305 try sema.requireRuntimeBlock(block, src, operand_src);
src/arch/aarch64/CodeGen.zig+1-1
...@@ -531,7 +531,7 @@ pub fn generate(...@@ -531,7 +531,7 @@ pub fn generate(
531531
532 var mir = Mir{532 var mir = Mir{
533 .instructions = function.mir_instructions.toOwnedSlice(),533 .instructions = function.mir_instructions.toOwnedSlice(),
534 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),534 .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator),
535 };535 };
536 defer mir.deinit(bin_file.allocator);536 defer mir.deinit(bin_file.allocator);
537537
src/arch/arm/CodeGen.zig+1-1
...@@ -328,7 +328,7 @@ pub fn generate(...@@ -328,7 +328,7 @@ pub fn generate(
328328
329 var mir = Mir{329 var mir = Mir{
330 .instructions = function.mir_instructions.toOwnedSlice(),330 .instructions = function.mir_instructions.toOwnedSlice(),
331 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),331 .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator),
332 };332 };
333 defer mir.deinit(bin_file.allocator);333 defer mir.deinit(bin_file.allocator);
334334
src/arch/riscv64/CodeGen.zig+1-1
...@@ -291,7 +291,7 @@ pub fn generate(...@@ -291,7 +291,7 @@ pub fn generate(
291291
292 var mir = Mir{292 var mir = Mir{
293 .instructions = function.mir_instructions.toOwnedSlice(),293 .instructions = function.mir_instructions.toOwnedSlice(),
294 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),294 .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator),
295 };295 };
296 defer mir.deinit(bin_file.allocator);296 defer mir.deinit(bin_file.allocator);
297297
src/arch/sparc64/CodeGen.zig+1-1
...@@ -330,7 +330,7 @@ pub fn generate(...@@ -330,7 +330,7 @@ pub fn generate(
330330
331 var mir = Mir{331 var mir = Mir{
332 .instructions = function.mir_instructions.toOwnedSlice(),332 .instructions = function.mir_instructions.toOwnedSlice(),
333 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),333 .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator),
334 };334 };
335 defer mir.deinit(bin_file.allocator);335 defer mir.deinit(bin_file.allocator);
336336
src/arch/wasm/CodeGen.zig+4-4
...@@ -1064,8 +1064,8 @@ fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []cons...@@ -1064,8 +1064,8 @@ fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []cons
1064 }1064 }
10651065
1066 return wasm.Type{1066 return wasm.Type{
1067 .params = temp_params.toOwnedSlice(),1067 .params = try temp_params.toOwnedSlice(),
1068 .returns = returns.toOwnedSlice(),1068 .returns = try returns.toOwnedSlice(),
1069 };1069 };
1070}1070}
10711071
...@@ -1176,7 +1176,7 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1176,7 +1176,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
11761176
1177 var mir: Mir = .{1177 var mir: Mir = .{
1178 .instructions = func.mir_instructions.toOwnedSlice(),1178 .instructions = func.mir_instructions.toOwnedSlice(),
1179 .extra = func.mir_extra.toOwnedSlice(func.gpa),1179 .extra = try func.mir_extra.toOwnedSlice(func.gpa),
1180 };1180 };
1181 defer mir.deinit(func.gpa);1181 defer mir.deinit(func.gpa);
11821182
...@@ -1258,7 +1258,7 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV...@@ -1258,7 +1258,7 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
1258 },1258 },
1259 else => return func.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}),1259 else => return func.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}),
1260 }1260 }
1261 result.args = args.toOwnedSlice();1261 result.args = try args.toOwnedSlice();
1262 return result;1262 return result;
1263}1263}
12641264
src/arch/x86_64/CodeGen.zig+1-1
...@@ -331,7 +331,7 @@ pub fn generate(...@@ -331,7 +331,7 @@ pub fn generate(
331331
332 var mir = Mir{332 var mir = Mir{
333 .instructions = function.mir_instructions.toOwnedSlice(),333 .instructions = function.mir_instructions.toOwnedSlice(),
334 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),334 .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator),
335 };335 };
336 defer mir.deinit(bin_file.allocator);336 defer mir.deinit(bin_file.allocator);
337337
src/codegen/c.zig+12-12
...@@ -1286,7 +1286,7 @@ pub const DeclGen = struct {...@@ -1286,7 +1286,7 @@ pub const DeclGen = struct {
1286 }1286 }
1287 try bw.writeAll(");\n");1287 try bw.writeAll(");\n");
12881288
1289 const rendered = buffer.toOwnedSlice();1289 const rendered = try buffer.toOwnedSlice();
1290 errdefer dg.typedefs.allocator.free(rendered);1290 errdefer dg.typedefs.allocator.free(rendered);
1291 const name = rendered[name_begin..name_end];1291 const name = rendered[name_begin..name_end];
12921292
...@@ -1326,7 +1326,7 @@ pub const DeclGen = struct {...@@ -1326,7 +1326,7 @@ pub const DeclGen = struct {
1326 const name_end = buffer.items.len;1326 const name_end = buffer.items.len;
1327 try bw.writeAll(";\n");1327 try bw.writeAll(";\n");
13281328
1329 const rendered = buffer.toOwnedSlice();1329 const rendered = try buffer.toOwnedSlice();
1330 errdefer dg.typedefs.allocator.free(rendered);1330 errdefer dg.typedefs.allocator.free(rendered);
1331 const name = rendered[name_begin..name_end];1331 const name = rendered[name_begin..name_end];
13321332
...@@ -1369,7 +1369,7 @@ pub const DeclGen = struct {...@@ -1369,7 +1369,7 @@ pub const DeclGen = struct {
1369 buffer.appendSliceAssumeCapacity(buffer.items[name_begin..name_end]);1369 buffer.appendSliceAssumeCapacity(buffer.items[name_begin..name_end]);
1370 buffer.appendSliceAssumeCapacity(";\n");1370 buffer.appendSliceAssumeCapacity(";\n");
13711371
1372 const rendered = buffer.toOwnedSlice();1372 const rendered = try buffer.toOwnedSlice();
1373 errdefer dg.typedefs.allocator.free(rendered);1373 errdefer dg.typedefs.allocator.free(rendered);
1374 const name = rendered[name_begin..name_end];1374 const name = rendered[name_begin..name_end];
13751375
...@@ -1413,7 +1413,7 @@ pub const DeclGen = struct {...@@ -1413,7 +1413,7 @@ pub const DeclGen = struct {
1413 }1413 }
1414 try buffer.appendSlice("};\n");1414 try buffer.appendSlice("};\n");
14151415
1416 const rendered = buffer.toOwnedSlice();1416 const rendered = try buffer.toOwnedSlice();
1417 errdefer dg.typedefs.allocator.free(rendered);1417 errdefer dg.typedefs.allocator.free(rendered);
14181418
1419 try dg.typedefs.ensureUnusedCapacity(1);1419 try dg.typedefs.ensureUnusedCapacity(1);
...@@ -1448,7 +1448,7 @@ pub const DeclGen = struct {...@@ -1448,7 +1448,7 @@ pub const DeclGen = struct {
1448 try buffer.writer().print("}} zig_T_{};\n", .{typeToCIdentifier(t, dg.module)});1448 try buffer.writer().print("}} zig_T_{};\n", .{typeToCIdentifier(t, dg.module)});
1449 const name_end = buffer.items.len - ";\n".len;1449 const name_end = buffer.items.len - ";\n".len;
14501450
1451 const rendered = buffer.toOwnedSlice();1451 const rendered = try buffer.toOwnedSlice();
1452 errdefer dg.typedefs.allocator.free(rendered);1452 errdefer dg.typedefs.allocator.free(rendered);
1453 const name = rendered[name_begin..name_end];1453 const name = rendered[name_begin..name_end];
14541454
...@@ -1510,7 +1510,7 @@ pub const DeclGen = struct {...@@ -1510,7 +1510,7 @@ pub const DeclGen = struct {
1510 if (t.unionTagTypeSafety()) |_| try buffer.appendSlice(" } payload;\n");1510 if (t.unionTagTypeSafety()) |_| try buffer.appendSlice(" } payload;\n");
1511 try buffer.appendSlice("};\n");1511 try buffer.appendSlice("};\n");
15121512
1513 const rendered = buffer.toOwnedSlice();1513 const rendered = try buffer.toOwnedSlice();
1514 errdefer dg.typedefs.allocator.free(rendered);1514 errdefer dg.typedefs.allocator.free(rendered);
15151515
1516 try dg.typedefs.ensureUnusedCapacity(1);1516 try dg.typedefs.ensureUnusedCapacity(1);
...@@ -1553,7 +1553,7 @@ pub const DeclGen = struct {...@@ -1553,7 +1553,7 @@ pub const DeclGen = struct {
1553 const name_end = buffer.items.len;1553 const name_end = buffer.items.len;
1554 try bw.writeAll(";\n");1554 try bw.writeAll(";\n");
15551555
1556 const rendered = buffer.toOwnedSlice();1556 const rendered = try buffer.toOwnedSlice();
1557 errdefer dg.typedefs.allocator.free(rendered);1557 errdefer dg.typedefs.allocator.free(rendered);
1558 const name = rendered[name_begin..name_end];1558 const name = rendered[name_begin..name_end];
15591559
...@@ -1586,7 +1586,7 @@ pub const DeclGen = struct {...@@ -1586,7 +1586,7 @@ pub const DeclGen = struct {
1586 const c_len_val = Value.initPayload(&c_len_pl.base);1586 const c_len_val = Value.initPayload(&c_len_pl.base);
1587 try bw.print("[{}];\n", .{try dg.fmtIntLiteral(Type.usize, c_len_val)});1587 try bw.print("[{}];\n", .{try dg.fmtIntLiteral(Type.usize, c_len_val)});
15881588
1589 const rendered = buffer.toOwnedSlice();1589 const rendered = try buffer.toOwnedSlice();
1590 errdefer dg.typedefs.allocator.free(rendered);1590 errdefer dg.typedefs.allocator.free(rendered);
1591 const name = rendered[name_begin..name_end];1591 const name = rendered[name_begin..name_end];
15921592
...@@ -1614,7 +1614,7 @@ pub const DeclGen = struct {...@@ -1614,7 +1614,7 @@ pub const DeclGen = struct {
1614 const name_end = buffer.items.len;1614 const name_end = buffer.items.len;
1615 try bw.writeAll(";\n");1615 try bw.writeAll(";\n");
16161616
1617 const rendered = buffer.toOwnedSlice();1617 const rendered = try buffer.toOwnedSlice();
1618 errdefer dg.typedefs.allocator.free(rendered);1618 errdefer dg.typedefs.allocator.free(rendered);
1619 const name = rendered[name_begin..name_end];1619 const name = rendered[name_begin..name_end];
16201620
...@@ -1643,7 +1643,7 @@ pub const DeclGen = struct {...@@ -1643,7 +1643,7 @@ pub const DeclGen = struct {
1643 const name_end = buffer.items.len;1643 const name_end = buffer.items.len;
1644 try buffer.appendSlice(";\n");1644 try buffer.appendSlice(";\n");
16451645
1646 const rendered = buffer.toOwnedSlice();1646 const rendered = try buffer.toOwnedSlice();
1647 errdefer dg.typedefs.allocator.free(rendered);1647 errdefer dg.typedefs.allocator.free(rendered);
1648 const name = rendered[name_begin..name_end];1648 const name = rendered[name_begin..name_end];
16491649
...@@ -2006,7 +2006,7 @@ pub const DeclGen = struct {...@@ -2006,7 +2006,7 @@ pub const DeclGen = struct {
2006 _ = try airBreakpoint(bw);2006 _ = try airBreakpoint(bw);
2007 try buffer.appendSlice("}\n");2007 try buffer.appendSlice("}\n");
20082008
2009 const rendered = buffer.toOwnedSlice();2009 const rendered = try buffer.toOwnedSlice();
2010 errdefer dg.typedefs.allocator.free(rendered);2010 errdefer dg.typedefs.allocator.free(rendered);
2011 const name = rendered[name_begin..name_end];2011 const name = rendered[name_begin..name_end];
20122012
...@@ -2108,7 +2108,7 @@ pub const DeclGen = struct {...@@ -2108,7 +2108,7 @@ pub const DeclGen = struct {
2108 dg.module.markDeclAlive(decl);2108 dg.module.markDeclAlive(decl);
21092109
2110 if (dg.module.decl_exports.get(decl_index)) |exports| {2110 if (dg.module.decl_exports.get(decl_index)) |exports| {
2111 return writer.writeAll(exports[0].options.name);2111 return writer.writeAll(exports.items[0].options.name);
2112 } else if (decl.isExtern()) {2112 } else if (decl.isExtern()) {
2113 return writer.writeAll(mem.sliceTo(decl.name, 0));2113 return writer.writeAll(mem.sliceTo(decl.name, 0));
2114 } else {2114 } else {
src/codegen/llvm.zig+3-5
...@@ -693,7 +693,7 @@ pub const Object = struct {...@@ -693,7 +693,7 @@ pub const Object = struct {
693 for (mod.decl_exports.values()) |export_list, i| {693 for (mod.decl_exports.values()) |export_list, i| {
694 const decl_index = export_keys[i];694 const decl_index = export_keys[i];
695 const llvm_global = object.decl_map.get(decl_index) orelse continue;695 const llvm_global = object.decl_map.get(decl_index) orelse continue;
696 for (export_list) |exp| {696 for (export_list.items) |exp| {
697 // Detect if the LLVM global has already been created as an extern. In such697 // Detect if the LLVM global has already been created as an extern. In such
698 // case, we need to replace all uses of it with this exported global.698 // case, we need to replace all uses of it with this exported global.
699 // TODO update std.builtin.ExportOptions to have the name be a699 // TODO update std.builtin.ExportOptions to have the name be a
...@@ -1215,8 +1215,7 @@ pub const Object = struct {...@@ -1215,8 +1215,7 @@ pub const Object = struct {
1215 else => |e| return e,1215 else => |e| return e,
1216 };1216 };
12171217
1218 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};1218 try o.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
1219 try o.updateDeclExports(module, decl_index, decl_exports);
1220 }1219 }
12211220
1222 pub fn updateDecl(self: *Object, module: *Module, decl_index: Module.Decl.Index) !void {1221 pub fn updateDecl(self: *Object, module: *Module, decl_index: Module.Decl.Index) !void {
...@@ -1239,8 +1238,7 @@ pub const Object = struct {...@@ -1239,8 +1238,7 @@ pub const Object = struct {
1239 },1238 },
1240 else => |e| return e,1239 else => |e| return e,
1241 };1240 };
1242 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};1241 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
1243 try self.updateDeclExports(module, decl_index, decl_exports);
1244 }1242 }
12451243
1246 /// TODO replace this with a call to `Module::getNamedValue`. This will require adding1244 /// TODO replace this with a call to `Module::getNamedValue`. This will require adding
src/libc_installation.zig+3-3
...@@ -387,7 +387,7 @@ pub const LibCInstallation = struct {...@@ -387,7 +387,7 @@ pub const LibCInstallation = struct {
387 else => return error.FileSystem,387 else => return error.FileSystem,
388 };388 };
389389
390 self.include_dir = result_buf.toOwnedSlice();390 self.include_dir = try result_buf.toOwnedSlice();
391 return;391 return;
392 }392 }
393393
...@@ -434,7 +434,7 @@ pub const LibCInstallation = struct {...@@ -434,7 +434,7 @@ pub const LibCInstallation = struct {
434 else => return error.FileSystem,434 else => return error.FileSystem,
435 };435 };
436436
437 self.crt_dir = result_buf.toOwnedSlice();437 self.crt_dir = try result_buf.toOwnedSlice();
438 return;438 return;
439 }439 }
440 return error.LibCRuntimeNotFound;440 return error.LibCRuntimeNotFound;
...@@ -499,7 +499,7 @@ pub const LibCInstallation = struct {...@@ -499,7 +499,7 @@ pub const LibCInstallation = struct {
499 else => return error.FileSystem,499 else => return error.FileSystem,
500 };500 };
501501
502 self.kernel32_lib_dir = result_buf.toOwnedSlice();502 self.kernel32_lib_dir = try result_buf.toOwnedSlice();
503 return;503 return;
504 }504 }
505 return error.LibCKernel32LibNotFound;505 return error.LibCKernel32LibNotFound;
src/link/Coff.zig+6-6
...@@ -938,9 +938,9 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live...@@ -938,9 +938,9 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
938938
939 try self.updateDeclCode(decl_index, code, .FUNCTION);939 try self.updateDeclCode(decl_index, code, .FUNCTION);
940940
941 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.941 // Since we updated the vaddr and the size, each corresponding export
942 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};942 // symbol also needs to be updated.
943 return self.updateDeclExports(module, decl_index, decl_exports);943 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
944}944}
945945
946pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {946pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
...@@ -1053,9 +1053,9 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -1053,9 +1053,9 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
10531053
1054 try self.updateDeclCode(decl_index, code, .NULL);1054 try self.updateDeclCode(decl_index, code, .NULL);
10551055
1056 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.1056 // Since we updated the vaddr and the size, each corresponding export
1057 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};1057 // symbol also needs to be updated.
1058 return self.updateDeclExports(module, decl_index, decl_exports);1058 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
1059}1059}
10601060
1061fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {1061fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {
src/link/Elf.zig+6-6
...@@ -2450,9 +2450,9 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven...@@ -2450,9 +2450,9 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
2450 );2450 );
2451 }2451 }
24522452
2453 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.2453 // Since we updated the vaddr and the size, each corresponding export
2454 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};2454 // symbol also needs to be updated.
2455 return self.updateDeclExports(module, decl_index, decl_exports);2455 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
2456}2456}
24572457
2458pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !void {2458pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !void {
...@@ -2527,9 +2527,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v...@@ -2527,9 +2527,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
2527 );2527 );
2528 }2528 }
25292529
2530 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.2530 // Since we updated the vaddr and the size, each corresponding export
2531 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};2531 // symbol also needs to be updated.
2532 return self.updateDeclExports(module, decl_index, decl_exports);2532 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
2533}2533}
25342534
2535pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {2535pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
src/link/MachO.zig+2-4
...@@ -2225,8 +2225,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -2225,8 +2225,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
22252225
2226 // Since we updated the vaddr and the size, each corresponding export symbol also2226 // Since we updated the vaddr and the size, each corresponding export symbol also
2227 // needs to be updated.2227 // needs to be updated.
2228 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};2228 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
2229 try self.updateDeclExports(module, decl_index, decl_exports);
2230}2229}
22312230
2232pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {2231pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
...@@ -2377,8 +2376,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2377,8 +2376,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
23772376
2378 // Since we updated the vaddr and the size, each corresponding export symbol also2377 // Since we updated the vaddr and the size, each corresponding export symbol also
2379 // needs to be updated.2378 // needs to be updated.
2380 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};2379 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
2381 try self.updateDeclExports(module, decl_index, decl_exports);
2382}2380}
23832381
2384fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {2382fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {
src/link/MachO/Trie.zig+1-1
...@@ -165,7 +165,7 @@ pub const Node = struct {...@@ -165,7 +165,7 @@ pub const Node = struct {
165 break;165 break;
166 try label_buf.append(next);166 try label_buf.append(next);
167 }167 }
168 break :blk label_buf.toOwnedSlice();168 break :blk try label_buf.toOwnedSlice();
169 };169 };
170170
171 const seek_to = try leb.readULEB128(u64, reader);171 const seek_to = try leb.readULEB128(u64, reader);
src/link/Plan9.zig+8-8
...@@ -230,7 +230,7 @@ fn putFn(self: *Plan9, decl_index: Module.Decl.Index, out: FnDeclOutput) !void {...@@ -230,7 +230,7 @@ fn putFn(self: *Plan9, decl_index: Module.Decl.Index, out: FnDeclOutput) !void {
230230
231 // null terminate231 // null terminate
232 try a.append(0);232 try a.append(0);
233 const final = a.toOwnedSlice();233 const final = try a.toOwnedSlice();
234 self.syms.items[fn_map_res.value_ptr.sym_index - 1] = .{234 self.syms.items[fn_map_res.value_ptr.sym_index - 1] = .{
235 .type = .z,235 .type = .z,
236 .value = 1,236 .value = 1,
...@@ -296,7 +296,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -296,7 +296,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
296 },296 },
297 );297 );
298 const code = switch (res) {298 const code = switch (res) {
299 .appended => code_buffer.toOwnedSlice(),299 .appended => try code_buffer.toOwnedSlice(),
300 .fail => |em| {300 .fail => |em| {
301 decl.analysis = .codegen_failure;301 decl.analysis = .codegen_failure;
302 try module.failed_decls.put(module.gpa, decl_index, em);302 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -305,7 +305,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -305,7 +305,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
305 };305 };
306 const out: FnDeclOutput = .{306 const out: FnDeclOutput = .{
307 .code = code,307 .code = code,
308 .lineinfo = dbg_line_buffer.toOwnedSlice(),308 .lineinfo = try dbg_line_buffer.toOwnedSlice(),
309 .start_line = start_line.?,309 .start_line = start_line.?,
310 .end_line = end_line,310 .end_line = end_line,
311 };311 };
...@@ -574,7 +574,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -574,7 +574,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
574 }574 }
575 self.syms.items[decl.link.plan9.sym_index.?].value = off;575 self.syms.items[decl.link.plan9.sym_index.?].value = off;
576 if (mod.decl_exports.get(decl_index)) |exports| {576 if (mod.decl_exports.get(decl_index)) |exports| {
577 try self.addDeclExports(mod, decl, exports);577 try self.addDeclExports(mod, decl, exports.items);
578 }578 }
579 }579 }
580 }580 }
...@@ -611,7 +611,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -611,7 +611,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
611 }611 }
612 self.syms.items[decl.link.plan9.sym_index.?].value = off;612 self.syms.items[decl.link.plan9.sym_index.?].value = off;
613 if (mod.decl_exports.get(decl_index)) |exports| {613 if (mod.decl_exports.get(decl_index)) |exports| {
614 try self.addDeclExports(mod, decl, exports);614 try self.addDeclExports(mod, decl, exports.items);
615 }615 }
616 }616 }
617 // write the unnamed constants after the other data decls617 // write the unnamed constants after the other data decls
...@@ -641,7 +641,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -641,7 +641,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
641 self.syms.items[1].value = self.getAddr(0x0, .b);641 self.syms.items[1].value = self.getAddr(0x0, .b);
642 var sym_buf = std.ArrayList(u8).init(self.base.allocator);642 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
643 try self.writeSyms(&sym_buf);643 try self.writeSyms(&sym_buf);
644 const syms = sym_buf.toOwnedSlice();644 const syms = try sym_buf.toOwnedSlice();
645 defer self.base.allocator.free(syms);645 defer self.base.allocator.free(syms);
646 assert(2 + self.atomCount() == iovecs_i); // we didn't write all the decls646 assert(2 + self.atomCount() == iovecs_i); // we didn't write all the decls
647 iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len };647 iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len };
...@@ -914,7 +914,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -914,7 +914,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
914 const sym = self.syms.items[decl.link.plan9.sym_index.?];914 const sym = self.syms.items[decl.link.plan9.sym_index.?];
915 try self.writeSym(writer, sym);915 try self.writeSym(writer, sym);
916 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {916 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
917 for (exports) |e| {917 for (exports.items) |e| {
918 try self.writeSym(writer, self.syms.items[e.link.plan9.?]);918 try self.writeSym(writer, self.syms.items[e.link.plan9.?]);
919 }919 }
920 }920 }
...@@ -939,7 +939,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -939,7 +939,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
939 const sym = self.syms.items[decl.link.plan9.sym_index.?];939 const sym = self.syms.items[decl.link.plan9.sym_index.?];
940 try self.writeSym(writer, sym);940 try self.writeSym(writer, sym);
941 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {941 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
942 for (exports) |e| {942 for (exports.items) |e| {
943 const s = self.syms.items[e.link.plan9.?];943 const s = self.syms.items[e.link.plan9.?];
944 if (mem.eql(u8, s.name, "_start"))944 if (mem.eql(u8, s.name, "_start"))
945 self.entry_val = s.value;945 self.entry_val = s.value;
src/link/Wasm.zig+1-1
...@@ -3206,7 +3206,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !...@@ -3206,7 +3206,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
3206 const skip_export_non_fn = target.os.tag == .wasi and3206 const skip_export_non_fn = target.os.tag == .wasi and
3207 wasm.base.options.wasi_exec_model == .command;3207 wasm.base.options.wasi_exec_model == .command;
3208 for (mod.decl_exports.values()) |exports| {3208 for (mod.decl_exports.values()) |exports| {
3209 for (exports) |exprt| {3209 for (exports.items) |exprt| {
3210 const exported_decl = mod.declPtr(exprt.exported_decl);3210 const exported_decl = mod.declPtr(exprt.exported_decl);
3211 if (skip_export_non_fn and exported_decl.ty.zigTypeTag() != .Fn) {3211 if (skip_export_non_fn and exported_decl.ty.zigTypeTag() != .Fn) {
3212 // skip exporting symbols when we're building a WASI command3212 // skip exporting symbols when we're building a WASI command
src/link/Wasm/Object.zig+2-2
...@@ -557,7 +557,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -557,7 +557,7 @@ fn Parser(comptime ReaderType: type) type {
557 error.EndOfStream => {}, // finished parsing the file557 error.EndOfStream => {}, // finished parsing the file
558 else => |e| return e,558 else => |e| return e,
559 }559 }
560 parser.object.relocatable_data = relocatable_data.toOwnedSlice();560 parser.object.relocatable_data = try relocatable_data.toOwnedSlice();
561 }561 }
562562
563 /// Based on the "features" custom section, parses it into a list of563 /// Based on the "features" custom section, parses it into a list of
...@@ -742,7 +742,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -742,7 +742,7 @@ fn Parser(comptime ReaderType: type) type {
742 log.debug("Found legacy indirect function table. Created symbol", .{});742 log.debug("Found legacy indirect function table. Created symbol", .{});
743 }743 }
744744
745 parser.object.symtable = symbols.toOwnedSlice();745 parser.object.symtable = try symbols.toOwnedSlice();
746 },746 },
747 }747 }
748 }748 }
src/link/tapi/parse.zig+1-1
...@@ -262,7 +262,7 @@ pub const Tree = struct {...@@ -262,7 +262,7 @@ pub const Tree = struct {
262 }262 }
263263
264 self.source = source;264 self.source = source;
265 self.tokens = tokens.toOwnedSlice();265 self.tokens = try tokens.toOwnedSlice();
266266
267 var it = TokenIterator{ .buffer = self.tokens };267 var it = TokenIterator{ .buffer = self.tokens };
268 var parser = Parser{268 var parser = Parser{
src/link/tapi/yaml.zig+1-1
...@@ -193,7 +193,7 @@ pub const Value = union(ValueType) {...@@ -193,7 +193,7 @@ pub const Value = union(ValueType) {
193 }193 }
194 }194 }
195195
196 return Value{ .list = out_list.toOwnedSlice() };196 return Value{ .list = try out_list.toOwnedSlice() };
197 } else if (node.cast(Node.Value)) |value| {197 } else if (node.cast(Node.Value)) |value| {
198 const start = tree.tokens[value.start.?];198 const start = tree.tokens[value.start.?];
199 const end = tree.tokens[value.end.?];199 const end = tree.tokens[value.end.?];
src/main.zig+1-1
...@@ -4803,7 +4803,7 @@ pub const ClangArgIterator = struct {...@@ -4803,7 +4803,7 @@ pub const ClangArgIterator = struct {
4803 };4803 };
4804 self.root_args = args;4804 self.root_args = args;
4805 }4805 }
4806 const resp_arg_slice = resp_arg_list.toOwnedSlice();4806 const resp_arg_slice = try resp_arg_list.toOwnedSlice();
4807 self.next_index = 0;4807 self.next_index = 0;
4808 self.argv = resp_arg_slice;4808 self.argv = resp_arg_slice;
48094809
src/test.zig+3-3
...@@ -338,7 +338,7 @@ const TestManifest = struct {...@@ -338,7 +338,7 @@ const TestManifest = struct {
338 while (try it.next()) |item| {338 while (try it.next()) |item| {
339 try out.append(item);339 try out.append(item);
340 }340 }
341 return out.toOwnedSlice();341 return try out.toOwnedSlice();
342 }342 }
343343
344 fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T {344 fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T {
...@@ -361,7 +361,7 @@ const TestManifest = struct {...@@ -361,7 +361,7 @@ const TestManifest = struct {
361 while (it.next()) |line| {361 while (it.next()) |line| {
362 try out.append(line);362 try out.append(line);
363 }363 }
364 return out.toOwnedSlice();364 return try out.toOwnedSlice();
365 }365 }
366366
367 fn ParseFn(comptime T: type) type {367 fn ParseFn(comptime T: type) type {
...@@ -1179,7 +1179,7 @@ pub const TestContext = struct {...@@ -1179,7 +1179,7 @@ pub const TestContext = struct {
1179 if (output.items.len > 0) {1179 if (output.items.len > 0) {
1180 try output.resize(output.items.len - 1);1180 try output.resize(output.items.len - 1);
1181 }1181 }
1182 case.addCompareOutput(src, output.toOwnedSlice());1182 case.addCompareOutput(src, try output.toOwnedSlice());
1183 },1183 },
1184 .cli => @panic("TODO cli tests"),1184 .cli => @panic("TODO cli tests"),
1185 }1185 }
src/translate_c/ast.zig+1-1
...@@ -788,7 +788,7 @@ pub fn render(gpa: Allocator, zig_is_stage1: bool, nodes: []const Node) !std.zig...@@ -788,7 +788,7 @@ pub fn render(gpa: Allocator, zig_is_stage1: bool, nodes: []const Node) !std.zig
788 .source = try ctx.buf.toOwnedSliceSentinel(0),788 .source = try ctx.buf.toOwnedSliceSentinel(0),
789 .tokens = ctx.tokens.toOwnedSlice(),789 .tokens = ctx.tokens.toOwnedSlice(),
790 .nodes = ctx.nodes.toOwnedSlice(),790 .nodes = ctx.nodes.toOwnedSlice(),
791 .extra_data = ctx.extra_data.toOwnedSlice(gpa),791 .extra_data = try ctx.extra_data.toOwnedSlice(gpa),
792 .errors = &.{},792 .errors = &.{},
793 };793 };
794}794}
src/value.zig+14-24
...@@ -1677,22 +1677,8 @@ pub const Value = extern union {...@@ -1677,22 +1677,8 @@ pub const Value = extern union {
1677 @panic("TODO implement i64 Value clz");1677 @panic("TODO implement i64 Value clz");
1678 },1678 },
1679 .int_big_positive => {1679 .int_big_positive => {
1680 // TODO: move this code into std lib big ints
1681 const bigint = val.castTag(.int_big_positive).?.asBigInt();1680 const bigint = val.castTag(.int_big_positive).?.asBigInt();
1682 // Limbs are stored in little-endian order but we need1681 return bigint.clz(ty_bits);
1683 // to iterate big-endian.
1684 var total_limb_lz: u64 = 0;
1685 var i: usize = bigint.limbs.len;
1686 const bits_per_limb = @sizeOf(std.math.big.Limb) * 8;
1687 while (i != 0) {
1688 i -= 1;
1689 const limb = bigint.limbs[i];
1690 const this_limb_lz = @clz(limb);
1691 total_limb_lz += this_limb_lz;
1692 if (this_limb_lz != bits_per_limb) break;
1693 }
1694 const total_limb_bits = bigint.limbs.len * bits_per_limb;
1695 return total_limb_lz + ty_bits - total_limb_bits;
1696 },1682 },
1697 .int_big_negative => {1683 .int_big_negative => {
1698 @panic("TODO implement int_big_negative Value clz");1684 @panic("TODO implement int_big_negative Value clz");
...@@ -1703,6 +1689,12 @@ pub const Value = extern union {...@@ -1703,6 +1689,12 @@ pub const Value = extern union {
1703 return ty_bits;1689 return ty_bits;
1704 },1690 },
17051691
1692 .lazy_align, .lazy_size => {
1693 var bigint_buf: BigIntSpace = undefined;
1694 const bigint = val.toBigIntAdvanced(&bigint_buf, target, null) catch unreachable;
1695 return bigint.clz(ty_bits);
1696 },
1697
1706 else => unreachable,1698 else => unreachable,
1707 }1699 }
1708 }1700 }
...@@ -1721,16 +1713,8 @@ pub const Value = extern union {...@@ -1721,16 +1713,8 @@ pub const Value = extern union {
1721 @panic("TODO implement i64 Value ctz");1713 @panic("TODO implement i64 Value ctz");
1722 },1714 },
1723 .int_big_positive => {1715 .int_big_positive => {
1724 // TODO: move this code into std lib big ints
1725 const bigint = val.castTag(.int_big_positive).?.asBigInt();1716 const bigint = val.castTag(.int_big_positive).?.asBigInt();
1726 // Limbs are stored in little-endian order.1717 return bigint.ctz();
1727 var result: u64 = 0;
1728 for (bigint.limbs) |limb| {
1729 const limb_tz = @ctz(limb);
1730 result += limb_tz;
1731 if (limb_tz != @sizeOf(std.math.big.Limb) * 8) break;
1732 }
1733 return result;
1734 },1718 },
1735 .int_big_negative => {1719 .int_big_negative => {
1736 @panic("TODO implement int_big_negative Value ctz");1720 @panic("TODO implement int_big_negative Value ctz");
...@@ -1741,6 +1725,12 @@ pub const Value = extern union {...@@ -1741,6 +1725,12 @@ pub const Value = extern union {
1741 return ty_bits;1725 return ty_bits;
1742 },1726 },
17431727
1728 .lazy_align, .lazy_size => {
1729 var bigint_buf: BigIntSpace = undefined;
1730 const bigint = val.toBigIntAdvanced(&bigint_buf, target, null) catch unreachable;
1731 return bigint.ctz();
1732 },
1733
1744 else => unreachable,1734 else => unreachable,
1745 }1735 }
1746 }1736 }
test/cases/compile_errors/dereference_anyopaque.zig+6-6
...@@ -47,9 +47,9 @@ pub export fn entry() void {...@@ -47,9 +47,9 @@ pub export fn entry() void {
47// :11:22: error: comparison of 'void' with null47// :11:22: error: comparison of 'void' with null
48// :25:51: error: values of type 'anyopaque' must be comptime-known, but operand value is runtime-known48// :25:51: error: values of type 'anyopaque' must be comptime-known, but operand value is runtime-known
49// :25:51: note: opaque type 'anyopaque' has undefined size49// :25:51: note: opaque type 'anyopaque' has undefined size
50// :25:51: error: values of type 'fn(*anyopaque, usize, u29, u29, usize) error{OutOfMemory}![]u8' must be comptime-known, but operand value is runtime-known50// :25:51: error: values of type 'fn(*anyopaque, usize, u8, usize) ?[*]u8' must be comptime-known, but operand value is runtime-known
51// :25:51: note: use '*const fn(*anyopaque, usize, u29, u29, usize) error{OutOfMemory}![]u8' for a function pointer type51// :25:51: note: use '*const fn(*anyopaque, usize, u8, usize) ?[*]u8' for a function pointer type
52// :25:51: error: values of type 'fn(*anyopaque, []u8, u29, usize, u29, usize) ?usize' must be comptime-known, but operand value is runtime-known52// :25:51: error: values of type 'fn(*anyopaque, []u8, u8, usize, usize) bool' must be comptime-known, but operand value is runtime-known
53// :25:51: note: use '*const fn(*anyopaque, []u8, u29, usize, u29, usize) ?usize' for a function pointer type53// :25:51: note: use '*const fn(*anyopaque, []u8, u8, usize, usize) bool' for a function pointer type
54// :25:51: error: values of type 'fn(*anyopaque, []u8, u29, usize) void' must be comptime-known, but operand value is runtime-known54// :25:51: error: values of type 'fn(*anyopaque, []u8, u8, usize) void' must be comptime-known, but operand value is runtime-known
55// :25:51: note: use '*const fn(*anyopaque, []u8, u29, usize) void' for a function pointer type55// :25:51: note: use '*const fn(*anyopaque, []u8, u8, usize) void' for a function pointer type
test/compare_output.zig+6-5
...@@ -504,9 +504,10 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -504,9 +504,10 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
504 \\ const allocator = logging_allocator.allocator();504 \\ const allocator = logging_allocator.allocator();
505 \\505 \\
506 \\ var a = try allocator.alloc(u8, 10);506 \\ var a = try allocator.alloc(u8, 10);
507 \\ a = allocator.shrink(a, 5);507 \\ try std.testing.expect(allocator.resize(a, 5));
508 \\ a = a[0..5];
508 \\ try std.testing.expect(a.len == 5);509 \\ try std.testing.expect(a.len == 5);
509 \\ try std.testing.expect(allocator.resize(a, 20) == null);510 \\ try std.testing.expect(!allocator.resize(a, 20));
510 \\ allocator.free(a);511 \\ allocator.free(a);
511 \\}512 \\}
512 \\513 \\
...@@ -522,9 +523,9 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -522,9 +523,9 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
522 \\ nosuspend stdout.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;523 \\ nosuspend stdout.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
523 \\}524 \\}
524 ,525 ,
525 \\debug: alloc - success - len: 10, ptr_align: 1, len_align: 0526 \\debug: alloc - success - len: 10, ptr_align: 0
526 \\debug: shrink - success - 10 to 5, len_align: 0, buf_align: 1527 \\debug: shrink - success - 10 to 5, buf_align: 0
527 \\error: expand - failure - 5 to 20, len_align: 0, buf_align: 1528 \\error: expand - failure - 5 to 20, buf_align: 0
528 \\debug: free - len: 5529 \\debug: free - len: 5
529 \\530 \\
530 );531 );
test/tests.zig+1-1
...@@ -992,7 +992,7 @@ pub const StackTracesContext = struct {...@@ -992,7 +992,7 @@ pub const StackTracesContext = struct {
992 }992 }
993 try buf.appendSlice("\n");993 try buf.appendSlice("\n");
994 }994 }
995 break :got_result buf.toOwnedSlice();995 break :got_result try buf.toOwnedSlice();
996 };996 };
997997
998 if (!mem.eql(u8, self.expect_output, got)) {998 if (!mem.eql(u8, self.expect_output, got)) {