vt: add ghostty_alloc for buffer allocation
Add a ghostty_alloc function that pairs with the existing ghostty_free, giving embedders a symmetric malloc/free-style API for buffer allocation through the libghostty allocator interface. Returns NULL on allocation failure.pull/11785/head
parent
7039f566bb
commit
b819ce0e20
|
|
@ -44,6 +44,24 @@
|
|||
* 2. Create a GhosttyAllocator struct with your vtable and context
|
||||
* 3. Pass the allocator to functions that accept one
|
||||
*
|
||||
* ## Alloc/Free Helpers
|
||||
*
|
||||
* ghostty_alloc() and ghostty_free() provide a simple malloc/free-style
|
||||
* interface for allocating and freeing byte buffers through the library's
|
||||
* allocator. These are useful when:
|
||||
*
|
||||
* - You need to allocate a buffer to pass into a libghostty-vt function
|
||||
* (e.g. preparing input data for ghostty_terminal_vt_write()).
|
||||
* - You need to free a buffer returned by a libghostty-vt function
|
||||
* (e.g. the output of ghostty_formatter_format_alloc()).
|
||||
* - You are on a platform where the library's internal allocator differs
|
||||
* from the consumer's C runtime (e.g. Windows, where Zig's libc and
|
||||
* MSVC's CRT maintain separate heaps), so calling the standard C
|
||||
* free() on library-allocated memory would be undefined behavior.
|
||||
*
|
||||
* Always use the same allocator (or NULL) for both the allocation and
|
||||
* the corresponding free.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
|
@ -191,6 +209,21 @@ typedef struct GhosttyAllocator {
|
|||
const GhosttyAllocatorVtable *vtable;
|
||||
} GhosttyAllocator;
|
||||
|
||||
/**
|
||||
* Allocate a buffer of `len` bytes.
|
||||
*
|
||||
* Uses the provided allocator, or the default allocator if NULL is passed.
|
||||
* The returned buffer must be freed with ghostty_free() using the same
|
||||
* allocator.
|
||||
*
|
||||
* @param allocator Pointer to the allocator to use, or NULL for the default
|
||||
* @param len Number of bytes to allocate
|
||||
* @return Pointer to the allocated buffer, or NULL if allocation failed
|
||||
*
|
||||
* @ingroup allocator
|
||||
*/
|
||||
uint8_t* ghostty_alloc(const GhosttyAllocator* allocator, size_t len);
|
||||
|
||||
/**
|
||||
* Free memory that was allocated by a libghostty-vt function.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ comptime {
|
|||
@export(&c.grid_ref_graphemes, .{ .name = "ghostty_grid_ref_graphemes" });
|
||||
@export(&c.grid_ref_style, .{ .name = "ghostty_grid_ref_style" });
|
||||
@export(&c.build_info, .{ .name = "ghostty_build_info" });
|
||||
@export(&c.alloc_alloc, .{ .name = "ghostty_alloc" });
|
||||
@export(&c.alloc_free, .{ .name = "ghostty_free" });
|
||||
|
||||
// On Wasm we need to export our allocator convenience functions.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,20 @@ const testing = std.testing;
|
|||
const lib_alloc = @import("../../lib/allocator.zig");
|
||||
const CAllocator = lib_alloc.Allocator;
|
||||
|
||||
/// Allocate a buffer of `len` bytes using the given allocator
|
||||
/// (or the default allocator if NULL).
|
||||
///
|
||||
/// Returns a pointer to the allocated buffer, or NULL if the
|
||||
/// allocation failed.
|
||||
pub fn alloc(
|
||||
alloc_: ?*const CAllocator,
|
||||
len: usize,
|
||||
) callconv(.c) ?[*]u8 {
|
||||
const allocator = lib_alloc.default(alloc_);
|
||||
const buf = allocator.alloc(u8, len) catch return null;
|
||||
return buf.ptr;
|
||||
}
|
||||
|
||||
/// Free memory that was allocated by a libghostty-vt function.
|
||||
///
|
||||
/// This must be used to free buffers returned by functions like
|
||||
|
|
@ -14,8 +28,25 @@ pub fn free(
|
|||
len: usize,
|
||||
) callconv(.c) void {
|
||||
const mem = ptr orelse return;
|
||||
const alloc = lib_alloc.default(alloc_);
|
||||
alloc.free(mem[0..len]);
|
||||
const allocator = lib_alloc.default(alloc_);
|
||||
allocator.free(mem[0..len]);
|
||||
}
|
||||
|
||||
test "alloc returns non-null" {
|
||||
const ptr = alloc(&lib_alloc.test_allocator, 16);
|
||||
try testing.expect(ptr != null);
|
||||
free(&lib_alloc.test_allocator, ptr, 16);
|
||||
}
|
||||
|
||||
test "alloc with null allocator" {
|
||||
const ptr = alloc(null, 8);
|
||||
try testing.expect(ptr != null);
|
||||
free(null, ptr, 8);
|
||||
}
|
||||
|
||||
test "alloc zero length" {
|
||||
const ptr = alloc(&lib_alloc.test_allocator, 0);
|
||||
defer free(&lib_alloc.test_allocator, ptr, 0);
|
||||
}
|
||||
|
||||
test "free null pointer" {
|
||||
|
|
@ -23,14 +54,14 @@ test "free null pointer" {
|
|||
}
|
||||
|
||||
test "free allocated memory" {
|
||||
const alloc = lib_alloc.default(&lib_alloc.test_allocator);
|
||||
const mem = try alloc.alloc(u8, 16);
|
||||
const allocator = lib_alloc.default(&lib_alloc.test_allocator);
|
||||
const mem = try allocator.alloc(u8, 16);
|
||||
free(&lib_alloc.test_allocator, mem.ptr, mem.len);
|
||||
}
|
||||
|
||||
test "free with null allocator" {
|
||||
// null allocator falls back to the default (test allocator in tests)
|
||||
const alloc = lib_alloc.default(null);
|
||||
const mem = try alloc.alloc(u8, 8);
|
||||
const allocator = lib_alloc.default(null);
|
||||
const mem = try allocator.alloc(u8, 8);
|
||||
free(null, mem.ptr, mem.len);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ pub const mouse_encoder_encode = mouse_encode.encode;
|
|||
|
||||
pub const paste_is_safe = paste.is_safe;
|
||||
|
||||
pub const alloc_alloc = allocator.alloc;
|
||||
pub const alloc_free = allocator.free;
|
||||
|
||||
pub const size_report_encode = size_report.encode;
|
||||
|
|
|
|||
Loading…
Reference in New Issue