255 lines · 10.4 KB · C
RawDownload
/** * @file allocator.h * * Memory management interface for libghostty-vt. */ #ifndef GHOSTTY_VT_ALLOCATOR_H#define GHOSTTY_VT_ALLOCATOR_H #include <stdbool.h>#include <stddef.h>#include <stdint.h>#include <ghostty/vt/types.h> /** @defgroup allocator Memory Management * * libghostty-vt does require memory allocation for various operations, * but is resilient to allocation failures and will gracefully handle * out-of-memory situations by returning error codes. * * The exact memory management semantics are documented in the relevant * functions and data structures. * * libghostty-vt uses explicit memory allocation via an allocator * interface provided by GhosttyAllocator. The interface is based on the * [Zig](https://ziglang.org) allocator interface, since this has been * shown to be a flexible and powerful interface in practice and enables * a wide variety of allocation strategies. * * **For the common case, you can pass NULL as the allocator for any * function that accepts one,** and libghostty will use a default allocator. * The default allocator will be libc malloc/free if libc is linked.  * Otherwise, a custom allocator is used (currently Zig's SMP allocator) * that doesn't require any external dependencies. * * ## Basic Usage * * For simple use cases, you can ignore this interface entirely by passing NULL * as the allocator parameter to functions that accept one. This will use the * default allocator (typically libc malloc/free, if libc is linked, but * we provide our own default allocator if libc isn't linked). * * To use a custom allocator: * 1. Implement the GhosttyAllocatorVtable function pointers * 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. * * @{ */ /** * Function table for custom memory allocator operations. *  * This vtable defines the interface for a custom memory allocator. All * function pointers must be valid and non-NULL. * * @ingroup allocator * * If you're not going to use a custom allocator, you can ignore all of * this. All functions that take an allocator pointer allow NULL to use a * default allocator. * * The interface is based on the Zig allocator interface. I'll say up front * that it is easy to look at this interface and think "wow, this is really * overcomplicated". The reason for this complexity is well thought out by * the Zig folks, and it enables a diverse set of allocation strategies * as shown by the Zig ecosystem. As a consolation, please note that many * of the arguments are only needed for advanced use cases and can be * safely ignored in simple implementations. For example, if you look at  * the Zig implementation of the libc allocator in `lib/std/heap.zig` * (search for CAllocator), you'll see it is very simple. * * We chose to align with the Zig allocator interface because: * *   1. It is a proven interface that serves a wide variety of use cases *      in the real world via the Zig ecosystem. It's shown to work. * *   2. Our core implementation itself is Zig, and this lets us very *      cheaply and easily convert between C and Zig allocators. * * NOTE(mitchellh): In the future, we can have default implementations of * resize/remap and allow those to be null. */typedef struct {    /**     * Return a pointer to `len` bytes with specified `alignment`, or return     * `NULL` indicating the allocation failed.     *     * @param ctx The allocator context     * @param len Number of bytes to allocate     * @param alignment Required alignment for the allocation. Guaranteed to     *   be a power of two between 1 and 16 inclusive.     * @param ret_addr First return address of the allocation call stack (0 if not provided)     * @return Pointer to allocated memory, or NULL if allocation failed     */    void* (*alloc)(void *ctx, size_t len, uint8_t alignment, uintptr_t ret_addr);        /**     * Attempt to expand or shrink memory in place.     *     * `memory_len` must equal the length requested from the most recent     * successful call to `alloc`, `resize`, or `remap`. `alignment` must     * equal the same value that was passed as the `alignment` parameter to     * the original `alloc` call.     *     * `new_len` must be greater than zero.     *     * @param ctx The allocator context     * @param memory Pointer to the memory block to resize     * @param memory_len Current size of the memory block     * @param alignment Alignment (must match original allocation)     * @param new_len New requested size     * @param ret_addr First return address of the allocation call stack (0 if not provided)     * @return true if resize was successful in-place, false if relocation would be required     */    bool (*resize)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, size_t new_len, uintptr_t ret_addr);        /**     * Attempt to expand or shrink memory, allowing relocation.     *     * `memory_len` must equal the length requested from the most recent     * successful call to `alloc`, `resize`, or `remap`. `alignment` must     * equal the same value that was passed as the `alignment` parameter to     * the original `alloc` call.     *     * A non-`NULL` return value indicates the resize was successful. The     * allocation may have same address, or may have been relocated. In either     * case, the allocation now has size of `new_len`. A `NULL` return value     * indicates that the resize would be equivalent to allocating new memory,     * copying the bytes from the old memory, and then freeing the old memory.     * In such case, it is more efficient for the caller to perform the copy.     *     * `new_len` must be greater than zero.     *     * @param ctx The allocator context     * @param memory Pointer to the memory block to remap     * @param memory_len Current size of the memory block     * @param alignment Alignment (must match original allocation)     * @param new_len New requested size     * @param ret_addr First return address of the allocation call stack (0 if not provided)     * @return Pointer to resized memory (may be relocated), or NULL if manual copy is needed     */    void* (*remap)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, size_t new_len, uintptr_t ret_addr);        /**     * Free and invalidate a region of memory.     *     * `memory_len` must equal the length requested from the most recent     * successful call to `alloc`, `resize`, or `remap`. `alignment` must     * equal the same value that was passed as the `alignment` parameter to     * the original `alloc` call.     *     * @param ctx The allocator context     * @param memory Pointer to the memory block to free     * @param memory_len Size of the memory block     * @param alignment Alignment (must match original allocation)     * @param ret_addr First return address of the allocation call stack (0 if not provided)     */    void (*free)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, uintptr_t ret_addr);} GhosttyAllocatorVtable; /** * Custom memory allocator. * * For functions that take an allocator pointer, a NULL pointer indicates * that the default allocator should be used. The default allocator will  * be libc malloc/free if we're linking to libc. If libc isn't linked, * a custom allocator is used (currently Zig's SMP allocator). * * @ingroup allocator * * Usage example: * @code * GhosttyAllocator allocator = { *     .vtable = &my_allocator_vtable, *     .ctx = my_allocator_state * }; * @endcode */typedef struct GhosttyAllocator {    /**     * Opaque context pointer passed to all vtable functions.     * This allows the allocator implementation to maintain state     * or reference external resources needed for memory management.     */    void *ctx;     /**     * Pointer to the allocator's vtable containing function pointers     * for memory operations (alloc, resize, remap, free).     */    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 */GHOSTTY_API uint8_t* ghostty_alloc(const GhosttyAllocator* allocator, size_t len); /** * 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 */GHOSTTY_API void ghostty_free(const GhosttyAllocator* allocator, uint8_t* ptr, size_t len); /** @} */ #endif /* GHOSTTY_VT_ALLOCATOR_H */