diff options
Diffstat (limited to 'src/memory')
| -rw-r--r-- | src/memory/memory.c | 246 | ||||
| -rw-r--r-- | src/memory/memory.h | 98 | ||||
| -rw-r--r-- | src/memory/test.cpp | 566 | 
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; +} | 
