1/**
2 * @file allocator.h
3 *
4 * Memory management interface for libghostty-vt.
5 */
6
7#ifndef GHOSTTY_VT_ALLOCATOR_H
8#define GHOSTTY_VT_ALLOCATOR_H
9
10#include <stdbool.h>
11#include <stddef.h>
12#include <stdint.h>
13#include <ghostty/vt/types.h>
14
15/** @defgroup allocator Memory Management
16 *
17 * libghostty-vt does require memory allocation for various operations,
18 * but is resilient to allocation failures and will gracefully handle
19 * out-of-memory situations by returning error codes.
20 *
21 * The exact memory management semantics are documented in the relevant
22 * functions and data structures.
23 *
24 * libghostty-vt uses explicit memory allocation via an allocator
25 * interface provided by GhosttyAllocator. The interface is based on the
26 * [Zig](https://ziglang.org) allocator interface, since this has been
27 * shown to be a flexible and powerful interface in practice and enables
28 * a wide variety of allocation strategies.
29 *
30 * **For the common case, you can pass NULL as the allocator for any
31 * function that accepts one,** and libghostty will use a default allocator.
32 * The default allocator will be libc malloc/free if libc is linked.
33 * Otherwise, a custom allocator is used (currently Zig's SMP allocator)
34 * that doesn't require any external dependencies.
35 *
36 * ## Basic Usage
37 *
38 * For simple use cases, you can ignore this interface entirely by passing NULL
39 * as the allocator parameter to functions that accept one. This will use the
40 * default allocator (typically libc malloc/free, if libc is linked, but
41 * we provide our own default allocator if libc isn't linked).
42 *
43 * To use a custom allocator:
44 * 1. Implement the GhosttyAllocatorVtable function pointers
45 * 2. Create a GhosttyAllocator struct with your vtable and context
46 * 3. Pass the allocator to functions that accept one
47 *
48 * ## Alloc/Free Helpers
49 *
50 * ghostty_alloc() and ghostty_free() provide a simple malloc/free-style
51 * interface for allocating and freeing byte buffers through the library's
52 * allocator. These are useful when:
53 *
54 * - You need to allocate a buffer to pass into a libghostty-vt function
55 * (e.g. preparing input data for ghostty_terminal_vt_write()).
56 * - You need to free a buffer returned by a libghostty-vt function
57 * (e.g. the output of ghostty_formatter_format_alloc()).
58 * - You are on a platform where the library's internal allocator differs
59 * from the consumer's C runtime (e.g. Windows, where Zig's libc and
60 * MSVC's CRT maintain separate heaps), so calling the standard C
61 * free() on library-allocated memory would be undefined behavior.
62 *
63 * Always use the same allocator (or NULL) for both the allocation and
64 * the corresponding free.
65 *
66 * @{
67 */
68
69/**
70 * Function table for custom memory allocator operations.
71 *
72 * This vtable defines the interface for a custom memory allocator. All
73 * function pointers must be valid and non-NULL.
74 *
75 * @ingroup allocator
76 *
77 * If you're not going to use a custom allocator, you can ignore all of
78 * this. All functions that take an allocator pointer allow NULL to use a
79 * default allocator.
80 *
81 * The interface is based on the Zig allocator interface. I'll say up front
82 * that it is easy to look at this interface and think "wow, this is really
83 * overcomplicated". The reason for this complexity is well thought out by
84 * the Zig folks, and it enables a diverse set of allocation strategies
85 * as shown by the Zig ecosystem. As a consolation, please note that many
86 * of the arguments are only needed for advanced use cases and can be
87 * safely ignored in simple implementations. For example, if you look at
88 * the Zig implementation of the libc allocator in `lib/std/heap.zig`
89 * (search for CAllocator), you'll see it is very simple.
90 *
91 * We chose to align with the Zig allocator interface because:
92 *
93 * 1. It is a proven interface that serves a wide variety of use cases
94 * in the real world via the Zig ecosystem. It's shown to work.
95 *
96 * 2. Our core implementation itself is Zig, and this lets us very
97 * cheaply and easily convert between C and Zig allocators.
98 *
99 * NOTE(mitchellh): In the future, we can have default implementations of
100 * resize/remap and allow those to be null.
101 */
102typedef struct {
103 /**
104 * Return a pointer to `len` bytes with specified `alignment`, or return
105 * `NULL` indicating the allocation failed.
106 *
107 * @param ctx The allocator context
108 * @param len Number of bytes to allocate
109 * @param alignment Required alignment for the allocation. Guaranteed to
110 * be a power of two between 1 and 16 inclusive.
111 * @param ret_addr First return address of the allocation call stack (0 if not provided)
112 * @return Pointer to allocated memory, or NULL if allocation failed
113 */
114 void* (*alloc)(void *ctx, size_t len, uint8_t alignment, uintptr_t ret_addr);
115
116 /**
117 * Attempt to expand or shrink memory in place.
118 *
119 * `memory_len` must equal the length requested from the most recent
120 * successful call to `alloc`, `resize`, or `remap`. `alignment` must
121 * equal the same value that was passed as the `alignment` parameter to
122 * the original `alloc` call.
123 *
124 * `new_len` must be greater than zero.
125 *
126 * @param ctx The allocator context
127 * @param memory Pointer to the memory block to resize
128 * @param memory_len Current size of the memory block
129 * @param alignment Alignment (must match original allocation)
130 * @param new_len New requested size
131 * @param ret_addr First return address of the allocation call stack (0 if not provided)
132 * @return true if resize was successful in-place, false if relocation would be required
133 */
134 bool (*resize)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, size_t new_len, uintptr_t ret_addr);
135
136 /**
137 * Attempt to expand or shrink memory, allowing relocation.
138 *
139 * `memory_len` must equal the length requested from the most recent
140 * successful call to `alloc`, `resize`, or `remap`. `alignment` must
141 * equal the same value that was passed as the `alignment` parameter to
142 * the original `alloc` call.
143 *
144 * A non-`NULL` return value indicates the resize was successful. The
145 * allocation may have same address, or may have been relocated. In either
146 * case, the allocation now has size of `new_len`. A `NULL` return value
147 * indicates that the resize would be equivalent to allocating new memory,
148 * copying the bytes from the old memory, and then freeing the old memory.
149 * In such case, it is more efficient for the caller to perform the copy.
150 *
151 * `new_len` must be greater than zero.
152 *
153 * @param ctx The allocator context
154 * @param memory Pointer to the memory block to remap
155 * @param memory_len Current size of the memory block
156 * @param alignment Alignment (must match original allocation)
157 * @param new_len New requested size
158 * @param ret_addr First return address of the allocation call stack (0 if not provided)
159 * @return Pointer to resized memory (may be relocated), or NULL if manual copy is needed
160 */
161 void* (*remap)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, size_t new_len, uintptr_t ret_addr);
162
163 /**
164 * Free and invalidate a region of memory.
165 *
166 * `memory_len` must equal the length requested from the most recent
167 * successful call to `alloc`, `resize`, or `remap`. `alignment` must
168 * equal the same value that was passed as the `alignment` parameter to
169 * the original `alloc` call.
170 *
171 * @param ctx The allocator context
172 * @param memory Pointer to the memory block to free
173 * @param memory_len Size of the memory block
174 * @param alignment Alignment (must match original allocation)
175 * @param ret_addr First return address of the allocation call stack (0 if not provided)
176 */
177 void (*free)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, uintptr_t ret_addr);
178} GhosttyAllocatorVtable;
179
180/**
181 * Custom memory allocator.
182 *
183 * For functions that take an allocator pointer, a NULL pointer indicates
184 * that the default allocator should be used. The default allocator will
185 * be libc malloc/free if we're linking to libc. If libc isn't linked,
186 * a custom allocator is used (currently Zig's SMP allocator).
187 *
188 * @ingroup allocator
189 *
190 * Usage example:
191 * @code
192 * GhosttyAllocator allocator = {
193 * .vtable = &my_allocator_vtable,
194 * .ctx = my_allocator_state
195 * };
196 * @endcode
197 */
198typedef struct GhosttyAllocator {
199 /**
200 * Opaque context pointer passed to all vtable functions.
201 * This allows the allocator implementation to maintain state
202 * or reference external resources needed for memory management.
203 */
204 void *ctx;
205
206 /**
207 * Pointer to the allocator's vtable containing function pointers
208 * for memory operations (alloc, resize, remap, free).
209 */
210 const GhosttyAllocatorVtable *vtable;
211} GhosttyAllocator;
212
213/**
214 * Allocate a buffer of `len` bytes.
215 *
216 * Uses the provided allocator, or the default allocator if NULL is passed.
217 * The returned buffer must be freed with ghostty_free() using the same
218 * allocator.
219 *
220 * @param allocator Pointer to the allocator to use, or NULL for the default
221 * @param len Number of bytes to allocate
222 * @return Pointer to the allocated buffer, or NULL if allocation failed
223 *
224 * @ingroup allocator
225 */
226GHOSTTY_API uint8_t* ghostty_alloc(const GhosttyAllocator* allocator, size_t len);
227
228/**
229 * Free memory that was allocated by a libghostty-vt function.
230 *
231 * Use this to free buffers returned by functions such as
232 * ghostty_formatter_format_alloc(). Pass the same allocator that was
233 * used for the allocation, or NULL if the default allocator was used.
234 *
235 * On platforms where the library's internal allocator differs from the
236 * consumer's C runtime (e.g. Windows, where Zig's libc and MSVC's CRT
237 * maintain separate heaps), calling the standard C free() on memory
238 * allocated by the library causes undefined behavior. This function
239 * guarantees the correct allocator is used regardless of platform.
240 *
241 * It is safe to pass a NULL pointer; the call is a no-op in that case.
242 *
243 * @param allocator Pointer to the allocator that was used to allocate the
244 * memory, or NULL if the default allocator was used
245 * @param ptr Pointer to the memory to free (may be NULL)
246 * @param len Length of the allocation in bytes (must match the original
247 * allocation size)
248 *
249 * @ingroup allocator
250 */
251GHOSTTY_API void ghostty_free(const GhosttyAllocator* allocator, uint8_t* ptr, size_t len);
252
253/** @} */
254
255#endif /* GHOSTTY_VT_ALLOCATOR_H */