sic

The sic programming language, compiler and tools (WIP)
Log | Files | Refs

arena.c (2268B)



#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "arena.h"
#include "check.h"

// not thread safe

struct Arena {
    uint8_t* buf;
    size_t cap;
    size_t pos;
};

Arena* arena_init(size_t bytes)
{
    Arena* a;
    if (!bytes || bytes == 0) {
        bytes = 1024;
    }
    a = malloc(sizeof(Arena));
    if (!a) {
        fprintf(stderr, "arena_init failed to alloc\n");
        return NULL;
    }
    a->buf = malloc(bytes);
    if (!a->buf) {
        free(a);
        fprintf(stderr, "arena_init: failed to allocate buffer\n");
        return NULL;
    }
    a->cap = bytes;
    a->pos = 0;

    return a;
}

static size_t align_to(size_t offset, size_t align)
{
    // align to a power of 2
    return (offset + align - 1) & ~(align - 1);
}

void* arena_alloc(Arena* a, size_t size, size_t align)
{
    size_t pos, new_cap;
    void* ptr;
    uint8_t* p;

    if (!a) {
        fprintf(stderr, "arena: no arena given to alloc to\n");
        return NULL;
    }
    if (!size) {
        fprintf(stderr, "arena: no arena size given to alloc to\n");
        return NULL;
    }
    if (!align) {
        fprintf(stderr, "arena: no arena alignment given to alloc to\n");
        return NULL;
    }
    if ((align & (align - 1)) != 0) { /* not power of two */
        fprintf(stderr, "arena: alignment must be a power of two\n");
        return NULL;
    }

    pos = align_to(a->pos, align);
    if (SIZE_MAX - pos < size) {
        fprintf(stderr, "arena_alloc: too large for available memory\n");
        free(a->buf);
        return NULL;
    }
    if (pos + size > a->cap) {
        new_cap = a->cap == 0 ? 1024 : a->cap * 2;
        check(new_cap <= a->cap || new_cap > SIZE_MAX / sizeof(uint8_t), "arena: could not reallocate arena, underflow or overflow\n");
        p = realloc(a->buf, new_cap);
        if (!p) {
            fprintf(stderr, "arena: could not reallocate double its size!\n");
            free(a->buf);
            return NULL;
        }
        a->buf = p;
        a->cap = new_cap;
    }

    ptr = a->buf + pos;
    a->pos = pos + size;
    return (void*)ptr;
}

void arena_reset(Arena* a)
{
    if (!a) return;
    a->pos = 0;
}

void arena_destroy(Arena* a)
{
    if (!a) return;
    free(a->buf);
    free(a);
}