#include #include #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; }