#How to extern an allocated pointer and free it later?

1 messages · Page 1 of 1 (latest)

calm scaffold
#

If I'm writing a zig library, how do I return an allocated byte array and free it later?

Returning a pointer to a null-terminated allocated byte array is straight-forward:

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
export fn allocByteArrayZ(size: u8) [*]u8 {
    const arrayZ = gpa.allocator().alloc(u8, size) catch unreachable;
    arrayZ[size - 1] = 0;
    return arrayZ.ptr;
}

But I'm stuck on how to convert the pointer back into a slice so I can free it later.

export fn freeByteArrayZ(arrayZ: [*]u8) void {
    gpa.allocator().free(arrayZ); // doesn't work, expects a []u8
}

I know the length, as I can assume it is a null-terminated array:

const array = @ptrCast([*:0]u8, arrayZ);

But I don't know how to turn [*:0]u8 into []u8

thick anvil
#

protip: you can use [*:0]u8 in callconv(.C) function signatures

#

at any rate, the way allocators usually handle this is by adding metadata before the pointer

#

(since relying on null-termination is really flimsy, if the caller can modify the memory to contain null values)

#

you can add metadata before the returned pointer something like this:

const Metadata = struct { size: usize };

export fn allocByteArray(size: u8) [*]u8 {
    const full_slice = try allocator.alloc(u8, @sizeOf(Metadata) + size);
    errdefer allocator.free(full_slice);
    std.mem.bytesAsValue(Metadata, full_slice[0..@sizeOf(Metadata)]).* = .{
        .size = size,
    };
    return full_slice.ptr + @sizeOf(Metadata);
}

export fn freeByteArray(ptr: [*]u8) void {
    const full_ptr = ptr - @sizeOf(Metadata);
    const metadata = std.mem.bytesToValue(full_ptr[0..@sizeOf(Metadata)]);
    allocator.free(full_ptr[0 .. @sizeOf(Metadata) + metadata.size]);
}
#

this of course does add overhead to each allocation

#

which is why it's beneficial in zig to have the length accompanied with each allocation

calm scaffold
#

ah, i could have used allocator.free(array[0..std.mem.len(array)]) but good point on the metadata

calm scaffold
thick anvil
#

it allows anything that's allowed by the C ABI

#

in terms of zig, that includes all non-slice pointers, extern structs, and packed structs with an ABI-sized integer (power of two bits), enums with ABI-sized integers (ditto) and ABI-sized integers

#

oh, and optional variants of the aforementioned pointers

#

but not if the pointer type in question is allowzero

#

good source of truth is to just try it and see what the compiler says

#

but those rules should get you as far as you need

calm scaffold
#

ok, so .Unspecified is some sort of generic common ABI and you use callconv it narrow it down?

thick anvil
#

Unspecified is just "Do whatever you want Zig"

calm scaffold
#

thanks