fn doOneResize(ctx: *FuzzContext, new_len: usize) void {
@disableInstrumentation();
const index = @atomicRmw(Alloc.Index, &ctx.last_alloc_index, .Xchg, .none, .acquire);
if (index == .none) return;
const allocation = &ctx.allocs[@backingInt(index)];
assert(allocation.common.len != .free);
const memory = allocation.sample_ptr[0..@backingInt(allocation.common.len)];
const alignment = allocation.common.alignment;
assert(alignment.check(@intFromPtr(allocation.control_ptr)));
assert(alignment.check(@intFromPtr(allocation.sample_ptr)));
// Since `resize` is fallible, we have to ensure that `control_allocator`
// is always successful by reserving the memory we need beforehand.
const new_control_ptr = ctx.control_allocator.rawAlloc(new_len, alignment, @returnAddress()) orelse
return;
if (ctx.sample_allocator.rawResize(memory, alignment, new_len, @returnAddress())) {
const old_control = allocation.control_ptr[0..memory.len];
const overlap = @min(memory.len, new_len);
@memcpy(new_control_ptr[0..overlap], old_control[0..overlap]);
ctx.control_allocator.rawFree(old_control, alignment, @returnAddress());
} else {
ctx.control_allocator.rawFree(new_control_ptr[0..new_len], alignment, @returnAddress());
return;
}
ctx.allocs[@backingInt(index)] = .{
.control_ptr = new_control_ptr,
.sample_ptr = memory.ptr,
.common = .{
.len = @fromBackingInt(@intCast(new_len)),
.alignment = alignment,
},
};
if (new_len > memory.len) {
for (
allocation.control_ptr[memory.len..new_len],
allocation.sample_ptr[memory.len..new_len],
0..,
) |*control, *sample, i| {
control.* = @truncate(i);
sample.* = @truncate(i);
}
}
@atomicStore(Alloc.Index, &ctx.last_alloc_index, index, .release);
}