From 467ec9890002a3903fa18c6df158ddbcbfa04ee8 Mon Sep 17 00:00:00 2001 From: Randy Jordan Date: Tue, 12 May 2026 07:50:58 -0500 Subject: [PATCH] ci2_mem and basic test --- include/ci2.h | 1 + include/ci2_mem.h | 53 ++++++++++++++ src/ci2_mem.c | 73 +++++++++++++++++++ tests/04_memory_exception.c | 141 ++++++++++++++++++++++++++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 include/ci2_mem.h create mode 100644 src/ci2_mem.c create mode 100644 tests/04_memory_exception.c diff --git a/include/ci2.h b/include/ci2.h index 09a9fd0..f0d09f9 100644 --- a/include/ci2.h +++ b/include/ci2.h @@ -25,6 +25,7 @@ SOFTWARE. #include "ci2_window.h" // Includes base #include "ci2_exception.h" +#include "ci2_mem.h" #endif // ci2.h diff --git a/include/ci2_mem.h b/include/ci2_mem.h new file mode 100644 index 0000000..e718b6b --- /dev/null +++ b/include/ci2_mem.h @@ -0,0 +1,53 @@ +/* - | Copyright | -------------------------------------------------------------- + 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. + + * --------------------------------------------------------------------------*/ +#ifndef CI2_MEM_H +#define CI2_MEM_H + +#include "ci2_exception.h" // Exceptions +#include // size_t + +/* General Macros*/ +#define CI2_KB(x) ((size_t)(x) * 1024ULL) +#define CI2_MB(x) ((size_t)(x) * 1024ULL * 1024ULL) +#define CI2_GB(x) ((size_t)(x) * 1024ULL * 1024ULL * 1024ULL) +#define CI2_SIZE(x) (ptrdiff_t)sizeof(x) +#define CI2_COUNT(a) (CI2_SIZE(a) / CI2_SIZE(*(a))) +#define CI2_LEN(s) (CI2_COUNT(s) - 1) + + +extern const CI2_Exception oom; // Out of memory + +extern void *ci2_alloc (size_t nbytes,const char *file, int line); +extern void *ci2_calloc(size_t count, size_t nbytes, const char *file, int line); +extern void ci2_free(void *ptr, const char *file, int line); +extern 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..90192b3 --- /dev/null +++ b/src/ci2_mem.c @@ -0,0 +1,73 @@ +/* - | Copyright | -------------------------------------------------------------- + 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. + + * --------------------------------------------------------------------------*/ +#include "../include/ci2.h" +#include // malloc calloc free +const struct CI2_Exception oom = { ENOMEM, "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, line, CURRENT_FUNC); + } + 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, line, CURRENT_FUNC); + } + 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, line, CURRENT_FUNC); + } + + ptr = tmp; + return ptr; +} diff --git a/tests/04_memory_exception.c b/tests/04_memory_exception.c new file mode 100644 index 0000000..d9abbb0 --- /dev/null +++ b/tests/04_memory_exception.c @@ -0,0 +1,141 @@ +/* - | Copyright | -------------------------------------------------------------- + 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 %ld 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: %d: %s\n", oom.num, oom.msg); + return EXIT_SUCCESS; /* requested behavior */ + } + CI2_FINALLY { + /* cleanup if needed, runs always */ + printf("Have to free allocation of %ld 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_SUCCESS; // Shouldn't but still. +#endif + return EXIT_SUCCESS; +} + +#ifdef CI2_WINDOWS +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + (void)hInstance; + (void)hPrevInstance; + (void)lpCmdLine; + (void) nShowCmd; + if (!ci2_init()) { + MessageBoxA(NULL, "ci2_init failed", "Error", MB_OK | MB_ICONERROR); + return EXIT_FAILURE; + } + static ci2_sys_char *ci2_window_title = L"Win32 CI2 Window Title"; + CI2_Window* window = ci2_create_window(ci2_window_title, 800, 600); + if (!window) { + MessageBoxA(NULL, "ci2_create_window failed", "Error", MB_OK | MB_ICONERROR); + ci2_shutdown(); + return EXIT_FAILURE; + } + + // 4) Main loop: run until ci2_poll_events reports the window should close. + // This loop is friendly to rendering or other per-frame work. + int result = -1; + while (ci2_poll_events(window)) { + // Per-frame update/render would go here. + // Example: simple sleep to avoid busy-looping; adjust to suit your app's timing. + + result = test_uncaught_exception(); + result = test_caught_exception(); + Sleep(1); + #ifdef GUI_DEBUG_CLOSE + break; + #endif + } + + // 5) Destroy window and shutdown ci2. + ci2_destroy_window(window); + ci2_shutdown(); + + return result; +} +#else /* non-Windows (Linux, macOS, etc.) */ +#include +int main(int argc, ci2_sys_char *argv[]) +{ + UNUSED(argc); + UNUSED(argv); + if (!ci2_init()) return 1; + + static ci2_sys_char *ci2_window_title = "Linux CI2 Window Title"; + CI2_Window* window = ci2_create_window(ci2_window_title, 800, 600); + if (!window) return 1; + + int result = -1; + while (ci2_poll_events(window)) { + // Your rendering / game loop goes here + + result = test_uncaught_exception(); + result = test_caught_exception(); + sleep(1); + #ifdef GUI_DEBUG_CLOSE + break; + #endif + } + + ci2_destroy_window(window); + ci2_shutdown(); + return result; +} +#endif