From ab3edaa58eed4ff73410954ca094531d49eb5844 Mon Sep 17 00:00:00 2001 From: talha Date: Thu, 15 Feb 2024 09:58:42 +0500 Subject: added personal libraries to git tracking --- .gitignore | 1 + README.txt | 2 + memory/memory.cpp | 275 +++++++++++++++++++++++++ memory/memory.h | 94 +++++++++ memory/test.cpp | 566 ++++++++++++++++++++++++++++++++++++++++++++++++++++ strings/strings.cpp | 31 +++ strings/strings.h | 42 ++++ strings/test.cpp | 65 ++++++ types.h | 19 ++ 9 files changed, 1095 insertions(+) create mode 100644 .gitignore create mode 100644 README.txt create mode 100644 memory/memory.cpp create mode 100644 memory/memory.h create mode 100644 memory/test.cpp create mode 100644 strings/strings.cpp create mode 100644 strings/strings.h create mode 100644 strings/test.cpp create mode 100644 types.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e92f57 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tags diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..2568cf1 --- /dev/null +++ b/README.txt @@ -0,0 +1,2 @@ +1. Introduction +Code libraries built for my personal use. Use with caution... and care. \ No newline at end of file diff --git a/memory/memory.cpp b/memory/memory.cpp new file mode 100644 index 0000000..3e305c8 --- /dev/null +++ b/memory/memory.cpp @@ -0,0 +1,275 @@ +#include "memory.h" + +size_t clamp_top(size_t size, size_t top) +{ + return size > top ? top : size; +} + +uintptr_t mem_clamp_top(uintptr_t mem, uintptr_t top) +{ + return mem > top ? top : mem; +} + +bool is_power_of_two(uintptr_t x) { + return (x & (x-1)) == 0; +} + +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 = p & (a-1); + + 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; +} + +uintptr_t fast_modulo(uintptr_t p, uintptr_t a) +{ + return (p & (a-1)); +} + +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 res_void 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 res_void result = {}; + + 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 res_void 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 res_void stack_resize_aligned(struct stack* s, void* old_memory, size_t old_size, + size_t new_size, size_t alignment) +{ + struct res_void result = {}; + + 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 res_void 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/memory/memory.h b/memory/memory.h new file mode 100644 index 0000000..14504b4 --- /dev/null +++ b/memory/memory.h @@ -0,0 +1,94 @@ +#ifndef AMR_MEMORY_H +#define AMR_MEMORY_H + +#include +#include +#include +#include + +#ifndef 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; + +#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 res_void { + enum MemStatus status; + size_t bytes_count; + void* memory; +}; + +bool is_power_of_two(uintptr_t x); + +//=========================================================================================== +// ---------------------------------- ARENA ------------------------------------------------- +//=========================================================================================== + +struct arena { + unsigned char* buffer; + size_t prev_offset; + size_t curr_offset; + size_t capacity; +}; + +uintptr_t align_forward(uintptr_t ptr, size_t align); +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 res_void stack_alloc_aligned(struct stack* s, size_t size, size_t alignment); +struct res_void stack_alloc(struct stack* s, size_t size); +enum MemStatus stack_free(struct stack* s); +struct res_void stack_resize_aligned(struct stack* s, void* old_memory, size_t old_size, + size_t new_size, size_t alignment); +struct res_void 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/memory/test.cpp b/memory/test.cpp new file mode 100644 index 0000000..4d17abd --- /dev/null +++ b/memory/test.cpp @@ -0,0 +1,566 @@ +#include +#include +#include "memory.cpp" + +// ================ 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: Failed to fit 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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 res_void 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; +} diff --git a/strings/strings.cpp b/strings/strings.cpp new file mode 100644 index 0000000..4c8486f --- /dev/null +++ b/strings/strings.cpp @@ -0,0 +1,31 @@ +u64 CstrSize(const char* cstr) +{ + u8 *iter = (u8*)cstr; + u64 size = 0; + for(;*iter != 0; *iter++) { + size += 1; + } + + return size; +} + +struct str8 Str8(u8 *string, u64 size) +{ + struct str8 result = {string, size}; + return result; +} + +struct res_str8 Str8InitCstr(struct arena *a, const char* cstr) +{ + u64 cstr_size = CstrSize(cstr); + struct res_void cstr_res = arena_alloc(a, cstr_size); + u8 *str = (u8 *)cstr_res.memory; + u64 str_size = cstr_res.bytes_count; + + MemCopy((void*)str, (void*)cstr, str_size*sizeof(u8)); + + struct res_str8 result = {}; + result.string = Str8(str, str_size); + result.bytes_count = str_size; + return result; +} diff --git a/strings/strings.h b/strings/strings.h new file mode 100644 index 0000000..dbaff7a --- /dev/null +++ b/strings/strings.h @@ -0,0 +1,42 @@ +#ifndef AMR_STR +#define AMR_STR + +#include +#include + +#define MemCopy memcpy + +// @todo: the function names have been changed a bit for the interest +// of speed. Some structs and functions share the same names, so +// I need to fix this but also make sure the functions have good names + +enum StrStatus {STR_OK=0, STR_FULL}; + +struct str8 { + u8 *buffer; + u64 size; +}; + +struct str8_node { + str8_node *next; + str8 string; +}; + +struct str8_list { + str8_node *first; + str8_node *last; + u64 count; + u64 capacity; +}; + +struct res_str8 { + str8 string; + u64 bytes_count; + enum StrStatus status; +}; + +struct str8 Str8(u8 *string, u64 size); +u64 CstrSize(const char* cstr); +struct res_str8 Str8InitCstr(struct arena *a, const char* cstr, u64 size); + +#endif diff --git a/strings/test.cpp b/strings/test.cpp new file mode 100644 index 0000000..49f81c9 --- /dev/null +++ b/strings/test.cpp @@ -0,0 +1,65 @@ +#include +#include "strings.h" +#include "strings.cpp" + +void TestCstrSize() +{ + u64 size = CstrSize("hello sailor\n"); + assert(size == 13 && "test_cstr_size() failed" + "the cstr size was wrong\n"); +} + +void TestEmptyCstrSize() +{ + u64 size = CstrSize(""); + assert(size == 0 && "test_empty_cstr_size() failed" + "the cstr size was wrong\n"); +} + +void TestStr8InitCstr(u8* buffer, u64 buffer_size) +{ + struct arena a = {}; + arena_init(&a, buffer, buffer_size); + struct res_str8 qres = Str8InitCstr(&a, "hello sailor"); + assert((qres.bytes_count == 12) && "Error TestStr8InitCstr:\n" + "Reason: Invalid size of allocated string\n" + "Hint: check what is happening to the bytes_count and how it is set first\n"); +} + +void TestStr8InitCstrEmpty(u8* buffer, u64 buffer_size) +{ + struct arena a = {}; + arena_init(&a, buffer, buffer_size); + struct res_str8 qres = Str8InitCstr(&a, ""); + assert((qres.string.size == 0) && "Error TestStr8InitCstrEmpty:\n" + "Reason: Invalid size of allocated string\n" + "Hint: check what is happening to the bytes_count and how it is set first\n"); +} + +void TestStr8InitCstrNoSpace(u8* buffer, u8 buffer_size) +{ + struct arena a = {}; + arena_init(&a, buffer, buffer_size); + struct res_str8 qres = Str8InitCstr(&a, "hello sailor, looks like you've got no space here on this boat!"); + assert(qres.string.size == 0 && "Error TestStr8InitStrNoSpace:\n" + "Reason: the bytes count is not 0, even though the string is larger than the supposedly available\n" + "arena space. This is an error and should not occur.\n" + "Hint: Look at what is going on in the allocation since that arena helpers handle allocations\n."); +} + +void test_strings() +{ + printf("\n======= Testing Strings Library =======\n"); + printf("===== Testing Cstring Functions =====\n"); + TestCstrSize(); + TestEmptyCstrSize(); + + u64 buffer_size = KB(64); + u8* buffer = (u8*)malloc(sizeof(u8)*buffer_size); + + TestStr8InitCstr(buffer, buffer_size); + TestStr8InitCstrEmpty(buffer, buffer_size); + TestStr8InitCstrNoSpace(buffer, 8); + printf("Cstring tests passed successfully\n"); + free(buffer); +} diff --git a/types.h b/types.h new file mode 100644 index 0000000..c77a1e1 --- /dev/null +++ b/types.h @@ -0,0 +1,19 @@ +#ifndef TYPES_H +#define TYPES_H + +#include + +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; + +#pragma section(".roglob", read) +#define read_only __declspec(allocate(".roglob")) + +#endif // TYPES_H \ No newline at end of file -- cgit v1.2.3