Is to make it more consistent, i need to change also later arraylist the init function so its also more consistent, next is making alloc ensure capacity first so no using it first or some shit.
209 lines
5.0 KiB
C
209 lines
5.0 KiB
C
#include "arena.h"
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
struct Arena {
|
|
uint8_t *buffer;
|
|
size_t capacity;
|
|
size_t offset;
|
|
};
|
|
|
|
|
|
ArenaResult arena_init(size_t capacity) {
|
|
if (capacity < 1) {
|
|
return (ArenaResult) {.is_valid = false, .err = ARENA_INVALID_SIZE};
|
|
}
|
|
|
|
void *buffer = malloc(capacity);
|
|
if (buffer == NULL) {
|
|
return (ArenaResult) {.is_valid = false, .err = ARENA_BAD_ALLOC};
|
|
}
|
|
|
|
Arena new_arena = {
|
|
.buffer = buffer,
|
|
.capacity = capacity,
|
|
.offset = 0,
|
|
};
|
|
|
|
return (ArenaResult) {.is_valid = true, .arena = new_arena};
|
|
}
|
|
|
|
void arena_destroy(Arena *arena) {
|
|
free(arena->buffer);
|
|
arena->buffer = NULL;
|
|
arena->offset = 0;
|
|
arena->capacity = 0;
|
|
}
|
|
|
|
ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) {
|
|
if (arena == NULL) {
|
|
return (ArenaPointer) {.is_valid = false, .err = ARENA_NULL_ARG};
|
|
}
|
|
|
|
if (size < 1) {
|
|
return (ArenaPointer) {.is_valid = false, .err = ARENA_INVALID_SIZE};
|
|
}
|
|
|
|
if (alignment < 1) {
|
|
return (ArenaPointer) {.is_valid = false, .err = ARENA_INVALID_ALIGN};
|
|
}
|
|
|
|
ArenaSizeResult padding = arena_get_align_padding(arena, alignment);
|
|
if (!padding.is_valid) {
|
|
return (ArenaPointer) {.is_valid = false, .err = padding.err};
|
|
}
|
|
|
|
if (arena->offset + padding.val + size > arena->capacity) {
|
|
return (ArenaPointer) {.is_valid = false, .err = ARENA_OUT_OF_SPACE};
|
|
}
|
|
|
|
if (arena->offset > SIZE_MAX - padding.val - size) {
|
|
return (ArenaPointer) {.is_valid = false, .err = ARENA_CAPACITY_OVERFLOW};
|
|
}
|
|
|
|
size_t aligned_offset = arena->offset + padding.val;
|
|
|
|
arena->offset = aligned_offset + size;
|
|
return (ArenaPointer) {.is_valid = true, .offset = aligned_offset, .arena = arena};
|
|
}
|
|
|
|
ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment) {
|
|
if (arena == NULL) {
|
|
return ARENA_NULL_ARG;
|
|
}
|
|
|
|
if (size == 0 || size == SIZE_MAX) {
|
|
return ARENA_INVALID_SIZE;
|
|
}
|
|
|
|
if (alignment == 0 || (alignment & (alignment - 1)) != 0) {
|
|
return ARENA_INVALID_ALIGN;
|
|
}
|
|
|
|
ArenaSizeResult padding_res = arena_get_align_padding(arena, alignment);
|
|
if (!padding_res.is_valid) {
|
|
return padding_res.err;
|
|
}
|
|
|
|
size_t padding = padding_res.val;
|
|
|
|
while (true) {
|
|
size_t required;
|
|
|
|
if (arena->offset > SIZE_MAX - padding) {
|
|
return ARENA_CAPACITY_OVERFLOW;
|
|
}
|
|
required = arena->offset + padding;
|
|
|
|
if (required > SIZE_MAX - size) {
|
|
return ARENA_CAPACITY_OVERFLOW;
|
|
}
|
|
required += size;
|
|
|
|
if (required <= arena->capacity) {
|
|
return ARENA_OK;
|
|
}
|
|
|
|
size_t new_capacity;
|
|
|
|
if (arena->capacity > SIZE_MAX / 2) {
|
|
return ARENA_CAPACITY_OVERFLOW;
|
|
}
|
|
|
|
new_capacity = arena->capacity * 2;
|
|
|
|
if (new_capacity < required) {
|
|
new_capacity = required;
|
|
}
|
|
|
|
ArenaErr err = arena_realloc(arena, new_capacity);
|
|
if (err != ARENA_OK) {
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
|
|
ArenaPointer arena_push(Arena *arena, void *data, size_t size, size_t alignment) {
|
|
if (arena == NULL || data == NULL) {
|
|
return (ArenaPointer) {.is_valid = false, .err = ARENA_NULL_ARG};
|
|
}
|
|
|
|
if (size < 1) {
|
|
return (ArenaPointer) {.is_valid = false, .err = ARENA_INVALID_SIZE};
|
|
}
|
|
|
|
if (size < 1) {
|
|
return (ArenaPointer) {.is_valid = false, .err = ARENA_INVALID_ALIGN};
|
|
}
|
|
|
|
ArenaErr err = arena_ensure_capacity(arena, size, alignment);
|
|
if (err != ARENA_OK) {
|
|
return (ArenaPointer) {.is_valid = false, .err = err};
|
|
}
|
|
|
|
ArenaPointer pointer = arena_alloc(arena, size, alignment);
|
|
if (!pointer.is_valid) {
|
|
return pointer;
|
|
}
|
|
|
|
memcpy(
|
|
arena_unwrap_pointer(pointer),
|
|
data,
|
|
size);
|
|
|
|
return pointer;
|
|
}
|
|
|
|
ArenaErr arena_realloc(Arena *arena, size_t new_capacity) {
|
|
if (arena == NULL) {
|
|
return ARENA_NULL_ARG;
|
|
}
|
|
|
|
if (new_capacity < 1) {
|
|
return ARENA_INVALID_SIZE;
|
|
}
|
|
|
|
uint8_t *tmp = realloc(arena->buffer, new_capacity);
|
|
if (tmp == NULL) {
|
|
return ARENA_BAD_ALLOC;
|
|
}
|
|
|
|
arena->buffer = tmp;
|
|
arena->capacity = new_capacity;
|
|
return ARENA_OK;
|
|
}
|
|
|
|
|
|
|
|
ArenaSizeResult arena_get_align_padding(Arena *arena, size_t alignment) {
|
|
if (arena == NULL) {
|
|
return (ArenaSizeResult){ .is_valid = false, .err = ARENA_NULL_ARG };
|
|
}
|
|
|
|
if (alignment == 0 || (alignment & (alignment - 1)) != 0) {
|
|
return (ArenaSizeResult){ .is_valid = false, .err = ARENA_INVALID_ALIGN };
|
|
}
|
|
|
|
uintptr_t current_address = (uintptr_t)(arena->buffer + arena->offset);
|
|
|
|
size_t mask = alignment - 1;
|
|
size_t padding = (size_t)(-current_address) & mask;
|
|
|
|
return (ArenaSizeResult){
|
|
.is_valid = true,
|
|
.val = padding
|
|
};
|
|
}
|
|
|
|
void *arena_unwrap_pointer(ArenaPointer p) {
|
|
if (!p.is_valid) {
|
|
return NULL;
|
|
}
|
|
return p.arena->buffer + p.offset;
|
|
}
|
|
|
|
|