| ... | @@ -9,6 +9,7 @@ const math = std.math; | ... | @@ -9,6 +9,7 @@ const math = std.math; |
| 9 | const mem = std.mem; | 9 | const mem = std.mem; |
| 10 | const Alignment = std.mem.Alignment; | 10 | const Alignment = std.mem.Alignment; |
| 11 | const Slice = std.meta.Slice; | 11 | const Slice = std.meta.Slice; |
| | 12 | const AbsorbSentinel = std.meta.AbsorbSentinel; |
| 12 | | 13 | |
| 13 | pub const Error = error{OutOfMemory}; | 14 | pub const Error = error{OutOfMemory}; |
| 14 | pub const Log2Align = math.Log2Int(usize); | 15 | pub const Log2Align = math.Log2Int(usize); |
| ... | @@ -316,15 +317,16 @@ pub fn allocBytesAligned( | ... | @@ -316,15 +317,16 @@ pub fn allocBytesAligned( |
| 316 | /// | 317 | /// |
| 317 | /// `new_len` may be zero, in which case the allocation is freed. | 318 | /// `new_len` may be zero, in which case the allocation is freed. |
| 318 | pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { | 319 | pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { |
| 319 | const SliceType = Slice(@TypeOf(allocation)); | 320 | const slice_info = @typeInfo(@TypeOf(allocation)).pointer; |
| 320 | const slice: SliceType = allocation; // coerce *[len]T to []T | 321 | const T = if (slice_info.size != .slice) comptime T: { |
| 321 | const slice_info = @typeInfo(SliceType).pointer; | 322 | assert(slice_info.size == .one); |
| 322 | const T = slice_info.child; | 323 | break :T @typeInfo(slice_info.child).array.child; |
| | 324 | } else slice_info.child; |
| 323 | if (new_len == 0) { | 325 | if (new_len == 0) { |
| 324 | self.free(slice); | 326 | self.free(allocation); |
| 325 | return true; | 327 | return true; |
| 326 | } | 328 | } |
| 327 | if (slice.len == 0) { | 329 | if (allocation.len == 0) { |
| 328 | return false; | 330 | return false; |
| 329 | } | 331 | } |
| 330 | const old_memory: []u8 = @ptrCast(@constCast(mem.absorbSentinel(allocation))); | 332 | const old_memory: []u8 = @ptrCast(@constCast(mem.absorbSentinel(allocation))); |
| ... | @@ -356,25 +358,26 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { | ... | @@ -356,25 +358,26 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { |
| 356 | /// `new_len` may be zero, in which case the allocation is freed. | 358 | /// `new_len` may be zero, in which case the allocation is freed. |
| 357 | /// | 359 | /// |
| 358 | /// If the allocation's elements' type is zero bytes sized, `allocation.len` is set to `new_len`. | 360 | /// If the allocation's elements' type is zero bytes sized, `allocation.len` is set to `new_len`. |
| 359 | pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?Slice(@TypeOf(allocation)) { | 361 | pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?Slice(AbsorbSentinel(@TypeOf(allocation))) { |
| 360 | const SliceType = Slice(@TypeOf(allocation)); | 362 | const slice_info = @typeInfo(@TypeOf(allocation)).pointer; |
| 361 | const slice: SliceType = allocation; // coerce *[len]T to []T | 363 | const T = if (slice_info.size != .slice) comptime T: { |
| 362 | const slice_info = @typeInfo(SliceType).pointer; | 364 | assert(slice_info.size == .one); |
| 363 | const T = slice_info.child; | 365 | break :T @typeInfo(slice_info.child).array.child; |
| | 366 | } else slice_info.child; |
| 364 | | 367 | |
| 365 | if (new_len == 0) { | 368 | if (new_len == 0) { |
| 366 | self.free(slice); | 369 | self.free(allocation); |
| 367 | return slice[0..0]; | 370 | return allocation[0..0]; |
| 368 | } | 371 | } |
| 369 | if (slice.len == 0) { | 372 | if (allocation.len == 0) { |
| 370 | return null; | 373 | return null; |
| 371 | } | 374 | } |
| 372 | if (@sizeOf(T) == 0) { | 375 | if (@sizeOf(T) == 0) { |
| 373 | var new_memory = slice; | 376 | var new_memory = allocation; |
| 374 | new_memory.len = new_len; | 377 | new_memory.len = new_len; |
| 375 | return new_memory; | 378 | return new_memory; |
| 376 | } | 379 | } |
| 377 | const old_memory: []u8 = @ptrCast(@constCast(mem.absorbSentinel(slice))); | 380 | const old_memory: []u8 = @ptrCast(@constCast(mem.absorbSentinel(allocation))); |
| 378 | // I would like to use saturating multiplication here, but LLVM cannot lower it | 381 | // I would like to use saturating multiplication here, but LLVM cannot lower it |
| 379 | // on WebAssembly: https://github.com/ziglang/zig/issues/9660 | 382 | // on WebAssembly: https://github.com/ziglang/zig/issues/9660 |
| 380 | //const new_len_bytes = new_len *| @sizeOf(T); | 383 | //const new_len_bytes = new_len *| @sizeOf(T); |
| ... | @@ -402,7 +405,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?Slice(@TypeO | ... | @@ -402,7 +405,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?Slice(@TypeO |
| 402 | /// do the realloc more efficiently than the caller | 405 | /// do the realloc more efficiently than the caller |
| 403 | /// * `resize` which returns `false` when the `Allocator` implementation cannot | 406 | /// * `resize` which returns `false` when the `Allocator` implementation cannot |
| 404 | /// change the size without relocating the allocation. | 407 | /// change the size without relocating the allocation. |
| 405 | pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) Error!Slice(@TypeOf(old_mem)) { | 408 | pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) Error!Slice(AbsorbSentinel(@TypeOf(old_mem))) { |
| 406 | return self.reallocAdvanced(old_mem, new_n, @returnAddress()); | 409 | return self.reallocAdvanced(old_mem, new_n, @returnAddress()); |
| 407 | } | 410 | } |
| 408 | | 411 | |
| ... | @@ -411,24 +414,24 @@ pub fn reallocAdvanced( | ... | @@ -411,24 +414,24 @@ pub fn reallocAdvanced( |
| 411 | old_mem: anytype, | 414 | old_mem: anytype, |
| 412 | new_n: usize, | 415 | new_n: usize, |
| 413 | return_address: usize, | 416 | return_address: usize, |
| 414 | ) Error!Slice(@TypeOf(old_mem)) { | 417 | ) Error!Slice(AbsorbSentinel(@TypeOf(old_mem))) { |
| 415 | const SliceType = Slice(@TypeOf(old_mem)); | 418 | const slice_info = @typeInfo(@TypeOf(old_mem)).pointer; |
| 416 | const slice: SliceType = old_mem; // coerce *[len]T to []T | 419 | const T = if (slice_info.size != .slice) comptime T: { |
| 417 | const slice_info = @typeInfo(SliceType).pointer; | 420 | assert(slice_info.size == .one); |
| 418 | comptime assert(slice_info.size == .slice); | 421 | break :T @typeInfo(slice_info.child).array.child; |
| 419 | const T = slice_info.child; | 422 | } else slice_info.child; |
| 420 | if (slice.len == 0) { | 423 | if (old_mem.len == 0) { |
| 421 | return self.allocAdvancedWithRetAddr(T, .fromByteUnitsOptional(slice_info.attrs.@"align"), new_n, return_address); | 424 | return self.allocAdvancedWithRetAddr(T, .fromByteUnitsOptional(slice_info.attrs.@"align"), new_n, return_address); |
| 422 | } | 425 | } |
| 423 | if (new_n == 0) { | 426 | if (new_n == 0) { |
| 424 | self.free(slice); | 427 | self.free(old_mem); |
| 425 | const alignment = slice_info.attrs.@"align" orelse @alignOf(T); | 428 | const alignment = slice_info.attrs.@"align" orelse @alignOf(T); |
| 426 | const addr = comptime std.mem.alignBackward(usize, math.maxInt(usize), alignment); | 429 | const addr = comptime std.mem.alignBackward(usize, math.maxInt(usize), alignment); |
| 427 | const ptr: *align(alignment) [0]T = @ptrFromInt(addr); | 430 | const ptr: *align(alignment) [0]T = @ptrFromInt(addr); |
| 428 | return ptr; | 431 | return ptr; |
| 429 | } | 432 | } |
| 430 | | 433 | |
| 431 | const old_byte_slice: []u8 = @ptrCast(@constCast(mem.absorbSentinel(slice))); | 434 | const old_byte_slice: []u8 = @ptrCast(@constCast(mem.absorbSentinel(old_mem))); |
| 432 | const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return error.OutOfMemory; | 435 | const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return error.OutOfMemory; |
| 433 | // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure | 436 | // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure |
| 434 | if (self.rawRemap(old_byte_slice, .fromByteUnits(slice_info.attrs.@"align" orelse @alignOf(T)), byte_count, return_address)) |p| { | 437 | if (self.rawRemap(old_byte_slice, .fromByteUnits(slice_info.attrs.@"align" orelse @alignOf(T)), byte_count, return_address)) |p| { |
| ... | @@ -451,10 +454,8 @@ pub fn reallocAdvanced( | ... | @@ -451,10 +454,8 @@ pub fn reallocAdvanced( |
| 451 | pub fn free(self: Allocator, memory: anytype) void { | 454 | pub fn free(self: Allocator, memory: anytype) void { |
| 452 | const slice_info = @typeInfo(@TypeOf(memory)).pointer; | 455 | const slice_info = @typeInfo(@TypeOf(memory)).pointer; |
| 453 | if (slice_info.size != .slice) { | 456 | if (slice_info.size != .slice) { |
| 454 | const slice: Slice(@TypeOf(memory)) = memory; // coerce *[len]T to []T | 457 | assert(slice_info.size == .one and @typeInfo(slice_info.child) == .array); |
| 455 | return free(self, slice); | | |
| 456 | } | 458 | } |
| 457 | comptime assert(slice_info.size == .slice); | | |
| 458 | const bytes: []u8 = @ptrCast(@constCast(mem.absorbSentinel(memory))); | 459 | const bytes: []u8 = @ptrCast(@constCast(mem.absorbSentinel(memory))); |
| 459 | if (bytes.len == 0) return; | 460 | if (bytes.len == 0) return; |
| 460 | @memset(bytes, undefined); | 461 | @memset(bytes, undefined); |
| ... | @@ -600,6 +601,33 @@ test failing { | ... | @@ -600,6 +601,33 @@ test failing { |
| 600 | | 601 | |
| 601 | test "free single-pointer to array" { | 602 | test "free single-pointer to array" { |
| 602 | const allocator = std.testing.allocator; | 603 | const allocator = std.testing.allocator; |
| 603 | const bytes = allocator.alloc(u32, 128) catch return error.SkipZigTest; | 604 | { |
| 604 | allocator.free(bytes.ptr[0..128]); | 605 | const slice = allocator.alloc(u32, 128) catch return error.SkipZigTest; |
| | 606 | slice[127] = 0; |
| | 607 | const ptr = slice[0..127 :0]; |
| | 608 | allocator.free(ptr); |
| | 609 | } |
| | 610 | { |
| | 611 | const slice = allocator.alloc(u32, 128) catch return error.SkipZigTest; |
| | 612 | slice[127] = 0; |
| | 613 | const ptr = slice[0..127 :0]; |
| | 614 | if (allocator.resize(ptr, 16)) { |
| | 615 | allocator.free(ptr[0..16]); |
| | 616 | } else allocator.free(ptr); |
| | 617 | } |
| | 618 | { |
| | 619 | const slice = allocator.alloc(u32, 128) catch return error.SkipZigTest; |
| | 620 | slice[127] = 0; |
| | 621 | const ptr = slice[0..127 :0]; |
| | 622 | if (allocator.remap(ptr, 16)) |new| { |
| | 623 | allocator.free(new); |
| | 624 | } else allocator.free(ptr); |
| | 625 | } |
| | 626 | { |
| | 627 | const slice = allocator.alloc(u32, 128) catch return error.SkipZigTest; |
| | 628 | slice[127] = 0; |
| | 629 | const ptr = slice[0..127 :0]; |
| | 630 | const new = allocator.realloc(ptr, 16) catch return error.SkipZigTest; |
| | 631 | allocator.free(new); |
| | 632 | } |
| 605 | } | 633 | } |