diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 746fc74bd0c0f78d459619cdbff2f9aca3ba3a98..2abe00c7a07cb7469cce47cd858a72eb2d3aaa51 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -147,12 +147,12 @@ const CAllocator = struct { return @as(*[*]u8, @ptrFromInt(@intFromPtr(ptr) - @sizeOf(usize))); } - fn alignedAlloc(len: usize, log2_align: u8) ?[*]u8 { - const alignment = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_align)); + fn alignedAlloc(len: usize, alignment: mem.Alignment) ?[*]u8 { + const alignment_bytes = alignment.toByteUnits(); if (supports_posix_memalign) { // The posix_memalign only accepts alignment values that are a // multiple of the pointer size - const eff_alignment = @max(alignment, @sizeOf(usize)); + const eff_alignment = @max(alignment_bytes, @sizeOf(usize)); var aligned_ptr: ?*anyopaque = undefined; if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0) @@ -164,9 +164,9 @@ const CAllocator = struct { // Thin wrapper around regular malloc, overallocate to account for // alignment padding and store the original malloc()'ed pointer before // the aligned address. - const unaligned_ptr = @as([*]u8, @ptrCast(c.malloc(len + alignment - 1 + @sizeOf(usize)) orelse return null)); + const unaligned_ptr = @as([*]u8, @ptrCast(c.malloc(len + alignment_bytes - 1 + @sizeOf(usize)) orelse return null)); const unaligned_addr = @intFromPtr(unaligned_ptr); - const aligned_addr = mem.alignForward(usize, unaligned_addr + @sizeOf(usize), alignment); + const aligned_addr = mem.alignForward(usize, unaligned_addr + @sizeOf(usize), alignment_bytes); const aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr); getHeader(aligned_ptr).* = unaligned_ptr; @@ -195,22 +195,22 @@ const CAllocator = struct { fn alloc( _: *anyopaque, len: usize, - log2_align: u8, + alignment: mem.Alignment, return_address: usize, ) ?[*]u8 { _ = return_address; assert(len > 0); - return alignedAlloc(len, log2_align); + return alignedAlloc(len, alignment); } fn resize( _: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: mem.Alignment, new_len: usize, return_address: usize, ) bool { - _ = log2_buf_align; + _ = alignment; _ = return_address; if (new_len <= buf.len) { return true; @@ -227,10 +227,10 @@ const CAllocator = struct { fn free( _: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: mem.Alignment, return_address: usize, ) void { - _ = log2_buf_align; + _ = alignment; _ = return_address; alignedFree(buf.ptr); } @@ -267,28 +267,28 @@ const raw_c_allocator_vtable = Allocator.VTable{ fn rawCAlloc( _: *anyopaque, len: usize, - log2_ptr_align: u8, + alignment: mem.Alignment, ret_addr: usize, ) ?[*]u8 { _ = ret_addr; - assert(log2_ptr_align <= comptime std.math.log2_int(usize, @alignOf(std.c.max_align_t))); + assert(alignment.order(.le, comptime .fromByteUnits(@alignOf(std.c.max_align_t)))); // Note that this pointer cannot be aligncasted to max_align_t because if // len is < max_align_t then the alignment can be smaller. For example, if // max_align_t is 16, but the user requests 8 bytes, there is no built-in // type in C that is size 8 and has 16 byte alignment, so the alignment may // be 8 bytes rather than 16. Similarly if only 1 byte is requested, malloc // is allowed to return a 1-byte aligned pointer. - return @as(?[*]u8, @ptrCast(c.malloc(len))); + return @ptrCast(c.malloc(len)); } fn rawCResize( _: *anyopaque, buf: []u8, - log2_old_align: u8, + alignment: mem.Alignment, new_len: usize, ret_addr: usize, ) bool { - _ = log2_old_align; + _ = alignment; _ = ret_addr; if (new_len <= buf.len) @@ -305,10 +305,10 @@ fn rawCResize( fn rawCFree( _: *anyopaque, buf: []u8, - log2_old_align: u8, + alignment: mem.Alignment, ret_addr: usize, ) void { - _ = log2_old_align; + _ = alignment; _ = ret_addr; c.free(buf.ptr); } @@ -380,13 +380,13 @@ pub const HeapAllocator = switch (builtin.os.tag) { fn alloc( ctx: *anyopaque, n: usize, - log2_ptr_align: u8, + alignment: mem.Alignment, return_address: usize, ) ?[*]u8 { _ = return_address; const self: *HeapAllocator = @ptrCast(@alignCast(ctx)); - const ptr_align = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_ptr_align)); + const ptr_align = alignment.toByteUnits(); const amt = n + ptr_align - 1 + @sizeOf(usize); const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, .seq_cst); const heap_handle = optional_heap_handle orelse blk: { @@ -407,11 +407,11 @@ pub const HeapAllocator = switch (builtin.os.tag) { fn resize( ctx: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: mem.Alignment, new_size: usize, return_address: usize, ) bool { - _ = log2_buf_align; + _ = alignment; _ = return_address; const self: *HeapAllocator = @ptrCast(@alignCast(ctx)); @@ -432,10 +432,10 @@ pub const HeapAllocator = switch (builtin.os.tag) { fn free( ctx: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: mem.Alignment, return_address: usize, ) void { - _ = log2_buf_align; + _ = alignment; _ = return_address; const self: *HeapAllocator = @ptrCast(@alignCast(ctx)); windows.HeapFree(self.heap_handle.?, 0, @as(*anyopaque, @ptrFromInt(getRecordPtr(buf).*))); @@ -482,6 +482,7 @@ pub fn StackFallbackAllocator(comptime size: usize) type { .vtable = &.{ .alloc = alloc, .resize = resize, + .remap = remap, .free = free, }, }; @@ -496,40 +497,55 @@ pub fn StackFallbackAllocator(comptime size: usize) type { fn alloc( ctx: *anyopaque, len: usize, - log2_ptr_align: u8, + alignment: mem.Alignment, ra: usize, ) ?[*]u8 { const self: *Self = @ptrCast(@alignCast(ctx)); - return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, log2_ptr_align, ra) orelse - return self.fallback_allocator.rawAlloc(len, log2_ptr_align, ra); + return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, alignment, ra) orelse + return self.fallback_allocator.rawAlloc(len, alignment, ra); } fn resize( ctx: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: mem.Alignment, new_len: usize, ra: usize, ) bool { const self: *Self = @ptrCast(@alignCast(ctx)); if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) { - return FixedBufferAllocator.resize(&self.fixed_buffer_allocator, buf, log2_buf_align, new_len, ra); + return FixedBufferAllocator.resize(&self.fixed_buffer_allocator, buf, alignment, new_len, ra); } else { - return self.fallback_allocator.rawResize(buf, log2_buf_align, new_len, ra); + return self.fallback_allocator.rawResize(buf, alignment, new_len, ra); + } + } + + fn remap( + context: *anyopaque, + memory: []u8, + alignment: mem.Alignment, + new_len: usize, + return_address: usize, + ) ?[*]u8 { + const self: *Self = @ptrCast(@alignCast(context)); + if (self.fixed_buffer_allocator.ownsPtr(memory.ptr)) { + return FixedBufferAllocator.remap(&self.fixed_buffer_allocator, memory, alignment, new_len, return_address); + } else { + return self.fallback_allocator.rawRemap(memory, alignment, new_len, return_address); } } fn free( ctx: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: mem.Alignment, ra: usize, ) void { const self: *Self = @ptrCast(@alignCast(ctx)); if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) { - return FixedBufferAllocator.free(&self.fixed_buffer_allocator, buf, log2_buf_align, ra); + return FixedBufferAllocator.free(&self.fixed_buffer_allocator, buf, alignment, ra); } else { - return self.fallback_allocator.rawFree(buf, log2_buf_align, ra); + return self.fallback_allocator.rawFree(buf, alignment, ra); } } }; diff --git a/lib/std/heap/log_to_writer_allocator.zig b/lib/std/heap/log_to_writer_allocator.zig index b5c86c9bebbfd6e7c3725d40e02aa95d6f363e57..239a580f9bae01625d43e32b3462e2cf94d4f171 100644 --- a/lib/std/heap/log_to_writer_allocator.zig +++ b/lib/std/heap/log_to_writer_allocator.zig @@ -23,6 +23,7 @@ pub fn LogToWriterAllocator(comptime Writer: type) type { .vtable = &.{ .alloc = alloc, .resize = resize, + .remap = remap, .free = free, }, }; @@ -31,12 +32,12 @@ pub fn LogToWriterAllocator(comptime Writer: type) type { fn alloc( ctx: *anyopaque, len: usize, - log2_ptr_align: u8, + alignment: std.mem.Alignment, ra: usize, ) ?[*]u8 { const self: *Self = @ptrCast(@alignCast(ctx)); self.writer.print("alloc : {}", .{len}) catch {}; - const result = self.parent_allocator.rawAlloc(len, log2_ptr_align, ra); + const result = self.parent_allocator.rawAlloc(len, alignment, ra); if (result != null) { self.writer.print(" success!\n", .{}) catch {}; } else { @@ -48,7 +49,7 @@ pub fn LogToWriterAllocator(comptime Writer: type) type { fn resize( ctx: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: std.mem.Alignment, new_len: usize, ra: usize, ) bool { @@ -59,7 +60,7 @@ pub fn LogToWriterAllocator(comptime Writer: type) type { self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {}; } - if (self.parent_allocator.rawResize(buf, log2_buf_align, new_len, ra)) { + if (self.parent_allocator.rawResize(buf, alignment, new_len, ra)) { if (new_len > buf.len) { self.writer.print(" success!\n", .{}) catch {}; } @@ -71,15 +72,41 @@ pub fn LogToWriterAllocator(comptime Writer: type) type { return false; } + fn remap( + ctx: *anyopaque, + buf: []u8, + alignment: std.mem.Alignment, + new_len: usize, + ra: usize, + ) ?[*]u8 { + const self: *Self = @ptrCast(@alignCast(ctx)); + if (new_len <= buf.len) { + self.writer.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {}; + } else { + self.writer.print("expand: {} to {}", .{ buf.len, new_len }) catch {}; + } + + if (self.parent_allocator.rawRemap(buf, alignment, new_len, ra)) |new_memory| { + if (new_len > buf.len) { + self.writer.print(" success!\n", .{}) catch {}; + } + return new_memory; + } + + std.debug.assert(new_len > buf.len); + self.writer.print(" failure!\n", .{}) catch {}; + return null; + } + fn free( ctx: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: std.mem.Alignment, ra: usize, ) void { const self: *Self = @ptrCast(@alignCast(ctx)); self.writer.print("free : {}\n", .{buf.len}) catch {}; - self.parent_allocator.rawFree(buf, log2_buf_align, ra); + self.parent_allocator.rawFree(buf, alignment, ra); } }; } diff --git a/lib/std/mem.zig b/lib/std/mem.zig index c357c768d768ce4c4305c5c0fff8fe97dae48b9f..0f56762c27823cd1ab73f07d72c33ab28c4b2b0c 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -92,6 +92,7 @@ pub fn ValidationAllocator(comptime T: type) type { .vtable = &.{ .alloc = alloc, .resize = resize, + .remap = remap, .free = free, }, }; @@ -105,41 +106,54 @@ pub fn ValidationAllocator(comptime T: type) type { pub fn alloc( ctx: *anyopaque, n: usize, - log2_ptr_align: u8, + alignment: mem.Alignment, ret_addr: usize, ) ?[*]u8 { assert(n > 0); const self: *Self = @ptrCast(@alignCast(ctx)); const underlying = self.getUnderlyingAllocatorPtr(); - const result = underlying.rawAlloc(n, log2_ptr_align, ret_addr) orelse + const result = underlying.rawAlloc(n, alignment, ret_addr) orelse return null; - assert(mem.isAlignedLog2(@intFromPtr(result), log2_ptr_align)); + assert(alignment.check(@intFromPtr(result))); return result; } pub fn resize( ctx: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: Alignment, new_len: usize, ret_addr: usize, ) bool { const self: *Self = @ptrCast(@alignCast(ctx)); assert(buf.len > 0); const underlying = self.getUnderlyingAllocatorPtr(); - return underlying.rawResize(buf, log2_buf_align, new_len, ret_addr); + return underlying.rawResize(buf, alignment, new_len, ret_addr); + } + + pub fn remap( + ctx: *anyopaque, + buf: []u8, + alignment: Alignment, + new_len: usize, + ret_addr: usize, + ) ?[*]u8 { + const self: *Self = @ptrCast(@alignCast(ctx)); + assert(buf.len > 0); + const underlying = self.getUnderlyingAllocatorPtr(); + return underlying.rawRemap(buf, alignment, new_len, ret_addr); } pub fn free( ctx: *anyopaque, buf: []u8, - log2_buf_align: u8, + alignment: Alignment, ret_addr: usize, ) void { const self: *Self = @ptrCast(@alignCast(ctx)); assert(buf.len > 0); const underlying = self.getUnderlyingAllocatorPtr(); - underlying.rawFree(buf, log2_buf_align, ret_addr); + underlying.rawFree(buf, alignment, ret_addr); } pub fn reset(self: *Self) void { @@ -167,27 +181,9 @@ pub fn alignAllocLen(full_len: usize, alloc_len: usize, len_align: u29) usize { return adjusted; } -const fail_allocator = Allocator{ - .ptr = undefined, - .vtable = &failAllocator_vtable, -}; - -const failAllocator_vtable = Allocator.VTable{ - .alloc = failAllocatorAlloc, - .resize = Allocator.noResize, - .free = Allocator.noFree, -}; - -fn failAllocatorAlloc(_: *anyopaque, n: usize, log2_alignment: u8, ra: usize) ?[*]u8 { - _ = n; - _ = log2_alignment; - _ = ra; - return null; -} - test "Allocator basics" { - try testing.expectError(error.OutOfMemory, fail_allocator.alloc(u8, 1)); - try testing.expectError(error.OutOfMemory, fail_allocator.allocSentinel(u8, 1, 0)); + try testing.expectError(error.OutOfMemory, testing.failing_allocator.alloc(u8, 1)); + try testing.expectError(error.OutOfMemory, testing.failing_allocator.allocSentinel(u8, 1, 0)); } test "Allocator.resize" {