Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265const std = @import("std");const builtin = @import("builtin");const testing = std.testing;
/// Convenience functionspub const convenience = @import("allocator/convenience.zig");
/// Useful alias since they're required to create Zig allocatorspub const ZigVTable = std.mem.Allocator.VTable;
/// The VTable required by the C interface./// C: GhosttyAllocatorVtablepub const VTable = extern struct { alloc: *const fn (*anyopaque, len: usize, alignment: u8, ret_addr: usize) callconv(.c) ?[*]u8, resize: *const fn (*anyopaque, memory: [*]u8, memory_len: usize, alignment: u8, new_len: usize, ret_addr: usize) callconv(.c) bool, remap: *const fn (*anyopaque, memory: [*]u8, memory_len: usize, alignment: u8, new_len: usize, ret_addr: usize) callconv(.c) ?[*]u8, free: *const fn (*anyopaque, memory: [*]u8, memory_len: usize, alignment: u8, ret_addr: usize) callconv(.c) void,};
/// Returns an allocator to use for the given possibly-null C allocator,/// ensuring some allocator is always returned.pub fn default(c_alloc_: ?*const Allocator) std.mem.Allocator { // If we're given an allocator, use it. if (c_alloc_) |c_alloc| return c_alloc.zig();
// Tests always use the test allocator so we can detect leaks. if (comptime builtin.is_test) return testing.allocator;
// If we have libc, use that. We prefer libc if we have it because // its generally fast but also lets the embedder easily override // malloc/free with custom allocators like mimalloc or something. if (comptime builtin.link_libc) return std.heap.c_allocator;
// Wasm if (comptime builtin.target.cpu.arch.isWasm()) return std.heap.wasm_allocator;
// No libc, use the preferred allocator for releases which is the // Zig SMP allocator. return std.heap.smp_allocator;}
/// The Allocator interface for custom memory allocation strategies/// within C libghostty APIs.////// This -- purposely -- matches the Zig allocator interface. We do this/// for two reasons: (1) Zig's allocator interface is well proven in/// the real world to be flexible and useful, and (2) it allows us to/// easily convert C allocators to Zig allocators and vice versa, since/// we're written in Zig.////// C: GhosttyAllocatorpub const Allocator = extern struct { ctx: *anyopaque, vtable: *const VTable,
/// vtable for the Zig allocator interface to map our extern /// allocator to Zig's allocator interface. pub const zig_vtable: ZigVTable = .{ .alloc = alloc, .resize = resize, .remap = remap, .free = free, };
/// Create a C allocator from a Zig allocator. This requires that /// the Zig allocator be pointer-stable for the lifetime of the /// C allocator. pub fn fromZig(zig_alloc: *const std.mem.Allocator) Allocator { return .{ .ctx = @ptrCast(@constCast(zig_alloc)), .vtable = &ZigAllocator.vtable, }; }
/// Create a Zig allocator from this C allocator. This requires /// a pointer to a Zig allocator vtable that we can populate with /// our callbacks. pub fn zig(self: *const Allocator) std.mem.Allocator { return .{ .ptr = @ptrCast(@constCast(self)), .vtable = &zig_vtable, }; }
fn alloc( ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ra: usize, ) ?[*]u8 { const self: *Allocator = @ptrCast(@alignCast(ctx)); return self.vtable.alloc( self.ctx, len, @intFromEnum(alignment), ra, ); }
fn resize( ctx: *anyopaque, old_mem: []u8, alignment: std.mem.Alignment, new_len: usize, ra: usize, ) bool { const self: *Allocator = @ptrCast(@alignCast(ctx)); return self.vtable.resize( self.ctx, old_mem.ptr, old_mem.len, @intFromEnum(alignment), new_len, ra, ); }
fn remap( ctx: *anyopaque, old_mem: []u8, alignment: std.mem.Alignment, new_len: usize, ra: usize, ) ?[*]u8 { const self: *Allocator = @ptrCast(@alignCast(ctx)); return self.vtable.remap( self.ctx, old_mem.ptr, old_mem.len, @intFromEnum(alignment), new_len, ra, ); }
fn free( ctx: *anyopaque, old_mem: []u8, alignment: std.mem.Alignment, ra: usize, ) void { const self: *Allocator = @ptrCast(@alignCast(ctx)); self.vtable.free( self.ctx, old_mem.ptr, old_mem.len, @intFromEnum(alignment), ra, ); }};
/// An allocator implementation that wraps a Zig allocator so that/// it can be exposed to C.const ZigAllocator = struct { const vtable: VTable = .{ .alloc = alloc, .resize = resize, .remap = remap, .free = free, };
fn alloc( ctx: *anyopaque, len: usize, alignment: u8, ra: usize, ) callconv(.c) ?[*]u8 { const zig_alloc: *const std.mem.Allocator = @ptrCast(@alignCast(ctx)); return zig_alloc.vtable.alloc( zig_alloc.ptr, len, @enumFromInt(alignment), ra, ); }
fn resize( ctx: *anyopaque, memory: [*]u8, memory_len: usize, alignment: u8, new_len: usize, ra: usize, ) callconv(.c) bool { const zig_alloc: *const std.mem.Allocator = @ptrCast(@alignCast(ctx)); return zig_alloc.vtable.resize( zig_alloc.ptr, memory[0..memory_len], @enumFromInt(alignment), new_len, ra, ); }
fn remap( ctx: *anyopaque, memory: [*]u8, memory_len: usize, alignment: u8, new_len: usize, ra: usize, ) callconv(.c) ?[*]u8 { const zig_alloc: *const std.mem.Allocator = @ptrCast(@alignCast(ctx)); return zig_alloc.vtable.remap( zig_alloc.ptr, memory[0..memory_len], @enumFromInt(alignment), new_len, ra, ); }
fn free( ctx: *anyopaque, memory: [*]u8, memory_len: usize, alignment: u8, ra: usize, ) callconv(.c) void { const zig_alloc: *const std.mem.Allocator = @ptrCast(@alignCast(ctx)); return zig_alloc.vtable.free( zig_alloc.ptr, memory[0..memory_len], @enumFromInt(alignment), ra, ); }};
/// libc Allocator, requires linking libcpub const c_allocator: Allocator = .fromZig(&std.heap.c_allocator);
/// Allocator that can be sent to the C API that does full/// leak checking within Zig tests. This should only be used from/// Zig tests.pub const test_allocator: Allocator = b: { if (!builtin.is_test) @compileError("test_allocator can only be used in tests"); break :b .fromZig(&testing.allocator);};
test "c allocator" { if (!comptime builtin.link_libc) return error.SkipZigTest;
const alloc = c_allocator.zig(); const str = try alloc.alloc(u8, 10); defer alloc.free(str); try testing.expectEqual(10, str.len);}
test "fba allocator" { var buf: [1024]u8 = undefined; var fba: std.heap.FixedBufferAllocator = .init(&buf); const zig_alloc = fba.allocator();
// Convert the Zig allocator to a C interface const c_alloc: Allocator = .fromZig(&zig_alloc);
// Convert back to Zig so we can test it. const alloc = c_alloc.zig(); const str = try alloc.alloc(u8, 10); defer alloc.free(str); try testing.expectEqual(10, str.len);}