What's the right way to destroy an array where you don't have the type specifically, but you have the alignment and size

Specifically right now lets say we have some kind of Registry of allocations

const Allocation = struct {
    object:*anyopaque,
    typeAlign: usize,
    typeSize: usize,
    allocator: std.mem.Allocator,
};

const Registry = struct {
    allocated: ArrayList(Allocation),

    pub fn emergencyShutdown(self: *@This()) void
    {
        for(self.allocated) |allocation|
        {
            // How would one destroy all of these allocations from here?
            // in C we can just free() on the pointer but allocator.destroy() 
            // requires a typed pointer
        }
    }
};

My current solution is to keep a function pointer to a teardown function for each of the allocated objects. But Was wondering if it’s at all possible to map to the c style functionality

This is a bit tricky. The preferred functions, free and destroy, take the alignment at compile-time through the type information.
I guess the safest option would be to use rawFree which takes the alignment at runtime.
However it comes with a warning in the doc comment:

/// This function is not intended to be called except from within the
/// implementation of an Allocator
pub inline fn rawFree(self: Allocator, buf: []u8, log2_buf_align: u8, ret_addr: usize) void {

But it could be argued that you made something resembling parts of an “allocator” with your registry there. So I think it is alright to use this here:

for(self.allocated) |allocation|
{
    const rawMemory: []u8 = @as([*]u8, @ptrCast(allocation.object))[0..allocation.typeSize];
    const log2Align =  std.math.log2_int(usize, allocation.typeAlign);
    allocation.allocator.rawFree(rawMemory, log2Align, @returnAddress());
}
1 Like

A quick solution is to just use malloc. At that point you don’t even need to know the length of the array.

https://ziglang.org/documentation/master/std/#A;std:c.malloc

https://ziglang.org/documentation/master/std/#A;std:c.free

You will need to link libc to use those though.

1 Like

Interesting I like the idea of just using malloc/free for when im doing this, it’s a pretty exceptional case. Thanks for the info guys!

Another option is to create a []u8 with the information you have and pass that to free. I know that the allocator documentation says that you should free the exact slice that you were give from alloc, but I’ve looked at a few implementations of allocators and, in all that I’ve looked, this would work.

1 Like

This works well however it seems to piss off the GPA. GPA is expecting a free of alignment 8 for a larger struct, but []u8 has an alignment of 1.

Wonder how i can convince it that it’s ok. @alignCast requires a destination type to cast to in 0.11.0 and beyond. If this was 0.10 I could just specify a custom alignment.

you can use []align(alignment) T see Zig Documentation - Alignment
so from what you are saying []align(8) u8 should work?

1 Like

Hmm interesting I’m trying to figure out how I can make that work for runtime. As the the alignment if I understand correctly can’t be changed, it’s part of the type.

Oh I suppose I can just allocate everything as if they were align 8. Like c style malloc behaviour. For release builds I just use malloc/free for this specific operation.

My current solution which satisfies the GPA is a comptime which properly casts it to the right type and frees it.

struct managed_allocation{
    data: []u8,
    destroyFn: *const fn (*anyopaque, std.mem.Allocator) void,

    pub fn create(T: type, allocator: std.mem.Allocator) @This() 
    { 
        const Wrap = struct {
            pub fn wrappedDestroy(ptr: *anyopaque, alloc: std.mem.Allocator) void {
                var p = @as(*T, @ptrCast(@alignCast(ptr)));
                alloc.destroy(p);
            }
        };
        return .{
              .data = <... allocator.create and a bunch of casting>, 
              .destroyFn = Wrap.wrappedDestroy,
        };
    }

    pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void 
    {
         self.destroyFn(self.data.ptr, self.allocator);
    }
};
1 Like