Affix
view release on metacpan or search on metacpan
infix/src/core/arena.c view on Meta::CPAN
* @param initial_size The number of bytes for the initial backing buffer. A larger
* size can reduce the chance of reallocation for complex types.
* @return A pointer to the new `infix_arena_t`, or `nullptr` on failure.
*/
INFIX_API c23_nodiscard infix_arena_t * infix_arena_create(size_t initial_size) {
// Use calloc to ensure the initial struct state is zeroed.
infix_arena_t * arena = infix_calloc(1, sizeof(infix_arena_t));
if (arena == nullptr) {
_infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
return nullptr;
}
arena->buffer = infix_calloc(1, initial_size);
if (arena->buffer == nullptr && initial_size > 0) {
infix_free(arena);
_infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
return nullptr;
}
arena->capacity = initial_size;
arena->current_offset = 0;
arena->error = false;
arena->next_block = nullptr;
arena->block_size = initial_size;
return arena;
}
/**
* @internal
* @brief Destroys an arena and frees all memory associated with it.
*
* This function frees the arena's single backing buffer and the `infix_arena_t`
* struct itself. Any pointers returned by `infix_arena_alloc` from this arena
* become invalid after this call. It is safe to call this function with a
* `nullptr` argument.
*
* @param arena A pointer to the arena to destroy.
*/
INFIX_API void infix_arena_destroy(infix_arena_t * arena) {
if (arena == nullptr)
return;
// Traverse the chain of blocks and free each one.
infix_arena_t * current = arena;
while (current != nullptr) {
infix_arena_t * next = current->next_block;
if (current->buffer)
infix_free(current->buffer);
infix_free(current);
current = next;
}
}
/**
* @internal
* @brief Allocates a block of memory from an arena with a specified alignment.
*
* This is a "bump" allocator. It calculates the next memory address that satisfies
* the requested alignment, checks if there is sufficient capacity in the arena's
* buffer, and if so, "bumps" the `current_offset` pointer and returns the address.
*
* This operation is extremely fast as it involves no system calls, only simple
* integer and pointer arithmetic.
*
* If an allocation fails (due to insufficient space or invalid arguments), the
* arena's `error` flag is set, a detailed error is reported, and all subsequent
* allocations from this arena will also fail.
*
* @param arena The arena to allocate from.
* @param size The number of bytes to allocate.
* @param alignment The required alignment for the allocation. Must be a power of two.
* @return A pointer to the allocated memory, or `nullptr` if the arena is out of
* memory, has its error flag set, or an invalid alignment is requested.
*/
INFIX_API c23_nodiscard void * infix_arena_alloc(infix_arena_t * arena, size_t size, size_t alignment) {
if (arena == nullptr)
return nullptr;
// Ensure alignment is power of 2
if (alignment == 0 || (alignment & (alignment - 1)) != 0) {
arena->error = true;
_infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_INVALID_ALIGNMENT, 0);
return nullptr;
}
infix_arena_t * block = arena;
while (true) {
if (block->error)
return nullptr;
// Calculate current absolute address
uintptr_t current_ptr = (uintptr_t)(block->buffer + block->current_offset);
// Calculate aligned address
// (x + align - 1) & ~(align - 1)
uintptr_t aligned_ptr = (current_ptr + (alignment - 1)) & ~(alignment - 1);
// Calculate padding needed
size_t padding = (size_t)(aligned_ptr - current_ptr);
// Calculate total space required in this block
size_t total_needed = size + padding;
// Check if fits in current block
if (block->current_offset + total_needed <= block->capacity) {
void * ret = (void *)aligned_ptr;
block->current_offset += total_needed;
return ret;
}
// Allocation failed in current block. Check next or create new.
if (block->next_block) {
block = block->next_block;
continue;
}
// Create new block. Ensure it's large enough for alignment + size.
size_t next_cap = block->block_size * 2;
if (next_cap < size + alignment)
next_cap = size + alignment;
block->next_block = infix_arena_create(next_cap);
if (!block->next_block) {
block->error = true;
return nullptr;
}
block = block->next_block;
}
}
/**
* @internal
* @brief Allocates and zero-initializes a block of memory from an arena.
( run in 2.783 seconds using v1.01-cache-2.11-cpan-cdf2f3d4e48 )