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 )