#ifndef SAND_ALLOCATOR_H #define SAND_ALLOCATOR_H // This module defines a generic allocator interface. All allocations in this // family of programs happen through this allocation interface. // // As references to `SandAllocator`s are passed all around the program, the // convention adopted here is to allow single-variable (mainly `a`) names for // these. #include #include typedef struct { // Shall return a new piece of writable and readable memory of size `size` and with alignment `alignment`. // This function may return `NULL` to indicate out-of-memory. // Caller promises to call `deallocate` or `reallocate` on the returned pointer. void *(* allocate)(size_t size, size_t alignment, void *user_data); // Shall the allocation made at `old_ptr`. // Unlike with `realloc(3)`, `old_ptr` is not nullable. // This operation is assumed to be infallible, as there is no reasonable way to recover. // FIXME: This should take alignment as well. void (* deallocate)(void *old_ptr, size_t old_size, void *user_data); // Resize the allocation at `old_ptr` from `old_size` to `new_size`. // `alignment` is guaranteed to be the same as passed to the original call to `allocate`. // `old_ptr` and `old_size` are guaranteed to be from the last successful invocation of `allocate` or `reallocate`. // This function may return `NULL` to indicate out-of-memory, however `old_ptr` should still remain valid in that case. // The struct member itself may be `NULL`, in which case a default implementation is used. void *(* reallocate)(void *old_ptr, size_t old_size, size_t new_size, size_t alignment, void *user_data); // Custom data which is passed to every function in the interface. The // allocator can use this for extra metadata it needs to keep. void *user_data; } SandAllocator; // Allocates a chunk of memory with the given size and alignment. // Returns `NULL` on failure. // Caller assumes ownership of returned pointer, must free with `sand_deallocate`. void *sand_allocate_aligned(SandAllocator *, size_t size, size_t alignment); // Same as `sand_allocate_aligned` but with default alignment. void *sand_allocate(SandAllocator *, size_t size); // Resize the allocation at `old_ptr` from `old_size` to `new_size`. // `alignment` is guaranteed to be the same as passed to the original call to `allocate`. // Returns a new pointer or `NULL` if reallocation fails. // If `NULL` is returned, `old_ptr` remains valid. // Caller assumes ownership of returned pointer (or retains ownership of old pointer). void *sand_reallocate_aligned(SandAllocator *, void *old_ptr, size_t old_size, size_t new_size, size_t alignment); // Same as `sand_reallocate_aligned` but with default alignment. void *sand_reallocate(SandAllocator *, void *old_ptr, size_t old_size, size_t new_size); // Frees `old_ptr`. // `old_ptr` is nullable, in which case `old_size` is ignored. // `old_size` must match the size passed to last call to `sand_allocate` or `sand_reallocate`. void sand_deallocate(SandAllocator *, void *old_ptr, size_t old_size); // The following are only intended to be used by allocator implementations. bool sand_is_valid_alignment(size_t alignment); bool sand_pointer_is_aligned(void *pointer, size_t alignment); void *sand_align_pointer_forward(void *ptr, size_t alignment); bool sand_size_is_aligned(size_t n, size_t alignment); size_t sand_align_size_forward(size_t n, size_t alignment); #endif