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