summaryrefslogtreecommitdiff
path: root/src/memory
diff options
context:
space:
mode:
Diffstat (limited to 'src/memory')
-rw-r--r--src/memory/memory.c246
-rw-r--r--src/memory/memory.h98
-rw-r--r--src/memory/test.cpp566
3 files changed, 910 insertions, 0 deletions
diff --git a/src/memory/memory.c b/src/memory/memory.c
new file mode 100644
index 0000000..1d521e4
--- /dev/null
+++ b/src/memory/memory.c
@@ -0,0 +1,246 @@
+#include "memory.h"
+
+b8 is_power_of_two(uintptr_t x) { return (x & (x - 1)) == 0; }
+
+uintptr_t fast_modulo(uintptr_t p, uintptr_t a) { return (p & (a - 1)); }
+
+uintptr_t align_forward(uintptr_t ptr, size_t alignment) {
+ uintptr_t p, a, modulo;
+
+ assert(is_power_of_two(alignment));
+
+ p = ptr;
+ a = (uintptr_t)alignment;
+ modulo = fast_modulo(p, a);
+
+ if (modulo != 0) {
+ p += (a - modulo);
+ }
+
+ return p;
+}
+
+//===========================================================================================
+// ---------------------------------- Arena
+// -------------------------------------------------
+//===========================================================================================
+
+/*
+ A cases where arena allocation WILL fail:
+ | size = size_t + ${some_number_that_comes_up_higher_than_offset}
+
+ This is because there is no check being made
+*/
+void arena_init(struct Arena *a, unsigned char *backing_store,
+ size_t capacity) {
+ a->buffer = backing_store;
+ a->curr_offset = 0;
+ a->prev_offset = 0;
+ a->capacity = capacity;
+}
+
+void *arena_alloc_aligned(struct Arena *a, size_t size, size_t alignment) {
+ void *ptr = NULL;
+
+ assert(is_power_of_two(alignment));
+
+ uintptr_t curr_ptr = (uintptr_t)a->buffer + a->curr_offset;
+ uintptr_t offset = align_forward(curr_ptr, alignment);
+ offset = offset - (uintptr_t)a->buffer;
+
+ if (size <= a->capacity - offset) {
+ ptr = &a->buffer[offset];
+ a->prev_offset = a->curr_offset;
+ a->curr_offset = offset + size;
+ memset(ptr, 0, size);
+ }
+
+ return ptr;
+}
+
+void *arena_alloc(struct Arena *a, size_t size) {
+ return arena_alloc_aligned(a, size, DEFAULT_ALIGNMENT);
+}
+
+void *arena_resize_aligned(struct Arena *a, void *old_memory, size_t old_size,
+ size_t new_size, size_t alignment) {
+ unsigned char *old = (unsigned char *)old_memory;
+ void *ptr = NULL;
+
+ assert(is_power_of_two(alignment));
+
+ if (old >= a->buffer && old < a->buffer + a->capacity) {
+ if (a->buffer + a->prev_offset == old) {
+ // extend_last_element
+ if (new_size > old_size) {
+ size_t size_increase = new_size - old_size;
+ if (size_increase > (a->capacity - a->curr_offset)) {
+ new_size = old_size;
+ size_increase = 0;
+ }
+ memset(&a->buffer[a->curr_offset], 0, size_increase);
+ }
+ a->curr_offset = a->prev_offset + new_size;
+ ptr = old_memory;
+ } else {
+ ptr = arena_alloc_aligned(a, new_size, alignment);
+ if (ptr != NULL) {
+ size_t copy_size = old_size < new_size ? old_size : new_size;
+ memmove(ptr, old_memory, copy_size);
+ }
+ }
+ }
+
+ return ptr;
+}
+
+void *arena_resize(struct Arena *a, void *old_mem, size_t old_size,
+ size_t new_size) {
+ return arena_resize_aligned(a, old_mem, old_size, new_size,
+ DEFAULT_ALIGNMENT);
+}
+
+void arena_clear(struct Arena *a) {
+ a->curr_offset = 0;
+ a->prev_offset = 0;
+}
+
+//===========================================================================================
+// ---------------------------------- STACK
+// -------------------------------------------------
+//===========================================================================================
+
+void stack_init(struct stack *s, void *backing_store, size_t capacity) {
+ s->buffer = (unsigned char *)backing_store;
+ s->prev_offset = 0;
+ s->curr_offset = 0;
+ s->capacity = capacity;
+}
+
+size_t calc_padding_with_header(uintptr_t ptr, uintptr_t alignment,
+ size_t hdr_sz) {
+ uintptr_t p, a, modulo, padding, space_needed;
+
+ assert(is_power_of_two(alignment));
+
+ padding = space_needed = 0;
+
+ p = ptr;
+ a = alignment;
+ modulo = fast_modulo(p, a);
+
+ if (modulo != 0) {
+ padding = a - modulo;
+ }
+
+ space_needed = (uintptr_t)hdr_sz;
+ if (padding < space_needed) {
+ space_needed -= padding;
+ if (fast_modulo(space_needed, a) != 0) {
+ padding = padding + space_needed + a;
+ } else {
+ padding = padding + space_needed;
+ }
+ }
+
+ return (size_t)padding;
+}
+
+struct ResVoid stack_alloc_aligned(struct stack *s, size_t size,
+ size_t alignment) {
+ uintptr_t curr_addr, next_addr;
+ size_t padding;
+ struct stack_hdr *header;
+
+ assert(is_power_of_two(alignment));
+ if (alignment > 128) {
+ alignment = 128;
+ }
+
+ struct ResVoid result = {.status = MEM_OK, .bytes_count = 0, .memory = 0};
+
+ curr_addr = (uintptr_t)s->buffer + (uintptr_t)s->curr_offset;
+ padding = calc_padding_with_header(curr_addr, (uintptr_t)alignment,
+ sizeof(struct stack_hdr));
+
+ if (size > s->capacity - (s->curr_offset + padding)) {
+ result.status = MEM_FULL;
+ return result;
+ }
+
+ next_addr = curr_addr + (uintptr_t)padding;
+ header = (struct stack_hdr *)(next_addr - sizeof(struct stack_hdr));
+ header->prev_offset = s->prev_offset;
+ header->padding = padding;
+
+ s->prev_offset = s->curr_offset + padding;
+ s->curr_offset = s->prev_offset + size;
+
+ result.memory = memset((void *)next_addr, 0, size);
+ result.bytes_count = size;
+
+ return result;
+}
+
+struct ResVoid stack_alloc(struct stack *s, size_t size) {
+ return stack_alloc_aligned(s, size, DEFAULT_ALIGNMENT);
+}
+
+enum MemStatus stack_free(struct stack *s) {
+ uintptr_t last_ele = (uintptr_t)s->buffer + (uintptr_t)s->prev_offset;
+ struct stack_hdr *header =
+ (struct stack_hdr *)(last_ele - sizeof(struct stack_hdr));
+
+ uintptr_t prev_ele = (uintptr_t)s->buffer + (uintptr_t)header->prev_offset;
+ s->curr_offset =
+ (size_t)((last_ele - (uintptr_t)header->padding) - (uintptr_t)s->buffer);
+ s->prev_offset = (size_t)(prev_ele - (uintptr_t)s->buffer);
+
+ return MEM_OK;
+}
+
+struct ResVoid stack_resize_aligned(struct stack *s, void *old_memory,
+ size_t old_size, size_t new_size,
+ size_t alignment) {
+ struct ResVoid result = {.status = MEM_OK, .bytes_count = 0, .memory = 0};
+
+ if (old_memory < s->buffer || old_memory > s->buffer + s->capacity) {
+ result.status = MEM_OUT_OF_BOUNDS;
+ return result;
+ }
+
+ // is_last_element()
+ if (s->buffer + s->prev_offset == old_memory) {
+ if (new_size > old_size) {
+ size_t size_difference = new_size - old_size;
+ if (size_difference > s->capacity - s->curr_offset) {
+ result.status = MEM_FULL;
+ return result;
+ }
+
+ memset(&s->buffer[s->curr_offset], 0, size_difference);
+ }
+ s->curr_offset = s->prev_offset + new_size;
+
+ result.memory = old_memory;
+ return result;
+ }
+
+ result = stack_alloc_aligned(s, new_size, alignment);
+ size_t min_size =
+ old_size < result.bytes_count ? old_size : result.bytes_count;
+ memmove(result.memory, old_memory, min_size);
+
+ return result;
+}
+
+struct ResVoid stack_resize(struct stack *s, void *old_memory, size_t old_size,
+ size_t new_size) {
+ return stack_resize_aligned(s, old_memory, old_size, new_size,
+ DEFAULT_ALIGNMENT);
+}
+
+void stack_clear(struct stack *s) {
+ s->prev_offset = 0;
+ s->curr_offset = 0;
+}
diff --git a/src/memory/memory.h b/src/memory/memory.h
new file mode 100644
index 0000000..e402c0b
--- /dev/null
+++ b/src/memory/memory.h
@@ -0,0 +1,98 @@
+#ifndef AMR_MEMORY_H
+#define AMR_MEMORY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#ifndef AMR_TYPES_H
+#define AMR_TYPES_H
+
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef u8 b8;
+
+#endif // AMR_TYPES_H
+
+
+#ifndef DEFAULT_ALIGNMENT
+#define DEFAULT_ALIGNMENT (2*sizeof(void *))
+#endif
+
+// @todo: build a logging mechanism for handling errors
+// maybe read about that
+
+enum MemStatus { MEM_OK=0, MEM_OUT_OF_BOUNDS, MEM_FULL };
+
+struct ResVoid {
+ enum MemStatus status;
+ size_t bytes_count;
+ void* memory;
+};
+
+b8 is_power_of_two(uintptr_t x);
+uintptr_t fast_modulo(uintptr_t p, uintptr_t a);
+uintptr_t align_forward(uintptr_t ptr, size_t align);
+
+//===========================================================================================
+// ---------------------------------- ARENA -------------------------------------------------
+//===========================================================================================
+
+struct Arena {
+ unsigned char* buffer;
+ size_t prev_offset;
+ size_t curr_offset;
+ size_t capacity;
+};
+
+void arena_init(struct Arena *a, unsigned char *backing_store, size_t capacity);
+void* arena_alloc_aligned(struct Arena* a, size_t size, size_t align);
+void* arena_alloc(struct Arena* a, size_t size);
+void* arena_resize_aligned(struct Arena* a, void* old_memory, size_t old_size,
+ size_t new_size, size_t align);
+void* arena_resize(struct Arena* a, void* old_mem, size_t old_size,
+ size_t new_size);
+void arena_clear(struct Arena *a);
+
+//===========================================================================================
+// ---------------------------------- STACK -------------------------------------------------
+//===========================================================================================
+
+/*
+* @todo: stack needs to be updated, it's really just a work in progress right now.
+* The main thing is minimizing the use of compound types, since that is pretty annoying to deal with.
+* I would rather write code that makes sure to collapse all possible cases and lets me just not worry about code.
+* Would rather stick to worrying about data being data
+*/
+
+struct stack {
+ unsigned char* buffer;
+ size_t prev_offset;
+ size_t curr_offset;
+ size_t capacity;
+};
+
+struct stack_hdr {
+ size_t prev_offset;
+ size_t padding;
+};
+
+void stack_init(struct stack* s, void *backing_store, size_t capacity);
+struct ResVoid stack_alloc_aligned(struct stack* s, size_t size, size_t alignment);
+struct ResVoid stack_alloc(struct stack* s, size_t size);
+enum MemStatus stack_free(struct stack* s);
+struct ResVoid stack_resize_aligned(struct stack* s, void* old_memory, size_t old_size,
+ size_t new_size, size_t alignment);
+struct ResVoid stack_resize(struct stack* s, void* old_memory, size_t old_size, size_t new_size);
+void stack_clear(struct stack* s);
+
+#endif
diff --git a/src/memory/test.cpp b/src/memory/test.cpp
new file mode 100644
index 0000000..f62ce96
--- /dev/null
+++ b/src/memory/test.cpp
@@ -0,0 +1,566 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "memory.c"
+
+// ================ ARENA ================ //
+void test_arena_init(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ assert((a.buffer == buffer) && "Arena does not start at the buffer");
+ assert((a.curr_offset == 0) && (a.prev_offset == 0) && "Arena offsets not initialised properly");
+ assert((a.capacity == buffer_size) && "Arena capacity does not match buffer size");
+}
+
+void test_arena_alloc(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 8);
+ assert((qres != NULL) && "Arena Allocation Test Failed: "
+ "Reason: Failed to allocate memory");
+}
+
+void test_arena_alloc_low_memory(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, buffer_size + 20);
+ assert((qres == NULL) && "Low Memory Arena Allocation Test Failed: "
+ "Reason: Should have allocated the entire buffer since size is larger. \n");
+ qres = arena_alloc(&a, 20);
+}
+
+void test_arena_alloc_negative_size(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 16);
+ u8* n1 = (u8*)qres;
+
+ qres = arena_alloc(&a, -16);
+
+ assert((qres == NULL) && "Low Memory Arena Allocation Test Failed: "
+ "Reason: Should have allocated the remaining buffer. \n");
+}
+
+void test_arena_resize(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 8);
+ u8* n1 = (u8*)qres;
+ *n1 = 23;
+
+ qres = arena_alloc(&a, 8);
+ u8* n2 = (u8*)qres;
+ *n2 = 20;
+
+ qres = arena_resize(&a, n1, 8, 16);
+ assert((qres != NULL) && "Arena Resize Test Failed: "
+ "Reason: Failed to resize previously allocated memory\n"
+ "this should not have happened since we do not have anything causing availability issues\n");
+ u8* n1_1 = (u8*)qres;
+
+ assert((*n1 == *n1_1) && "Arena Resize Test Failed: "
+ "Reason: Value of resized memory changed. This should not happen in any case!\n");
+}
+
+void test_arena_resize_tail(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 16);
+ u8* n1 = (u8*)qres;
+ *n1 = 23;
+
+ qres = arena_alloc(&a, 16);
+ u8* n2 = (u8*)qres;
+ *n2 = 20;
+
+ qres = arena_resize(&a, n2, 16, 8);
+ /*assert((qres.bytes_count == 8) && "Arena Resize Tail Test Failed: "
+ "Reason: Failed to resize previously allocated memory at tail\n");*/
+ u8* n2_1 = (u8*)qres;
+
+ assert((*n2 == *n2_1) && "Arena Resize Test Failed: "
+ "Reason: Value of resized memory changed. This should not happen in any case!\n");
+}
+
+void test_arena_resize_filled_completely(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 8);
+ u8* n1 = (u8*)qres;
+ *n1 = 23;
+
+ qres = arena_alloc(&a, 8);
+ u8* n2 = (u8*)qres;
+ *n2 = 20;
+
+ qres = arena_resize(&a, n1, 8, buffer_size - 32);
+ assert((qres != NULL) && "Arena Resize Filled Completely Failed: \n"
+ "Reason: Failed to resize an element even though it perfectly fills the memory Arena");
+}
+
+void test_arena_resize_out_of_bounds(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 8);
+ u8* n1 = (u8*)qres;
+ *n1 = 23;
+
+ qres = arena_alloc(&a, 8);
+ u8* n2 = (u8*)qres;
+ *n2 = 20;
+
+ qres = arena_resize(&a, n1, 8, buffer_size - 8);
+ assert(qres == NULL && "Arena Resize OUT OF BOUNDS test Failed: \n"
+ "Reason: Allocated a larger than capacity element within Arena capacity.\n");
+}
+
+void test_arena_resize_negative_size(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 8);
+ u8* n1 = (u8*)qres;
+ *n1 = 23;
+
+ qres = arena_alloc(&a, 8);
+ u8* n2 = (u8*)qres;
+ *n2 = 20;
+
+ qres = arena_resize(&a, n1, 8, -35);
+ assert(qres == NULL && "Arena Resize -ve size test Failed: \n"
+ "Reason: Failed to handle allocating a -ve size (VERY LARGE IN UINT) space.\n");
+}
+
+void test_arena_resize_tail_negative_size(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 8);
+ u8* n1 = (u8*)qres;
+ *n1 = 23;
+
+ qres = arena_alloc(&a, 8);
+ u8* n2 = (u8*)qres;
+ *n2 = 20;
+
+ qres = arena_resize(&a, n2, 8, -35);
+ assert(qres == NULL && "Arena Resize OUT OF BOUNDS test Failed: \n"
+ "Reason: Failed to fit a larger than capacity element within Arena capacity.\n");
+}
+
+void test_arena_clear(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 8);
+ arena_clear(&a);
+ assert((a.prev_offset == 0) && (a.curr_offset == 0) && "Arena Clear Test Failed: \n"
+ "Reason: Failed to clear offsets on an active Arena entity after it was cleared.\n");
+}
+
+void test_arena_realloc(unsigned char *buffer, u32 buffer_size)
+{
+ struct Arena a = {0};
+ arena_init(&a, buffer, buffer_size);
+ void *qres = arena_alloc(&a, 8);
+ u8* n1 = (u8*)qres;
+ *n1 = 12;
+ u8 n1_value = *n1;
+
+ // free Arena
+ arena_clear(&a);
+
+ qres = arena_alloc(&a, 16);
+ u8* n1_realloc = (u8*)qres;
+
+ assert((*n1_realloc != n1_value) && "Arena Realloc Test Failed: \n"
+ "Reason: Failed to properly clear memory in a region where memory was already allocated.\n");
+}
+
+// ================ STACK ================ //
+
+void test_stack_init(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+ assert((s.buffer == buffer) && "stack does not start at the buffer");
+ assert((s.curr_offset == 0) && (s.prev_offset == 0) && "stack offsets not initialised properly");
+ assert((s.capacity == buffer_size) && "stack capacity does not match buffer size");
+}
+
+void test_stack_alloc_n1(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ assert((qres.status == MEM_OK) && "failed to allocate stack element");
+
+ u8 *ele = (u8 *)qres.memory;
+ struct stack_hdr *header = (struct stack_hdr*)((uintptr_t)ele - sizeof(struct stack_hdr));
+ assert((header->prev_offset == 0) && "incorrect prev_offset for first stack element");
+
+ size_t pad = header->padding;
+
+ assert((s.curr_offset == pad + ele_sz ) && "incorrect curr offset memory");
+}
+
+void test_stack_alloc_n2(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ stack_alloc(&s, ele_sz);
+
+ size_t last_ele_head = s.prev_offset;
+ size_t last_ele_tail = s.curr_offset;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ assert((qres.status == MEM_OK) && "failed to allocate stack element");
+
+ u8 *ele = (u8 *)qres.memory;
+ struct stack_hdr *header = (struct stack_hdr*)((uintptr_t)ele - sizeof(struct stack_hdr));
+ assert((header->prev_offset == last_ele_head) && "incorrect prev_offset for second stack element");
+
+ size_t pad = header->padding;
+ assert((s.curr_offset == last_ele_tail + pad + ele_sz ) && "incorrect curr offset memory");
+}
+
+void test_stack_free(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ stack_alloc(&s, ele_sz);
+
+ enum MemStatus status = stack_free(&s);
+ assert((status == MEM_OK) && "failed to free stack element");
+
+ assert((s.buffer == buffer) && "failed to reset buffer pointer to start of memory");
+ assert((s.prev_offset == 0) && (s.curr_offset == 0) && "failed to move offsets back correctly");
+}
+
+void test_stack_resize_n0(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 static_mem[8] = {0,1,2,3,4,5,6,7};
+ struct ResVoid qres = stack_resize(&s, static_mem, 8, 16);
+ assert((qres.status == MEM_OUT_OF_BOUNDS) && "Stack Resize n0 Failed: \
+ Reason: should not have resized an element not belonging to the stack\n");
+}
+
+void test_stack_resize_n1(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ u8* n1 = (u8*)qres.memory;
+ *n1 = 23;
+
+ qres = stack_resize(&s, n1, ele_sz, ele_sz + 20);
+ assert((qres.status == MEM_OK) && "Stack Resize n1 Failed: \
+ Reason: failed to resize stack element\n");
+ n1 = (u8*)qres.memory;
+
+ struct stack_hdr* header = (struct stack_hdr*)(n1 - sizeof(struct stack_hdr));
+ assert((s.curr_offset == 52) && "Stack Resize n1 Failed: \
+ Reason: incorrectly resized stack element\n");
+}
+
+void test_stack_resize_n1_low_memory(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ u8* n1 = (u8*)qres.memory;
+ *n1 = 23;
+
+ qres = stack_resize(&s, n1, ele_sz, ele_sz + 256);
+ n1 = (u8*)qres.memory;
+
+ assert((qres.status == MEM_FULL) && "Stack Resize n1 low memory Failed: \
+ Reason: Should not be allowed to resize beyond the allocated stack capacity\n");
+}
+
+void test_stack_resize_n2(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ u8* n1 = (u8*)qres.memory;
+ *n1 = 1;
+
+ qres = stack_alloc(&s, ele_sz);
+ u8* n2 = (u8*)qres.memory;
+ *n2 = 2;
+
+ size_t last_ele_head = s.prev_offset;
+
+ qres = stack_resize(&s, n1, ele_sz, 8);
+ u8* n3 = (u8*)qres.memory;
+ assert((qres.status == MEM_OK) && "Stack Resize n2 Failed: "
+ "Reason: failed to resize first element in a two element array\n");
+
+ assert((*n3 == *n1) && "Stack Resize n2 Failed: "
+ "Reason: failed to move over data properly after resizing elements");
+
+ struct stack_hdr* header = (struct stack_hdr*)(n3 - sizeof(struct stack_hdr));
+ assert((header->prev_offset == last_ele_head) && "Stack Resize 2 Failed: "
+ "Reason: failed to set the previous offset of the header element properly after resize");
+}
+
+void test_stack_resize_tail(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ u8* n1 = (u8*)qres.memory;
+ *n1 = 1;
+
+ qres = stack_resize(&s, n1, ele_sz, 8);
+ assert((qres.status == MEM_OK) && "Stack Resize tail Failed: "
+ "Reason: failed to resize last element to be smaller\n");
+}
+
+void test_stack_alloc_free(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ u8* n1 = (u8*)qres.memory;
+ *n1 = 1;
+ size_t head_n1 = s.prev_offset;
+ size_t tail_n1 = s.curr_offset;
+
+ qres = stack_alloc(&s, ele_sz);
+ u8* n2 = (u8*)qres.memory;
+ *n2 = 2;
+ size_t head_n2 = s.prev_offset;
+ size_t tail_n2 = s.curr_offset;
+
+ /* @note: elements are allocated, now we will be testing a few things
+ *
+ * 1. can we free all elements up till the stack UNTIL the point where,
+ * we can not free the stack since it is empty
+ *
+ * 2. when we free elements, does the offset reset to the point we expect it to
+ * By which I mean,
+ *
+ * . . . . . ele_1 . . . . . ele_2 . . . . .
+ * | |
+ * | |-> curr_offset is here at the end of ele2
+ * |
+ * |-> this is the end of ele1, once we free, we expect the curr_offset
+ * to return to this point and we expect to start considering allocations from
+ * this point on
+ *
+ *
+ * */
+ stack_free(&s);
+
+ assert((s.prev_offset == head_n1) && "Stack alloc free error"
+ "Reason: failed to move the prev_offset back to the correct position");
+ assert((s.curr_offset == tail_n1) && "Stack alloc free error"
+ "Reason: failed to move the curr_offset back to the correct position");
+
+ /*
+ * @note: we now want to test after allocating something, whether the memory was overwritten properly
+ * and there was no garbage data present
+ */
+
+ qres = stack_alloc(&s, ele_sz);
+ u8* n2_1 = (u8*)qres.memory;
+ assert((*n2_1 == 0) && "Stack Alloc Free error"
+ "Reason: failed to free up memory properly on allocating memory that was cleared");
+}
+
+void test_stack_alloc_free_resize(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ u8* n1 = (u8*)qres.memory;
+ *n1 = 1;
+ size_t head_n1 = s.prev_offset;
+ size_t tail_n1 = s.curr_offset;
+
+ qres = stack_alloc(&s, ele_sz);
+ u8* n2 = (u8*)qres.memory;
+ *n2 = 2;
+ size_t head_n2 = s.prev_offset;
+ size_t tail_n2 = s.curr_offset;
+
+ qres = stack_alloc(&s, ele_sz);
+ u8* n3 = (u8*)qres.memory;
+ *n3 = 3;
+ size_t head_n3 = s.prev_offset;
+ size_t tail_n3 = s.curr_offset;
+
+ stack_free(&s);
+
+ qres = stack_resize(&s, n1, ele_sz, 32);
+ u8* n1_1 = (u8*)qres.memory;
+ assert((*n1_1 != 3) && "Stack Alloc Free Resize Test Failed: "
+ "Reason: the newly resized memory was not setup properly.\n"
+ "It had the memory contents of a previously allocated statement\n");
+ assert((n1_1 == n3) && "Stack Alloc Free Resize Test Failed: "
+ "Reason: the newly resized memory was not allocated to the correct next region\n"
+ "It should have the same memory address as memory allocation #3\n");
+}
+
+void test_stack_resize_n2_low_space(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ u8* n1 = (u8*)qres.memory;
+ *n1 = 1;
+ size_t head_n1 = s.prev_offset;
+ size_t tail_n1 = s.curr_offset;
+
+ qres = stack_alloc(&s, s.capacity - 64);
+ u8* n2 = (u8*)qres.memory;
+ *n2 = 2;
+ size_t head_n2 = s.prev_offset;
+ size_t tail_n2 = s.curr_offset;
+
+ qres = stack_resize(&s, n1, ele_sz, 30);
+
+ assert((qres.status == MEM_FULL) && "Test Stack Resize Low Space with 2 elements Failed: "
+ "Reason: Failed to catch resize with size larger than available space. \n");
+}
+
+void test_stack_alloc_negative_size(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ // @note: the way this works is that size_t is unsigned, which is what the function takes
+ // so we get a really large number, that actually ends up looping around and becomes less than the array.
+ // so we have a bounds check for that.
+ stack_alloc(&s, 16);
+ struct ResVoid qres = stack_alloc(&s, -16);
+ assert((qres.status == MEM_FULL) && "Test Stack Alloc Negative Size Failed\n"
+ "Reason: Failed to catch allocation with -ve size. \n"
+ "That will translate to be a very large size since the size variable is a size_t (unsigned)\n");
+}
+
+void test_stack_resize_negative_size(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ struct ResVoid qres = stack_alloc(&s, 16);
+ s8* n1 = (s8*)qres.memory;
+ *n1 = -20;
+
+ qres = stack_resize(&s, n1, 16, -16);
+ assert((qres.status == MEM_FULL) && "Test Stack Alloc Negative Size Failed\n"
+ "Reason: Failed to catch resize with -ve size. \n"
+ "That will translate to be a very large size since the size variable is a size_t (unsigned)\n");
+}
+
+void test_stack_resize_tail_negative_size(unsigned char *buffer, u32 buffer_size)
+{
+ struct stack s = {0};
+ stack_init(&s, buffer, buffer_size);
+
+ u8 ele_sz = 16;
+ struct ResVoid qres = stack_alloc(&s, ele_sz);
+ u8* n1 = (u8*)qres.memory;
+ *n1 = 1;
+
+ qres = stack_resize(&s, n1, ele_sz, -8);
+ u8* n2 = (u8*)qres.memory;
+ assert((qres.status == MEM_FULL) && "Stack Resize tail Negative Size Failed: "
+ "Reason: Failed to catch resize with -ve size. \n"
+ "That will translate to be a very large size since the size variable is a size_t (unsigned)\n");
+}
+
+int main(int argc, char** argv) {
+ u32 sz = 256*sizeof(u8);
+ u8 *buffer = (u8 *)malloc(sz);
+ // --------- ARENA --------- //
+ printf("\n===== Testing Arena Allocator =====\n");
+ test_arena_init(buffer, sz);
+ printf("- Arena initialization test passed\n");
+ test_arena_alloc(buffer, sz);
+ test_arena_alloc_low_memory(buffer, sz);
+ test_arena_alloc_negative_size(buffer, sz);
+ printf("- Arena allocation tests passed\n");
+ test_arena_resize(buffer, sz);
+ test_arena_resize_tail(buffer, sz);
+ test_arena_resize_negative_size(buffer, sz);
+ test_arena_resize_tail_negative_size(buffer, sz);
+ test_arena_resize_filled_completely(buffer, sz);
+ test_arena_resize_out_of_bounds(buffer, sz);
+ printf("- Arena resize tests passed\n");
+ test_arena_clear(buffer, sz);
+ printf("- Arena clear tests passed\n");
+ test_arena_realloc(buffer, sz);
+ printf("- Arena reallocation tests passed\n");
+
+ // --------- STACK --------- //
+ printf("\n===== Testing Stack Allocator =====\n");
+
+ test_stack_init(buffer, sz);
+ printf("- stack initialization passed\n");
+
+ test_stack_alloc_n1(buffer, sz);
+ test_stack_alloc_n2(buffer, sz);
+ printf("- Stack allocation tests passed\n");
+
+ test_stack_free(buffer, sz);
+ printf("- stack free passed\n");
+
+ test_stack_resize_n0(buffer, sz);
+ test_stack_resize_n1(buffer, sz);
+ test_stack_resize_n1_low_memory(buffer, sz);
+ test_stack_resize_n2(buffer, sz);
+ test_stack_resize_tail(buffer, sz);
+ printf("- stack resize passed\n");
+
+ printf("- testing stack alloc free edge cases \n");
+ printf(" - edge cases passed:\n");
+ test_stack_alloc_free(buffer, sz);
+ printf(" 1. alloc then free\n");
+ test_stack_alloc_free_resize(buffer, sz);
+ printf(" 2. alloc then free then resize\n");
+ test_stack_resize_n2_low_space(buffer, sz);
+ printf(" 3. alloc two elements and then resize when there is low space\n");
+ test_stack_alloc_negative_size(buffer, sz);
+ printf(" 4. allocating negative size detection\n");
+ test_stack_resize_negative_size(buffer, sz);
+ printf(" 5. resizing negative size detection\n");
+ test_stack_resize_tail_negative_size(buffer, sz);
+ printf(" 6. resizing tail to negative size detection\n");
+
+ printf("\n===== Memory tests completed successfully =====\n");
+ free(buffer);
+ return 1;
+}