libghostty: add ghostty_free for cross-runtime memory safety
On Windows, Zig's built-in libc and MSVC's CRT maintain separate heaps, so calling free() on memory allocated by the library causes undefined behavior. Add ghostty_free() that frees through the same allocator that performed the allocation, making it safe on all platforms. Update format_alloc docs and all examples to use ghostty_free() instead of free(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>pull/11785/head
parent
1213dacd5b
commit
c1e616c6cd
|
|
@ -45,7 +45,7 @@ int main() {
|
||||||
fwrite(buf, 1, len, stdout);
|
fwrite(buf, 1, len, stdout);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
free(buf);
|
ghostty_free(NULL, buf, len);
|
||||||
ghostty_formatter_free(formatter);
|
ghostty_formatter_free(formatter);
|
||||||
ghostty_terminal_free(terminal);
|
ghostty_terminal_free(terminal);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ int main() {
|
||||||
fwrite(buf, 1, len, stdout);
|
fwrite(buf, 1, len, stdout);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
free(buf);
|
ghostty_free(NULL, buf, len);
|
||||||
ghostty_formatter_free(formatter);
|
ghostty_formatter_free(formatter);
|
||||||
ghostty_terminal_free(terminal);
|
ghostty_terminal_free(terminal);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ int main() {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
free(buf);
|
ghostty_free(NULL, buf, len);
|
||||||
ghostty_formatter_free(formatter);
|
ghostty_formatter_free(formatter);
|
||||||
ghostty_terminal_free(terminal);
|
ghostty_terminal_free(terminal);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,31 @@ typedef struct GhosttyAllocator {
|
||||||
const GhosttyAllocatorVtable *vtable;
|
const GhosttyAllocatorVtable *vtable;
|
||||||
} GhosttyAllocator;
|
} GhosttyAllocator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free memory that was allocated by a libghostty-vt function.
|
||||||
|
*
|
||||||
|
* Use this to free buffers returned by functions such as
|
||||||
|
* ghostty_formatter_format_alloc(). Pass the same allocator that was
|
||||||
|
* used for the allocation, or NULL if the default allocator was used.
|
||||||
|
*
|
||||||
|
* On platforms 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), calling the standard C free() on memory
|
||||||
|
* allocated by the library causes undefined behavior. This function
|
||||||
|
* guarantees the correct allocator is used regardless of platform.
|
||||||
|
*
|
||||||
|
* It is safe to pass a NULL pointer; the call is a no-op in that case.
|
||||||
|
*
|
||||||
|
* @param allocator Pointer to the allocator that was used to allocate the
|
||||||
|
* memory, or NULL if the default allocator was used
|
||||||
|
* @param ptr Pointer to the memory to free (may be NULL)
|
||||||
|
* @param len Length of the allocation in bytes (must match the original
|
||||||
|
* allocation size)
|
||||||
|
*
|
||||||
|
* @ingroup allocator
|
||||||
|
*/
|
||||||
|
void ghostty_free(const GhosttyAllocator* allocator, uint8_t* ptr, size_t len);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
#endif /* GHOSTTY_VT_ALLOCATOR_H */
|
#endif /* GHOSTTY_VT_ALLOCATOR_H */
|
||||||
|
|
|
||||||
|
|
@ -186,10 +186,9 @@ GhosttyResult ghostty_formatter_format_buf(GhosttyFormatter formatter,
|
||||||
*
|
*
|
||||||
* Each call formats the current terminal state. The buffer is allocated
|
* Each call formats the current terminal state. The buffer is allocated
|
||||||
* using the provided allocator (or the default allocator if NULL).
|
* using the provided allocator (or the default allocator if NULL).
|
||||||
* The caller is responsible for freeing the returned buffer. When using
|
* The caller is responsible for freeing the returned buffer with
|
||||||
* the default allocator (NULL), the buffer can be freed with `free()`.
|
* ghostty_free(), passing the same allocator (or NULL for the default)
|
||||||
* When using a custom allocator, the buffer must be freed using the
|
* that was used for the allocation.
|
||||||
* same allocator.
|
|
||||||
*
|
*
|
||||||
* @param formatter The formatter handle (must not be NULL)
|
* @param formatter The formatter handle (must not be NULL)
|
||||||
* @param allocator Pointer to allocator, or NULL to use the default allocator
|
* @param allocator Pointer to allocator, or NULL to use the default allocator
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,7 @@ comptime {
|
||||||
@export(&c.grid_ref_graphemes, .{ .name = "ghostty_grid_ref_graphemes" });
|
@export(&c.grid_ref_graphemes, .{ .name = "ghostty_grid_ref_graphemes" });
|
||||||
@export(&c.grid_ref_style, .{ .name = "ghostty_grid_ref_style" });
|
@export(&c.grid_ref_style, .{ .name = "ghostty_grid_ref_style" });
|
||||||
@export(&c.build_info, .{ .name = "ghostty_build_info" });
|
@export(&c.build_info, .{ .name = "ghostty_build_info" });
|
||||||
|
@export(&c.free_alloc, .{ .name = "ghostty_free" });
|
||||||
|
|
||||||
// On Wasm we need to export our allocator convenience functions.
|
// On Wasm we need to export our allocator convenience functions.
|
||||||
if (builtin.target.cpu.arch.isWasm()) {
|
if (builtin.target.cpu.arch.isWasm()) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
const lib_alloc = @import("../../lib/allocator.zig");
|
||||||
|
const CAllocator = lib_alloc.Allocator;
|
||||||
|
|
||||||
const buildpkg = @import("build_info.zig");
|
const buildpkg = @import("build_info.zig");
|
||||||
pub const cell = @import("cell.zig");
|
pub const cell = @import("cell.zig");
|
||||||
pub const color = @import("color.zig");
|
pub const color = @import("color.zig");
|
||||||
|
|
@ -112,6 +115,21 @@ pub const mouse_encoder_encode = mouse_encode.encode;
|
||||||
|
|
||||||
pub const paste_is_safe = paste.is_safe;
|
pub const paste_is_safe = paste.is_safe;
|
||||||
|
|
||||||
|
/// Free memory that was allocated by a libghostty-vt function.
|
||||||
|
///
|
||||||
|
/// This must be used to free buffers returned by functions like
|
||||||
|
/// `format_alloc`. Pass the same allocator (or NULL for the default)
|
||||||
|
/// that was used for the allocation.
|
||||||
|
pub fn free_alloc(
|
||||||
|
alloc_: ?*const CAllocator,
|
||||||
|
ptr: ?[*]u8,
|
||||||
|
len: usize,
|
||||||
|
) callconv(.c) void {
|
||||||
|
const mem = ptr orelse return;
|
||||||
|
const alloc = lib_alloc.default(alloc_);
|
||||||
|
alloc.free(mem[0..len]);
|
||||||
|
}
|
||||||
|
|
||||||
pub const size_report_encode = size_report.encode;
|
pub const size_report_encode = size_report.encode;
|
||||||
|
|
||||||
pub const cell_get = cell.get;
|
pub const cell_get = cell.get;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue