diff --git a/include/ci2.h b/include/ci2.h index 5b7d459..14a63fe 100644 --- a/include/ci2.h +++ b/include/ci2.h @@ -32,5 +32,6 @@ You get to decide which parts of that platform layer you'd like to use. /* Then you import which parts of the platform layer, you'd like. */ #include "./platform/error/ci2_exception.h" +#include "./platform/mem/ci2_mem.h" #endif // ci2.h diff --git a/include/platform/mem/ci2_mem.h b/include/platform/mem/ci2_mem.h new file mode 100644 index 0000000..0c4c78f --- /dev/null +++ b/include/platform/mem/ci2_mem.h @@ -0,0 +1,62 @@ +/* - | Copyright / About | ---------------------------------------------------- + Copyright (c) 2026 Randy Jordan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +This is the ci2_mem.h interface. They all are just wrappers for the general +stdlib allocators. If they fail they raise an exception with macro magic. All +other functions that may raise an exception are in include/platform/try. It's +not recommended to use these functions, as they are hard to debug and it's not +a robust way to handle errors. + + * --------------------------------------------------------------------------*/ +#ifndef CI2_MEM_H +#define CI2_MEM_H + +#include "../ci2_platform.h" // Exceptions +#include "../error/ci2_exception.h" // Exceptions +#include // size_t + +CI2_DEF const struct Exception oom; // Out of memory + +CI2_DEF void* +ci2_alloc(size_t nbytes, const char* file, int line); + +CI2_DEF void* +ci2_calloc(size_t count, size_t nbytes, const char* file, int line); + +CI2_DEF void +ci2_free(void* ptr, const char* file, int line); + +CI2_DEF void* +ci2_resize(void* ptr, size_t nbytes, const char* file, int line); + +#define CI2_ALLOC(nbytes) ci2_alloc((nbytes), __FILE__, __LINE__) +#define CI2_CALLOC(count, nbytes) \ + ci2_calloc((count), (nbytes), __FILE__, __LINE__) + +#define CI2_NEW(p) ((p) = CI2_ALLOC((size_t)sizeof *(p))) +#define CI2_NEW0(p) ((p) = CI2_CALLOC(1, (size_t)sizeof *(p))) + +#define CI2_FREE(ptr) ((void)(ci2_free((ptr), __FILE__, __LINE__), (ptr) = 0)) + +#define CI2_RESIZE(ptr, nbytes) \ + ((ptr) = ci2_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..906780c --- /dev/null +++ b/src/ci2_mem.c @@ -0,0 +1,89 @@ +/* - | Copyright / About | ---------------------------------------------------- + Copyright (c) 2026 Randy Jordan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +This is the ci2_mem.h implementation. It's 'just wrappers for the general +stdlib allocators. If they fail they raise an exception with macro magic. All +other functions that may raise an exception are in include/platform/try. It's +not recommended to use these functions, as they are hard to debug and it's not +a robust way to handle errors. + * --------------------------------------------------------------------------*/ +#include "../include/platform/mem/ci2_mem.h" +#include // malloc calloc free + +const struct Exception oom = { "Out of memory!" }; + +void* +ci2_alloc(size_t nbytes, const char* file, int line) +{ + void* ptr; + ci2_rt_assert(nbytes > 0); + ptr = malloc(nbytes); + if (ptr == NULL) { + if (file == NULL) + CI2_RAISE(oom); + else + ci2_raise_exception(&oom, file, CI2_FUNC, line); + } + return ptr; +} + +void* +ci2_calloc(size_t count, size_t nbytes, const char* file, int line) +{ + void* ptr; + ci2_rt_assert(count > 0); + ci2_rt_assert(nbytes > 0); + ptr = calloc(count, nbytes); + if (ptr == NULL) { + if (file == NULL) + CI2_RAISE(oom); + else + ci2_raise_exception(&oom, file, CI2_FUNC, line); + } + return ptr; +} + +void +ci2_free(void* ptr, const char* file, int line) +{ + (void)file; + (void)line; + + if (ptr) + free(ptr); +} + +void* +ci2_resize(void* ptr, size_t nbytes, const char* file, int line) +{ + // ci2_rt_assert(ptr); // ? Maybe + // ci2_rt_assert(nbytes > 0);// ? Maybe + void* tmp = realloc(ptr, nbytes); + if (tmp == NULL) { + if (file == NULL) + CI2_RAISE(oom); + else + ci2_raise_exception(&oom, file, CI2_FUNC, line); + } + + ptr = tmp; + return ptr; +} diff --git a/tests/06_memory_exception.c b/tests/06_memory_exception.c new file mode 100644 index 0000000..05c5d81 --- /dev/null +++ b/tests/06_memory_exception.c @@ -0,0 +1,91 @@ +/* - | Copyright / About | ---------------------------------------------------- + Copyright (c) 2026 Randy Jordan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + * --------------------------------------------------------------------------*/ +#define DEBUG +#define GUI_DEBUG_CLOSE +#include "../include/ci2.h" +#include +#include + +static int +test_caught_exception() +{ + size_t nbytes = CI2_GB(1024); + void* buf = NULL; + const char* str = "Hello World"; + buf = CI2_CALLOC(1, strlen(str + 1)); /* You have no enable asan options. */ + strncpy(buf, str, strlen(str)); + + char* opts = "ASAN_OPTIONS=allocator_may_fail=1"; + + printf("Attempting oom re-allocation of %zu at %p\n", nbytes, buf); + printf("Adding %s to env....%d\n", opts, putenv(opts)); + + CI2_TRY + { + printf("Trying something dumb.\n"); + CI2_RESIZE(buf, nbytes); + } + CI2_EXCEPT(oom) + { + /* handle memory failure gracefully */ + printf("Caught: %s\n", oom.msg); + return EXIT_SUCCESS; /* requested behavior */ + } + CI2_FINALLY + { + /* cleanup if needed, runs always */ + printf("Have to free allocation of %zu at %p\n", strlen(buf), buf); + if (buf) + free(buf); + } + CI2_END_TRY; + return EXIT_FAILURE; +} + +static int +test_uncaught_exception(void) +{ +#ifdef CI2_EXCEPTION_CRASH + printf("CI2_MSG — Deliberate Failure (expect a debug break)\n"); + printf(" Triggering ci2_raise_exception() now...\n"); + fflush(stdout); + + struct CI2_Exception u = { EFAULT, "Deliberate uncaught exception test." }; + CI2_RAISE(u); + printf(" [FAIL] Execution continued past a failing ASSERT_MSG!\n"); + return EXIT_FAILURE; // Shouldn't but still. +#endif + return EXIT_SUCCESS; +} + +int +main(int argc, ci2_sys_char* argv[]) +{ + UNUSED(argc); + UNUSED(argv); + int result = -1; + result = test_uncaught_exception(); + result = test_caught_exception(); + + return result; +}