Compare commits

...

2 Commits

3 changed files with 126 additions and 31 deletions

View File

@@ -58,10 +58,10 @@ ArenaErr arena_realloc(Arena *arena, size_t new_capacity);
void *arena_unwrap_pointer(ArenaPointer p);
SizeResult arena_get_align_padding(Arena *arena, size_t alignment);
// can take big sizes but of sourse there is a limit,
// its protected against SIZE_MAX but anything that
// is big may break it, still, is an edge case that
// I shouldn't cover.
ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment);
// Should be moved to something like general utilities,
// i should make one for all my c projects
bool mul_size_t_safe(size_t a, size_t b, size_t *out);
#endif // !ARENA_H

View File

@@ -70,34 +70,50 @@ ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment) {
return ARENA_NULL_ARG;
}
if (size < 1) {
if (size == 0 || size == SIZE_MAX) {
return ARENA_INVALID_SIZE;
}
if (alignment < 1) {
if (alignment == 0 || (alignment & (alignment - 1)) != 0) {
return ARENA_INVALID_ALIGN;
}
SizeResult padding = arena_get_align_padding(arena, alignment);
if (!padding.is_valid) {
return padding.err;
}
while (true) {
if (arena->offset > SIZE_MAX - padding.val - size) {
return ARENA_CAPACITY_OVERFLOW;
SizeResult padding_res = arena_get_align_padding(arena, alignment);
if (!padding_res.is_valid) {
return padding_res.err;
}
size_t required = arena->offset + padding.val + size;
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 (mul_size_t_safe(arena->capacity, 2, &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;
@@ -162,14 +178,19 @@ SizeResult arena_get_align_padding(Arena *arena, size_t alignment) {
return (SizeResult){ .is_valid = false, .err = ARENA_NULL_ARG };
}
if (alignment < 1) {
if (alignment == 0 || (alignment & (alignment - 1)) != 0) {
return (SizeResult){ .is_valid = false, .err = ARENA_INVALID_ALIGN };
}
uintptr_t current_address = (uintptr_t)(arena->buffer + arena->offset);
uintptr_t aligned = ((current_address + (alignment - 1)) & ~(alignment - 1));
return (SizeResult) {.is_valid = true, .err = aligned - current_address};
size_t mask = alignment - 1;
size_t padding = (size_t)(-current_address) & mask;
return (SizeResult){
.is_valid = true,
.val = padding
};
}
void *arena_unwrap_pointer(ArenaPointer p) {
@@ -179,16 +200,4 @@ void *arena_unwrap_pointer(ArenaPointer p) {
return p.arena->buffer + p.offset;
}
bool mul_size_t_safe(size_t a, size_t b, size_t *out) {
if (out == NULL) {
return true;
}
if (a != 0 && b > SIZE_MAX / a) {
return true;
}
*out = a * b;
return false;
}

View File

@@ -108,6 +108,34 @@ static void test_arena_alloc_size_max(void **state) {
arena_destroy(&arena);
}
static void test_arena_alloc_align_0(void **state) {
(void) state;
ArenaResult value = arena_init(64);
assert_true(value.is_valid);
Arena arena = value.arena;
ArenaPointer pointer = arena_alloc(&arena, sizeof(int), 0);
assert_false(pointer.is_valid);
assert_int_equal(pointer.err, ARENA_INVALID_ALIGN);
arena_destroy(&arena);
}
static void test_arena_alloc_align_max(void **state) {
(void) state;
ArenaResult value = arena_init(64);
assert_true(value.is_valid);
Arena arena = value.arena;
ArenaPointer pointer = arena_alloc(&arena, sizeof(int), SIZE_MAX);
assert_false(pointer.is_valid);
assert_int_equal(pointer.err, ARENA_INVALID_ALIGN);
arena_destroy(&arena);
}
static void test_arena_align_size_0(void **state) {
(void) state;
@@ -122,6 +150,58 @@ static void test_arena_align_size_0(void **state) {
arena_destroy(&arena);
}
static void test_arena_ensure_capacity_size_0(void **state) {
(void) state;
ArenaResult value = arena_init(64);
assert_true(value.is_valid);
Arena arena = value.arena;
ArenaErr err = arena_ensure_capacity(&arena, 0, alignof(int));
assert_int_equal(err, ARENA_INVALID_SIZE);
arena_destroy(&arena);
}
static void test_arena_ensure_capacity_size_max(void **state) {
(void) state;
ArenaResult value = arena_init(64);
assert_true(value.is_valid);
Arena arena = value.arena;
ArenaErr err = arena_ensure_capacity(&arena, SIZE_MAX, alignof(int));
assert_int_equal(err, ARENA_INVALID_SIZE);
arena_destroy(&arena);
}
static void test_arena_ensure_capacity_align_0(void **state) {
(void) state;
ArenaResult value = arena_init(64);
assert_true(value.is_valid);
Arena arena = value.arena;
ArenaErr err = arena_ensure_capacity(&arena, sizeof(int), 0);
assert_int_equal(err, ARENA_INVALID_ALIGN);
arena_destroy(&arena);
}
static void test_arena_ensure_capacity_align_max(void **state) {
(void) state;
ArenaResult value = arena_init(64);
assert_true(value.is_valid);
Arena arena = value.arena;
ArenaErr err = arena_ensure_capacity(&arena, sizeof(int), SIZE_MAX);
assert_int_equal(err, ARENA_INVALID_ALIGN);
arena_destroy(&arena);
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_push_3_ints),
@@ -129,7 +209,13 @@ int main(void) {
cmocka_unit_test(test_init_arena_0_cap),
cmocka_unit_test(test_arena_alloc_size_0),
cmocka_unit_test(test_arena_alloc_size_max),
cmocka_unit_test(test_arena_alloc_align_0),
cmocka_unit_test(test_arena_alloc_align_max),
cmocka_unit_test(test_arena_align_size_0),
cmocka_unit_test(test_arena_ensure_capacity_align_0),
cmocka_unit_test(test_arena_ensure_capacity_align_max),
cmocka_unit_test(test_arena_ensure_capacity_size_0),
cmocka_unit_test(test_arena_ensure_capacity_size_max),
};