#Implement realloc for a C library with a backing zig allocator

1 messages · Page 1 of 1 (latest)

harsh musk
#

Hello !

I'm trying to build a wasm freestanding app that uses a C library. I need to provide a custom implementation of malloc using a Zig allocator. I managed to get the malloc and free part working, but the zig realloc API explicitely require a slice, while the C realloc api only gives you a raw pointer to work from. Is there a trick to make this work, or is there any example of a project that shipped a custom C allocator backed by a zig one ?

Thanks !

#

I'll close this thread as anwsered if that worked

boreal nest
#

I would have done it like this, bit more simple. Could always leave the validation part to something like GPA:

var allocator: std.mem.Allocator = std.heap.page_allocator; // set to whatever, or make it const if you want

const alignment = 16;
const padded_metadata_size = std.mem.alignForward(@sizeOf(Metadata), alignment);
const Metadata = struct {
    size: usize,

    fn fullLen(data: Metadata) usize {
        return padded_metadata_size + data.size;
    }
};

export fn zig_malloc(size: usize) ?[*]align(alignment) u8 {
    const metadata = Metadata{
        .size = size,
    };
    const full_alloc = allocator.alignedAlloc(u8, alignment, metadata.fullLen()) catch return null;
    std.mem.bytesAsValue(Metadata, full_alloc[0..@sizeOf(Metadata)]).* = metadata;
    return full_alloc[padded_metadata_size..].ptr;
}

export fn zig_realloc(maybe_ptr: ?[*]align(alignment) u8, new_size: usize) ?[*]align(alignment) u8 {
    const ptr = maybe_ptr orelse return @call(.always_inline, zig_malloc, .{new_size});
    const old_ptr = ptr - padded_metadata_size;
    const old_metadata = std.mem.bytesToValue(Metadata, old_ptr[0..@sizeOf(Metadata)]);
    const new_metadata = Metadata{
        .size = new_size,
    };
    const new_slice = allocator.realloc(old_ptr[0..old_metadata.fullLen()], new_metadata.fullLen()) catch
        return null;
    std.mem.bytesAsValue(Metadata, new_slice[0..@sizeOf(Metadata)]).* = new_metadata;
    return new_slice[padded_metadata_size..].ptr;
}

export fn zig_free(maybe_ptr: ?[*]align(alignment) u8) void {
    const ptr = maybe_ptr orelse return;
    const real_ptr = ptr - padded_metadata_size;
    const metadata = std.mem.bytesToValue(Metadata, real_ptr[0..@sizeOf(Metadata)]);
    allocator.free(real_ptr[0..metadata.fullLen()]);
}
#

but yes, ultimately you'll end up having to store data somewhere

#

you could also choose to store it in some global state, like a hash map that maps pointers to metadata

harsh musk
#

Well thank you very much, because your solution is working first try while the previous one was panicking everywhere (and due to the nature of freestanding I was having a hard time finding where the crash were)

boreal nest
#

just make sure you use something other than std.heap.page_allocator

#

that's gonna go sloww

harsh musk
#

yeah I'm using an arena because the allocations are only temporary

boreal nest
#

well, you are aware that arena allocator doesn't actually do anything in its free function implementation, aye?

#

so if they're only temporary, that means they're going to stick around for the entire duration of the program, instead of being freed as soon as you free them

#

like a sort of soft leak