From b638c8f183c448b05a28e5d78917df37cd1fdd38 Mon Sep 17 00:00:00 2001 From: Randy Jordan Date: Tue, 21 Apr 2026 20:44:51 -0500 Subject: [PATCH] Added memory allocator and tests. --- Makefile | 2 +- include/ci2.h | 5 ---- include/ci2_mem.h | 30 ++++++++++++++++++++ src/ci2_mem.c | 60 ++++++++++++++++++++++++++++++++++++++++ src/linux_exception.c | 4 ++- tests/01_sanity.c | 1 - tests/02_uncaught.c | 1 - tests/03_mem_exception.c | 33 ++++++++++++++++++++++ tests/04_malloc.c | 31 +++++++++++++++++++++ tests/05_calloc.c | 27 ++++++++++++++++++ 10 files changed, 185 insertions(+), 9 deletions(-) delete mode 100644 include/ci2.h create mode 100644 include/ci2_mem.h create mode 100644 src/ci2_mem.c create mode 100644 tests/03_mem_exception.c create mode 100644 tests/04_malloc.c create mode 100644 tests/05_calloc.c diff --git a/Makefile b/Makefile index 9135206..c2df4e5 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ test: $(LIB) $(TESTBINS) FAILURE=$$((FAILURE + 1)); \ fi; \ done; \ - printf "\nTests completed\n"; \ + printf "\nTests completed. First two should fail.\n"; \ printf "SUCCESS: %b%d%b\n" "$$GREEN" "$$SUCCESS" "$$NC"; \ printf "FAILURE: %b%d%b\n" "$$RED" "$$FAILURE" "$$NC"; \ test $$FAILURE -eq 0 diff --git a/include/ci2.h b/include/ci2.h deleted file mode 100644 index 0fb6a7b..0000000 --- a/include/ci2.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef CI2_INCLUDED -#define CI2_INCLUDED - - -#endif diff --git a/include/ci2_mem.h b/include/ci2_mem.h new file mode 100644 index 0000000..0aa9294 --- /dev/null +++ b/include/ci2_mem.h @@ -0,0 +1,30 @@ +#ifndef CI2_MEM_H +#define CI2_MEM_H + +#include "ci2_exception.h" // Exceptions +#include // size_t + +/* General Macros*/ +#define MEM_KB(x) ((size_t)(x) * 1024ULL) +#define MEM_MB(x) ((size_t)(x) * 1024ULL * 1024ULL) +#define MEM_GB(x) ((size_t)(x) * 1024ULL * 1024ULL * 1024ULL) +#define MEM_SIZE(x) (ptrdiff_t)sizeof(x) +#define MEM_COUNT(a) (MEM_SIZE(a) / MEM_SIZE(*(a))) +#define MEM_LEN(s) (MEM_COUNT(s) - 1) + + +extern const Exception oom; // Out of memory + +extern void *mem_alloc (size_t nbytes,const char *file, int line); +extern void *mem_calloc(size_t count, size_t nbytes, const char *file, int line); +extern void mem_free(void *ptr, const char *file, int line); +extern void *mem_resize(void *ptr, size_t nbytes, const char *file, int line); + +#define ALLOC(nbytes) mem_alloc((nbytes), __FILE__, __LINE__) +#define CALLOC(count, nbytes) mem_calloc((count), (nbytes), __FILE__, __LINE__) +#define NEW(p) ((p) = ALLOC((size_t)sizeof *(p))) +#define NEW0(p) ((p) = CALLOC(1, (size_t)sizeof *(p))) +#define FREE(ptr) ((void)(mem_free((ptr), __FILE__, __LINE__), (ptr) = 0)) +#define RESIZE(ptr, nbytes) ((ptr) = mem_resize((ptr), (nbytes), __FILE__, __LINE__)) + +#endif // CI2_MEM_H diff --git a/src/ci2_mem.c b/src/ci2_mem.c new file mode 100644 index 0000000..72397f3 --- /dev/null +++ b/src/ci2_mem.c @@ -0,0 +1,60 @@ +#include +#include +#include "../include/ci2_exception.h" +#include "../include/ci2_mem.h" + +const struct Exception oom = { "Out of memory!" }; + +void *mem_alloc(size_t nbytes, const char *file, int line) +{ + void *ptr; + assert(nbytes > 0); + ptr = malloc(nbytes); + if (ptr == NULL) + { + if (file == NULL) + RAISE(oom); + else + except_raise(&oom, file, line); + } + return ptr; +} +void *mem_calloc(size_t count, size_t nbytes, const char *file, int line) +{ + void *ptr; + assert(count > 0); + assert(nbytes > 0); + ptr = calloc(count, nbytes); + if (ptr == NULL) + { + if (file == NULL) + RAISE(oom); + else + except_raise(&oom, file, line); + } + return ptr; +} +void mem_free(void *ptr, const char *file, int line) +{ + (void) file; + (void) line; + + if (ptr) + free(ptr); + +} +void *mem_resize(void *ptr, size_t nbytes, const char *file, int line) +{ + assert(ptr); // ? + assert(nbytes > 0);// ? + void *tmp = realloc(ptr, nbytes); + if (tmp == NULL) + { + if (file == NULL) + RAISE(oom); + else + except_raise(&oom, file, line); + } + ptr = tmp; + return ptr; +} diff --git a/src/linux_exception.c b/src/linux_exception.c index 8ca5f9a..5203dfe 100644 --- a/src/linux_exception.c +++ b/src/linux_exception.c @@ -3,14 +3,16 @@ #include "../include/ci2_exception.h" struct Except_Frame *except_stack = NULL; -const struct Exception assertion_failed = { "Assertion failed" }; +// Custom Assertion +const struct Exception assertion_failed = { "Assertion failed" }; void ci2_assert(int e){ if(!e){ RAISE(assertion_failed); } } +// Raise an exception and add it to the stack. void except_raise(const struct Exception *e, const char *file,int line){ struct Except_Frame *p = except_stack; diff --git a/tests/01_sanity.c b/tests/01_sanity.c index 07026c0..7e0348b 100644 --- a/tests/01_sanity.c +++ b/tests/01_sanity.c @@ -5,7 +5,6 @@ #include int main(void){ - printf("\n\n"); printf("Assertion sanity test.\n"); ci2_assert(0); return EXIT_SUCCESS; diff --git a/tests/02_uncaught.c b/tests/02_uncaught.c index 0287af1..13b87d8 100644 --- a/tests/02_uncaught.c +++ b/tests/02_uncaught.c @@ -5,7 +5,6 @@ #include int main(void){ - printf("\n\n"); printf("Uncaught exceptions sanity test.\n"); struct Exception uncaught = {NULL}; RAISE(uncaught); diff --git a/tests/03_mem_exception.c b/tests/03_mem_exception.c new file mode 100644 index 0000000..3566651 --- /dev/null +++ b/tests/03_mem_exception.c @@ -0,0 +1,33 @@ +#define DEBUG +#include +#include +#include +#include "../include/ci2_mem.h" + + +__attribute__((constructor)) static void setup_asan(void) { + setenv("ASAN_OPTIONS", "allocator_may_return_null=1", 1); +} + +int main(void){ + size_t nbytes = MEM_GB(1024); + void *buf; + TRY { + buf = ALLOC(nbytes); /* try 1GB */ + if (!buf) + RAISE(oom); + free(buf); + } + EXCEPT(oom) { + /* handle memory failure gracefully */ + fprintf(stderr, "Caught: %s Exception! Now we can return success.\n", oom.reason); + return EXIT_SUCCESS; /* requested behavior */ + } + FINALLY { + /* cleanup if needed, runs always */ + if (buf) free(buf); + } + END_TRY; + + return EXIT_FAILURE; /* shouldn't reach here for this example */ + } diff --git a/tests/04_malloc.c b/tests/04_malloc.c new file mode 100644 index 0000000..3b643eb --- /dev/null +++ b/tests/04_malloc.c @@ -0,0 +1,31 @@ +#define DEBUG +#include +#include +#include "../include/ci2_mem.h" +int mem_is_zero(const void *ptr, size_t nbytes) { + assert(ptr); + assert(nbytes > 0); + static const unsigned char zero_block[1024] = {0}; + while (nbytes >= sizeof(zero_block)) { + if (memcmp(ptr, zero_block, sizeof(zero_block)) != 0) + return 0; + ptr = (const unsigned char *)ptr + sizeof(zero_block); + nbytes -= sizeof(zero_block); + } + if (nbytes > 0 && memcmp(ptr, zero_block, nbytes) != 0) + return 0; + return 1; +} + +int main(void){ + size_t nbytes = 4096; + + void *tmp = malloc(nbytes); + memset(tmp, 0xAB, nbytes); + free(tmp); + + void *ptr = ALLOC(nbytes); // likely gets the same region back + assert(!mem_is_zero(ptr, nbytes)); // FAILS — 0xAB still there + assert(ptr != NULL); + return EXIT_SUCCESS; +} diff --git a/tests/05_calloc.c b/tests/05_calloc.c new file mode 100644 index 0000000..b2e5d9b --- /dev/null +++ b/tests/05_calloc.c @@ -0,0 +1,27 @@ +#define DEBUG +#include +#include +#include "../include/ci2_mem.h" + +int mem_is_zero(const void *ptr, size_t nbytes) { + assert(ptr); + assert(nbytes > 0); + static const unsigned char zero_block[1024] = {0}; + while (nbytes >= sizeof(zero_block)) { + if (memcmp(ptr, zero_block, sizeof(zero_block)) != 0) + return 0; + ptr = (const unsigned char *)ptr + sizeof(zero_block); + nbytes -= sizeof(zero_block); + } + if (nbytes > 0 && memcmp(ptr, zero_block, nbytes) != 0) + return 0; + return 1; +} + +int main(void){ + size_t nbytes = 4096; + void *ptr = CALLOC(1,nbytes); + assert(ptr != NULL); + assert( mem_is_zero(ptr, nbytes)); + return EXIT_SUCCESS; +}