From 22b586b3d87c91581074cc2a47fe5eba6f10bf0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 3 Aug 2021 20:53:48 +0200 Subject: [PATCH] Add pluggable heap allocator This adds the glfwInitAllocator function for specifying a custom memory allocator to use instead of the C runtime library. The allocator is a struct of type GLFWallocator with fields corresponding to malloc, realloc and free, while the internal API corresponds to calloc, realloc and free. Heap allocation calls are filtered before reaching the user-provided functions, so deallocation of NULL and allocations of zero bytes are not passed on, reallocating NULL is transformed into an allocation and reallocating to size zero is transformed into deallocation. The clearing of a new block to zero is performed by the internal calloc-like function. Closes #544. Fixes #1628. Closes #1947. --- README.md | 3 + docs/intro.dox | 59 +++++++++++++++ docs/news.dox | 21 ++++++ include/GLFW/glfw3.h | 171 +++++++++++++++++++++++++++++++++++++++++++ src/cocoa_init.m | 2 +- src/cocoa_joystick.m | 8 +- src/cocoa_monitor.m | 22 +++--- src/cocoa_window.m | 10 +-- src/egl_context.c | 10 +-- src/glx_context.c | 4 +- src/init.c | 106 +++++++++++++++++++++++++-- src/input.c | 24 +++--- src/internal.h | 5 ++ src/monitor.c | 27 +++---- src/null_init.c | 2 +- src/null_monitor.c | 2 +- src/null_window.c | 2 +- src/osmesa_context.c | 6 +- src/vulkan.c | 6 +- src/wgl_context.c | 13 ++-- src/win32_init.c | 13 ++-- src/win32_joystick.c | 18 ++--- src/win32_monitor.c | 15 ++-- src/win32_window.c | 23 +++--- src/window.c | 4 +- src/wl_init.c | 6 +- src/wl_monitor.c | 2 +- src/wl_window.c | 24 +++--- src/x11_init.c | 4 +- src/x11_monitor.c | 8 +- src/x11_window.c | 34 ++++----- tests/CMakeLists.txt | 5 +- tests/allocator.c | 141 +++++++++++++++++++++++++++++++++++ 33 files changed, 647 insertions(+), 153 deletions(-) create mode 100644 tests/allocator.c diff --git a/README.md b/README.md index 0f5c2b9d..018561e9 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,9 @@ information on what to include when reporting a bug. ## Changelog + - Added `glfwInitAllocator` for setting a custom memory allocator (#544,#1628,#1947) + - Added `GLFWallocator` struct and `GLFWallocatefun`, `GLFWreallocatefun` and + `GLFWdeallocatefun` types (#544,#1628,#1947) - Added `GLFW_RESIZE_NWSE_CURSOR`, `GLFW_RESIZE_NESW_CURSOR`, `GLFW_RESIZE_ALL_CURSOR` and `GLFW_NOT_ALLOWED_CURSOR` cursor shapes (#427) - Added `GLFW_RESIZE_EW_CURSOR` alias for `GLFW_HRESIZE_CURSOR` (#427) diff --git a/docs/intro.dox b/docs/intro.dox index 3593ab1e..4134c84d 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -33,6 +33,7 @@ successfully initialized, and only from the main thread. - @ref glfwGetError - @ref glfwSetErrorCallback - @ref glfwInitHint + - @ref glfwInitAllocator - @ref glfwInit - @ref glfwTerminate @@ -143,6 +144,64 @@ Initialization hint | Default value | Supported v @ref GLFW_X11_XCB_VULKAN_SURFACE | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +@subsection init_allocator Custom heap memory allocator + +The heap memory allocator can be customized before initialization with @ref +glfwInitAllocator. + +@code +GLFWallocator allocator; +allocator.allocate = my_malloc; +allocator.reallocate = my_realloc; +allocator.deallocate = my_free; +allocator.user = NULL; + +glfwInitAllocator(&allocator); +@endcode + +The allocator will be picked up at the beginning of initialization and will be +used until GLFW has been fully terminated. Any allocator set after +initialization will be picked up only at the next initialization. + +The allocator will only be used for allocations that would have been made with +the C standard library. Memory allocations that must be made with platform +specific APIs will still use those. + +The allocation function must have a signature matching @ref GLFWallocatefun. It receives +the desired size, in bytes, and the user pointer passed to @ref glfwInitAllocator and +returns the address to the allocated memory block. + +@code +void* my_malloc(size_t size, void* user) +{ + ... +} +@endcode + +The reallocation function must have a function signature matching @ref GLFWreallocatefun. +It receives the memory block to be reallocated, the new desired size, in bytes, and the user +pointer passed to @ref glfwInitAllocator and returns the address to the resized memory +block. + +@code +void* my_realloc(void* block, size_t size, void* user) +{ + ... +} +@endcode + +The deallocation function must have a function signature matching @ref GLFWdeallocatefun. +It receives the memory block to be deallocated and the user pointer passed to @ref +glfwInitAllocator. + +@code +void my_free(void* block, void* user) +{ + ... +} +@endcode + + @subsection intro_init_terminate Terminating GLFW Before your application exits, you should terminate the GLFW library if it has diff --git a/docs/news.dox b/docs/news.dox index d9ed0b1d..6b1367d5 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -44,6 +44,16 @@ requesting a specific rendering backend when using contexts. +@subsubsection features_34_init_allocator Support for custom memory allocator + +GLFW now supports plugging a custom memory allocator at initialization with @ref +glfwInitAllocator. The allocator is a struct of type @ref GLFWallocator with +function pointers corresponding to the standard library functions `malloc`, +`realloc` and `free`. + +For more information see @ref init_allocator. + + @subsubsection features_34_win32_keymenu Support for keyboard access to Windows window menu GLFW now provides the @@ -118,7 +128,18 @@ then GLFW will fail to initialize. @subsection symbols_34 New symbols in version 3.4 @subsubsection functions_34 New functions in version 3.4 + + - @ref glfwInitAllocator + + @subsubsection types_34 New types in version 3.4 + + - @ref GLFWallocator + - @ref GLFWallocatefun + - @ref GLFWreallocatefun + - @ref GLFWdeallocatefun + + @subsubsection constants_34 New constants in version 3.4 - @ref GLFW_POINTING_HAND_CURSOR diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index a3ba9910..95aac6d9 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1330,6 +1330,131 @@ typedef struct GLFWwindow GLFWwindow; */ typedef struct GLFWcursor GLFWcursor; +/*! @brief The function pointer type for memory allocation callbacks. + * + * This is the function pointer type for memory allocation callbacks. A memory + * allocation callback function has the following signature: + * @code + * void* function_name(size_t size, void* user) + * @endcode + * + * This function must return either a memory block at least `size` bytes long, + * or `NULL` if allocation failed. Note that not all parts of GLFW handle allocation + * failures gracefully yet. + * + * This function may be called during @ref glfwInit but before the library is + * flagged as initialized, as well as during @ref glfwTerminate after the + * library is no longer flagged as initialized. + * + * Any memory allocated by this function will be deallocated during library + * termination or earlier. + * + * The size will always be greater than zero. Allocations of size zero are filtered out + * before reaching the custom allocator. + * + * @param[in] size The minimum size, in bytes, of the memory block. + * @param[in] user The user-defined pointer from the allocator. + * @return The address of the newly allocated memory block, or `NULL` if an + * error occurred. + * + * @pointer_lifetime The returned memory block must be valid at least until it + * is deallocated. + * + * @reentrancy This function should not call any GLFW function. + * + * @thread_safety This function may be called from any thread that calls GLFW functions. + * + * @sa @ref init_allocator + * @sa @ref GLFWallocator + * + * @since Added in version 3.4. + * + * @ingroup init + */ +typedef void* (* GLFWallocatefun)(size_t size, void* user); + +/*! @brief The function pointer type for memory reallocation callbacks. + * + * This is the function pointer type for memory reallocation callbacks. + * A memory reallocation callback function has the following signature: + * @code + * void* function_name(void* block, size_t size, void* user) + * @endcode + * + * This function must return a memory block at least `size` bytes long, or + * `NULL` if allocation failed. Note that not all parts of GLFW handle allocation + * failures gracefully yet. + * + * This function may be called during @ref glfwInit but before the library is + * flagged as initialized, as well as during @ref glfwTerminate after the + * library is no longer flagged as initialized. + * + * Any memory allocated by this function will be deallocated during library + * termination or earlier. + * + * The block address will never be `NULL` and the size will always be greater than zero. + * Reallocations of a block to size zero are converted into deallocations. Reallocations + * of `NULL` to a non-zero size are converted into regular allocations. + * + * @param[in] block The address of the memory block to reallocate. + * @param[in] size The new minimum size, in bytes, of the memory block. + * @param[in] user The user-defined pointer from the allocator. + * @return The address of the newly allocated or resized memory block, or + * `NULL` if an error occurred. + * + * @pointer_lifetime The returned memory block must be valid at least until it + * is deallocated. + * + * @reentrancy This function should not call any GLFW function. + * + * @thread_safety This function may be called from any thread that calls GLFW functions. + * + * @sa @ref init_allocator + * @sa @ref GLFWallocator + * + * @since Added in version 3.4. + * + * @ingroup init + */ +typedef void* (* GLFWreallocatefun)(void* block, size_t size, void* user); + +/*! @brief The function pointer type for memory deallocation callbacks. + * + * This is the function pointer type for memory deallocation callbacks. + * A memory deallocation callback function has the following signature: + * @code + * void function_name(void* block, void* user) + * @endcode + * + * This function may deallocate the specified memory block. This memory block + * will have been allocated with the same allocator. + * + * This function may be called during @ref glfwInit but before the library is + * flagged as initialized, as well as during @ref glfwTerminate after the + * library is no longer flagged as initialized. + * + * The block address will never be `NULL`. Deallocations of `NULL` are filtered out + * before reaching the custom allocator. + * + * @param[in] block The address of the memory block to deallocate. + * @param[in] user The user-defined pointer from the allocator. + * + * @pointer_lifetime The specified memory block will not be accessed by GLFW + * after this function is called. + * + * @reentrancy This function should not call any GLFW function. + * + * @thread_safety This function may be called from any thread that calls GLFW functions. + * + * @sa @ref init_allocator + * @sa @ref GLFWallocator + * + * @since Added in version 3.4. + * + * @ingroup init + */ +typedef void (* GLFWdeallocatefun)(void* block, void* user); + /*! @brief The function pointer type for error callbacks. * * This is the function pointer type for error callbacks. An error callback @@ -1887,6 +2012,23 @@ typedef struct GLFWgamepadstate float axes[6]; } GLFWgamepadstate; +/*! @brief + * + * @sa @ref init_allocator + * @sa @ref glfwInitAllocator + * + * @since Added in version 3.4. + * + * @ingroup init + */ +typedef struct GLFWallocator +{ + GLFWallocatefun allocate; + GLFWreallocatefun reallocate; + GLFWdeallocatefun deallocate; + void* user; +} GLFWallocator; + /************************************************************************* * GLFW API functions @@ -1930,6 +2072,8 @@ typedef struct GLFWgamepadstate * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init + * @sa @ref glfwInitHint + * @sa @ref glfwInitAllocator * @sa @ref glfwTerminate * * @since Added in version 1.0. @@ -2004,6 +2148,33 @@ GLFWAPI void glfwTerminate(void); */ GLFWAPI void glfwInitHint(int hint, int value); +/*! @brief Sets the init allocator to the desired value. + * + * To use the default allocator, call this function with a `NULL` argument. + * + * If you specify an allocator struct, every member must be a valid function + * pointer. If any member is `NULL`, this function emits @ref + * GLFW_INVALID_VALUE and the init allocator is unchanged. + * + * @param[in] allocator The allocator to use at the next initialization, or + * `NULL` to use the default one. + * + * @errors Possible errors include @ref GLFW_INVALID_VALUE. + * + * @pointer_lifetime The specified allocator is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref init_allocator + * @sa @ref glfwInit + * + * @since Added in version 3.4. + * + * @ingroup init + */ +GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator); + /*! @brief Retrieves the version of the GLFW library. * * This function retrieves the major, minor and revision numbers of the GLFW diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 626d95c2..d12f3427 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -602,7 +602,7 @@ void _glfwPlatformTerminate(void) if (_glfw.ns.keyUpMonitor) [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; - free(_glfw.ns.clipboardString); + _glfw_free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 4a64fb09..a8081d2c 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -102,15 +102,15 @@ static void closeJoystick(_GLFWjoystick* js) return; for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); + _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); CFRelease(js->ns.axes); for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); + _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); CFRelease(js->ns.buttons); for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) - free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); + _glfw_free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); CFRelease(js->ns.hats); _glfwFreeJoystick(js); @@ -251,7 +251,7 @@ static void matchCallback(void* context, if (target) { - _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); + _GLFWjoyelementNS* element = _glfw_calloc(1, sizeof(_GLFWjoyelementNS)); element->native = native; element->usage = usage; element->index = (int) CFArrayGetCount(target); diff --git a/src/cocoa_monitor.m b/src/cocoa_monitor.m index 31bf0434..2462beab 100644 --- a/src/cocoa_monitor.m +++ b/src/cocoa_monitor.m @@ -120,7 +120,7 @@ static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen) const CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), kCFStringEncodingUTF8); - char* name = calloc(size + 1, 1); + char* name = _glfw_calloc(size + 1, 1); CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8); CFRelease(info); @@ -301,7 +301,7 @@ void _glfwPollMonitorsNS(void) { uint32_t displayCount; CGGetOnlineDisplayList(0, NULL, &displayCount); - CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID)); + CGDirectDisplayID* displays = _glfw_calloc(displayCount, sizeof(CGDirectDisplayID)); CGGetOnlineDisplayList(displayCount, displays, &displayCount); for (int i = 0; i < _glfw.monitorCount; i++) @@ -311,7 +311,7 @@ void _glfwPollMonitorsNS(void) uint32_t disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); @@ -363,7 +363,7 @@ void _glfwPollMonitorsNS(void) monitor->ns.unitNumber = unitNumber; monitor->ns.screen = screen; - free(name); + _glfw_free(name); CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]); if (CGDisplayModeGetRefreshRate(mode) == 0.0) @@ -379,8 +379,8 @@ void _glfwPollMonitorsNS(void) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } - free(disconnected); - free(displays); + _glfw_free(disconnected); + _glfw_free(displays); } // Change the current video mode @@ -521,7 +521,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); const CFIndex found = CFArrayGetCount(modes); - GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode)); + GLFWvidmode* result = _glfw_calloc(found, sizeof(GLFWvidmode)); for (CFIndex i = 0; i < found; i++) { @@ -569,7 +569,7 @@ GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) @autoreleasepool { uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID); - CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); + CGGammaValue* values = _glfw_calloc(size * 3, sizeof(CGGammaValue)); CGGetDisplayTransferByTable(monitor->ns.displayID, size, @@ -587,7 +587,7 @@ GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535); } - free(values); + _glfw_free(values); return GLFW_TRUE; } // autoreleasepool @@ -597,7 +597,7 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { @autoreleasepool { - CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue)); + CGGammaValue* values = _glfw_calloc(ramp->size * 3, sizeof(CGGammaValue)); for (unsigned int i = 0; i < ramp->size; i++) { @@ -612,7 +612,7 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) values + ramp->size, values + ramp->size * 2); - free(values); + _glfw_free(values); } // autoreleasepool } diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 5b8396b4..6ab0ffa6 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -646,7 +646,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; const NSUInteger count = [urls count]; if (count) { - char** paths = calloc(count, sizeof(char*)); + char** paths = _glfw_calloc(count, sizeof(char*)); for (NSUInteger i = 0; i < count; i++) paths[i] = _glfw_strdup([urls[i] fileSystemRepresentation]); @@ -654,8 +654,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _glfwInputDrop(window, (int) count, (const char**) paths); for (NSUInteger i = 0; i < count; i++) - free(paths[i]); - free(paths); + _glfw_free(paths[i]); + _glfw_free(paths); } return YES; @@ -1747,7 +1747,7 @@ const char* _glfwPlatformGetClipboardString(void) return NULL; } - free(_glfw.ns.clipboardString); + _glfw_free(_glfw.ns.clipboardString); _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]); return _glfw.ns.clipboardString; @@ -1775,7 +1775,7 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) if (type) { - *attribs = calloc(3, sizeof(EGLint)); + *attribs = _glfw_calloc(3, sizeof(EGLint)); (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE; (*attribs)[1] = type; (*attribs)[2] = EGL_NONE; diff --git a/src/egl_context.c b/src/egl_context.c index 975c67be..264b233a 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -103,10 +103,10 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, return GLFW_FALSE; } - nativeConfigs = calloc(nativeCount, sizeof(EGLConfig)); + nativeConfigs = _glfw_calloc(nativeCount, sizeof(EGLConfig)); eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableConfigs = _glfw_calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; for (i = 0; i < nativeCount; i++) @@ -183,8 +183,8 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, if (closest) *result = (EGLConfig) closest->handle; - free(nativeConfigs); - free(usableConfigs); + _glfw_free(nativeConfigs); + _glfw_free(usableConfigs); return closest != NULL; } @@ -440,7 +440,7 @@ GLFWbool _glfwInitEGL(void) else _glfw.egl.display = eglGetDisplay(_glfwPlatformGetEGLNativeDisplay()); - free(attribs); + _glfw_free(attribs); if (_glfw.egl.display == EGL_NO_DISPLAY) { diff --git a/src/glx_context.c b/src/glx_context.c index 374c15e0..7207cd55 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -73,7 +73,7 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, return GLFW_FALSE; } - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableConfigs = _glfw_calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; for (i = 0; i < nativeCount; i++) @@ -138,7 +138,7 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, *result = (GLXFBConfig) closest->handle; XFree(nativeConfigs); - free(usableConfigs); + _glfw_free(usableConfigs); return closest != NULL; } diff --git a/src/init.c b/src/init.c index 25a6b4f3..a9f9a54d 100644 --- a/src/init.c +++ b/src/init.c @@ -49,6 +49,7 @@ _GLFWlibrary _glfw = { GLFW_FALSE }; // static _GLFWerror _glfwMainThreadError; static GLFWerrorfun _glfwErrorCallback; +static GLFWallocator _glfwInitAllocator; static _GLFWinitconfig _glfwInitHints = { GLFW_TRUE, // hat buttons @@ -62,6 +63,27 @@ static _GLFWinitconfig _glfwInitHints = }, }; +// The allocation function used when no custom allocator is set +// +static void* defaultAllocate(size_t size, void* user) +{ + return malloc(size); +} + +// The deallocation function used when no custom allocator is set +// +static void defaultDeallocate(void* block, void* user) +{ + free(block); +} + +// The reallocation function used when no custom allocator is set +// +static void* defaultReallocate(void* block, size_t size, void* user) +{ + return realloc(block, size); +} + // Terminate the library // static void terminate(void) @@ -84,11 +106,11 @@ static void terminate(void) _glfwFreeMonitor(monitor); } - free(_glfw.monitors); + _glfw_free(_glfw.monitors); _glfw.monitors = NULL; _glfw.monitorCount = 0; - free(_glfw.mappings); + _glfw_free(_glfw.mappings); _glfw.mappings = NULL; _glfw.mappingCount = 0; @@ -102,7 +124,7 @@ static void terminate(void) { _GLFWerror* error = _glfw.errorListHead; _glfw.errorListHead = error->next; - free(error); + _glfw_free(error); } _glfwPlatformDestroyTls(&_glfw.contextSlot); @@ -120,7 +142,7 @@ static void terminate(void) char* _glfw_strdup(const char* source) { const size_t length = strlen(source); - char* result = calloc(length + 1, 1); + char* result = _glfw_calloc(length + 1, 1); strcpy(result, source); return result; } @@ -149,6 +171,59 @@ float _glfw_fmaxf(float a, float b) return b; } +void* _glfw_calloc(size_t count, size_t size) +{ + if (count && size) + { + void* block; + + if (count > SIZE_MAX / size) + { + _glfwInputError(GLFW_INVALID_VALUE, "Allocation size overflow"); + return NULL; + } + + block = _glfw.allocator.allocate(count * size, _glfw.allocator.user); + if (block) + return memset(block, 0, count * size); + else + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return NULL; + } + } + else + return NULL; +} + +void* _glfw_realloc(void* block, size_t size) +{ + if (block && size) + { + void* resized = _glfw.allocator.reallocate(block, size, _glfw.allocator.user); + if (resized) + return resized; + else + { + _glfwInputError(GLFW_OUT_OF_MEMORY, NULL); + return NULL; + } + } + else if (block) + { + _glfw_free(block); + return NULL; + } + else + return _glfw_calloc(1, size); +} + +void _glfw_free(void* block) +{ + if (block) + _glfw.allocator.deallocate(block, _glfw.allocator.user); +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// @@ -208,7 +283,7 @@ void _glfwInputError(int code, const char* format, ...) error = _glfwPlatformGetTls(&_glfw.errorSlot); if (!error) { - error = calloc(1, sizeof(_GLFWerror)); + error = _glfw_calloc(1, sizeof(_GLFWerror)); _glfwPlatformSetTls(&_glfw.errorSlot, error); _glfwPlatformLockMutex(&_glfw.errorLock); error->next = _glfw.errorListHead; @@ -239,6 +314,14 @@ GLFWAPI int glfwInit(void) memset(&_glfw, 0, sizeof(_glfw)); _glfw.hints.init = _glfwInitHints; + _glfw.allocator = _glfwInitAllocator; + if (!_glfw.allocator.allocate) + { + _glfw.allocator.allocate = defaultAllocate; + _glfw.allocator.reallocate = defaultReallocate; + _glfw.allocator.deallocate = defaultDeallocate; + } + if (!_glfwPlatformInit()) { terminate(); @@ -297,6 +380,19 @@ GLFWAPI void glfwInitHint(int hint, int value) "Invalid init hint 0x%08X", hint); } +GLFWAPI void glfwInitAllocator(const GLFWallocator* allocator) +{ + if (allocator) + { + if (allocator->allocate && allocator->reallocate && allocator->deallocate) + _glfwInitAllocator = *allocator; + else + _glfwInputError(GLFW_INVALID_VALUE, "Missing function in allocator"); + } + else + memset(&_glfwInitAllocator, 0, sizeof(GLFWallocator)); +} + GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) { if (major != NULL) diff --git a/src/input.c b/src/input.c index 4f4830b5..83ef6921 100644 --- a/src/input.c +++ b/src/input.c @@ -419,7 +419,7 @@ void _glfwInitGamepadMappings(void) { size_t i; const size_t count = sizeof(_glfwDefaultMappings) / sizeof(char*); - _glfw.mappings = calloc(count, sizeof(_GLFWmapping)); + _glfw.mappings = _glfw_calloc(count, sizeof(_GLFWmapping)); for (i = 0; i < count; i++) { @@ -450,9 +450,9 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, js = _glfw.joysticks + jid; js->present = GLFW_TRUE; - js->axes = calloc(axisCount, sizeof(float)); - js->buttons = calloc(buttonCount + (size_t) hatCount * 4, 1); - js->hats = calloc(hatCount, 1); + js->axes = _glfw_calloc(axisCount, sizeof(float)); + js->buttons = _glfw_calloc(buttonCount + (size_t) hatCount * 4, 1); + js->hats = _glfw_calloc(hatCount, 1); js->axisCount = axisCount; js->buttonCount = buttonCount; js->hatCount = hatCount; @@ -468,9 +468,9 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, // void _glfwFreeJoystick(_GLFWjoystick* js) { - free(js->axes); - free(js->buttons); - free(js->hats); + _glfw_free(js->axes); + _glfw_free(js->buttons); + _glfw_free(js->hats); memset(js, 0, sizeof(_GLFWjoystick)); } @@ -754,7 +754,7 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - cursor = calloc(1, sizeof(_GLFWcursor)); + cursor = _glfw_calloc(1, sizeof(_GLFWcursor)); cursor->next = _glfw.cursorListHead; _glfw.cursorListHead = cursor; @@ -788,7 +788,7 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) return NULL; } - cursor = calloc(1, sizeof(_GLFWcursor)); + cursor = _glfw_calloc(1, sizeof(_GLFWcursor)); cursor->next = _glfw.cursorListHead; _glfw.cursorListHead = cursor; @@ -833,7 +833,7 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) *prev = cursor->next; } - free(cursor); + _glfw_free(cursor); } GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) @@ -1191,8 +1191,8 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string) { _glfw.mappingCount++; _glfw.mappings = - realloc(_glfw.mappings, - sizeof(_GLFWmapping) * _glfw.mappingCount); + _glfw_realloc(_glfw.mappings, + sizeof(_GLFWmapping) * _glfw.mappingCount); _glfw.mappings[_glfw.mappingCount - 1] = mapping; } } diff --git a/src/internal.h b/src/internal.h index efa7e5b8..f41db3c3 100644 --- a/src/internal.h +++ b/src/internal.h @@ -527,6 +527,7 @@ struct _GLFWmutex struct _GLFWlibrary { GLFWbool initialized; + GLFWallocator allocator; struct { _GLFWinitconfig init; @@ -798,3 +799,7 @@ char* _glfw_strdup(const char* source); float _glfw_fminf(float a, float b); float _glfw_fmaxf(float a, float b); +void* _glfw_calloc(size_t count, size_t size); +void* _glfw_realloc(void* pointer, size_t size); +void _glfw_free(void* pointer); + diff --git a/src/monitor.c b/src/monitor.c index bde93010..938582b2 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -80,7 +80,7 @@ static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); - free(monitor->modes); + _glfw_free(monitor->modes); monitor->modes = modes; monitor->modeCount = modeCount; @@ -100,7 +100,8 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) { _glfw.monitorCount++; _glfw.monitors = - realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount); + _glfw_realloc(_glfw.monitors, + sizeof(_GLFWmonitor*) * _glfw.monitorCount); if (placement == _GLFW_INSERT_FIRST) { @@ -166,7 +167,7 @@ void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) // _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) { - _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); + _GLFWmonitor* monitor = _glfw_calloc(1, sizeof(_GLFWmonitor)); monitor->widthMM = widthMM; monitor->heightMM = heightMM; @@ -187,17 +188,17 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor) _glfwFreeGammaArrays(&monitor->originalRamp); _glfwFreeGammaArrays(&monitor->currentRamp); - free(monitor->modes); - free(monitor); + _glfw_free(monitor->modes); + _glfw_free(monitor); } // Allocates red, green and blue value arrays of the specified size // void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) { - ramp->red = calloc(size, sizeof(unsigned short)); - ramp->green = calloc(size, sizeof(unsigned short)); - ramp->blue = calloc(size, sizeof(unsigned short)); + ramp->red = _glfw_calloc(size, sizeof(unsigned short)); + ramp->green = _glfw_calloc(size, sizeof(unsigned short)); + ramp->blue = _glfw_calloc(size, sizeof(unsigned short)); ramp->size = size; } @@ -205,9 +206,9 @@ void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) // void _glfwFreeGammaArrays(GLFWgammaramp* ramp) { - free(ramp->red); - free(ramp->green); - free(ramp->blue); + _glfw_free(ramp->red); + _glfw_free(ramp->green); + _glfw_free(ramp->blue); memset(ramp, 0, sizeof(GLFWgammaramp)); } @@ -472,7 +473,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) if (!original) return; - values = calloc(original->size, sizeof(unsigned short)); + values = _glfw_calloc(original->size, sizeof(unsigned short)); for (i = 0; i < original->size; i++) { @@ -494,7 +495,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) ramp.size = original->size; glfwSetGammaRamp(handle, &ramp); - free(values); + _glfw_free(values); } GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) diff --git a/src/null_init.c b/src/null_init.c index 57aafd5d..68599d1f 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -46,7 +46,7 @@ int _glfwPlatformInit(void) void _glfwPlatformTerminate(void) { - free(_glfw.null.clipboardString); + _glfw_free(_glfw.null.clipboardString); _glfwTerminateOSMesa(); } diff --git a/src/null_monitor.c b/src/null_monitor.c index 8301eb3a..a071e0c4 100644 --- a/src/null_monitor.c +++ b/src/null_monitor.c @@ -105,7 +105,7 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { - GLFWvidmode* mode = calloc(1, sizeof(GLFWvidmode)); + GLFWvidmode* mode = _glfw_calloc(1, sizeof(GLFWvidmode)); *mode = getVideoMode(); *found = 1; return mode; diff --git a/src/null_window.c b/src/null_window.c index b81ab601..1f59540e 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -513,7 +513,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) void _glfwPlatformSetClipboardString(const char* string) { char* copy = _glfw_strdup(string); - free(_glfw.null.clipboardString); + _glfw_free(_glfw.null.clipboardString); _glfw.null.clipboardString = copy; } diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 70e8675b..74a9291b 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -46,10 +46,10 @@ static void makeContextCurrentOSMesa(_GLFWwindow* window) (width != window->context.osmesa.width) || (height != window->context.osmesa.height)) { - free(window->context.osmesa.buffer); + _glfw_free(window->context.osmesa.buffer); // Allocate the new buffer (width * height * 8-bit RGBA) - window->context.osmesa.buffer = calloc(4, (size_t) width * height); + window->context.osmesa.buffer = _glfw_calloc(4, (size_t) width * height); window->context.osmesa.width = width; window->context.osmesa.height = height; } @@ -83,7 +83,7 @@ static void destroyContextOSMesa(_GLFWwindow* window) if (window->context.osmesa.buffer) { - free(window->context.osmesa.buffer); + _glfw_free(window->context.osmesa.buffer); window->context.osmesa.width = 0; window->context.osmesa.height = 0; } diff --git a/src/vulkan.c b/src/vulkan.c index b5340520..e810ca06 100644 --- a/src/vulkan.c +++ b/src/vulkan.c @@ -108,7 +108,7 @@ GLFWbool _glfwInitVulkan(int mode) return GLFW_FALSE; } - ep = calloc(count, sizeof(VkExtensionProperties)); + ep = _glfw_calloc(count, sizeof(VkExtensionProperties)); err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); if (err) @@ -117,7 +117,7 @@ GLFWbool _glfwInitVulkan(int mode) "Vulkan: Failed to query instance extensions: %s", _glfwGetVulkanResultString(err)); - free(ep); + _glfw_free(ep); _glfwTerminateVulkan(); return GLFW_FALSE; } @@ -145,7 +145,7 @@ GLFWbool _glfwInitVulkan(int mode) #endif } - free(ep); + _glfw_free(ep); _glfw.vk.available = GLFW_TRUE; diff --git a/src/wgl_context.c b/src/wgl_context.c index 5b69f8bc..62055847 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -30,7 +30,6 @@ #include "internal.h" #include -#include #include // Return the value corresponding to the specified attribute @@ -130,7 +129,7 @@ static int choosePixelFormat(_GLFWwindow* window, NULL); } - usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableConfigs = _glfw_calloc(nativeCount, sizeof(_GLFWfbconfig)); for (i = 0; i < nativeCount; i++) { @@ -149,7 +148,7 @@ static int choosePixelFormat(_GLFWwindow* window, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to retrieve pixel format attributes"); - free(usableConfigs); + _glfw_free(usableConfigs); return 0; } @@ -221,7 +220,7 @@ static int choosePixelFormat(_GLFWwindow* window, _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to describe pixel format"); - free(usableConfigs); + _glfw_free(usableConfigs); return 0; } @@ -271,7 +270,7 @@ static int choosePixelFormat(_GLFWwindow* window, _glfwInputError(GLFW_API_UNAVAILABLE, "WGL: The driver does not appear to support OpenGL"); - free(usableConfigs); + _glfw_free(usableConfigs); return 0; } @@ -281,12 +280,12 @@ static int choosePixelFormat(_GLFWwindow* window, _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "WGL: Failed to find a suitable pixel format"); - free(usableConfigs); + _glfw_free(usableConfigs); return 0; } pixelFormat = (int) closest->handle; - free(usableConfigs); + _glfw_free(usableConfigs); return pixelFormat; } diff --git a/src/win32_init.c b/src/win32_init.c index 549c64c7..c5370230 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -30,7 +30,6 @@ #include "internal.h" #include -#include static const GUID _glfw_GUID_DEVINTERFACE_HID = {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; @@ -405,13 +404,13 @@ WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) return NULL; } - target = calloc(count, sizeof(WCHAR)); + target = _glfw_calloc(count, sizeof(WCHAR)); if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string from UTF-8"); - free(target); + _glfw_free(target); return NULL; } @@ -433,13 +432,13 @@ char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) return NULL; } - target = calloc(size, 1); + target = _glfw_calloc(size, 1); if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string to UTF-8"); - free(target); + _glfw_free(target); return NULL; } @@ -593,8 +592,8 @@ void _glfwPlatformTerminate(void) _glfwUnregisterWindowClassWin32(); - free(_glfw.win32.clipboardString); - free(_glfw.win32.rawInput); + _glfw_free(_glfw.win32.clipboardString); + _glfw_free(_glfw.win32.rawInput); _glfwTerminateWGL(); _glfwTerminateEGL(); diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 9c71d114..3ac29d1d 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -199,11 +199,11 @@ static GLFWbool supportsXInput(const GUID* guid) if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) return GLFW_FALSE; - ridl = calloc(count, sizeof(RAWINPUTDEVICELIST)); + ridl = _glfw_calloc(count, sizeof(RAWINPUTDEVICELIST)); if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) { - free(ridl); + _glfw_free(ridl); return GLFW_FALSE; } @@ -248,7 +248,7 @@ static GLFWbool supportsXInput(const GUID* guid) } } - free(ridl); + _glfw_free(ridl); return result; } @@ -262,7 +262,7 @@ static void closeJoystick(_GLFWjoystick* js) IDirectInputDevice8_Release(js->win32.device); } - free(js->win32.objects); + _glfw_free(js->win32.objects); _glfwFreeJoystick(js); _glfwInputJoystick(js, GLFW_DISCONNECTED); @@ -416,8 +416,8 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) memset(&data, 0, sizeof(data)); data.device = device; - data.objects = calloc(dc.dwAxes + (size_t) dc.dwButtons + dc.dwPOVs, - sizeof(_GLFWjoyobjectWin32)); + data.objects = _glfw_calloc(dc.dwAxes + (size_t) dc.dwButtons + dc.dwPOVs, + sizeof(_GLFWjoyobjectWin32)); if (FAILED(IDirectInputDevice8_EnumObjects(device, deviceObjectCallback, @@ -428,7 +428,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) "Win32: Failed to enumerate device objects"); IDirectInputDevice8_Release(device); - free(data.objects); + _glfw_free(data.objects); return DIENUM_CONTINUE; } @@ -445,7 +445,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) "Win32: Failed to convert joystick name to UTF-8"); IDirectInputDevice8_Release(device); - free(data.objects); + _glfw_free(data.objects); return DIENUM_STOP; } @@ -473,7 +473,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) if (!js) { IDirectInputDevice8_Release(device); - free(data.objects); + _glfw_free(data.objects); return DIENUM_STOP; } diff --git a/src/win32_monitor.c b/src/win32_monitor.c index b4c53e4c..10e3d282 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -32,7 +32,6 @@ #include #include #include -#include #include @@ -96,7 +95,7 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DeleteDC(dc); monitor = _glfwAllocMonitor(name, widthMM, heightMM); - free(name); + _glfw_free(name); if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) monitor->win32.modesPruned = GLFW_TRUE; @@ -145,7 +144,7 @@ void _glfwPollMonitorsWin32(void) disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); @@ -197,7 +196,7 @@ void _glfwPollMonitorsWin32(void) monitor = createMonitor(&adapter, &display); if (!monitor) { - free(disconnected); + _glfw_free(disconnected); return; } @@ -227,7 +226,7 @@ void _glfwPollMonitorsWin32(void) monitor = createMonitor(&adapter, NULL); if (!monitor) { - free(disconnected); + _glfw_free(disconnected); return; } @@ -241,7 +240,7 @@ void _glfwPollMonitorsWin32(void) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } - free(disconnected); + _glfw_free(disconnected); } // Change the current video mode @@ -442,7 +441,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) if (*count == size) { size += 128; - result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode)); + result = (GLFWvidmode*) _glfw_realloc(result, size * sizeof(GLFWvidmode)); } (*count)++; @@ -452,7 +451,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) if (!*count) { // HACK: Report the current mode if no valid modes were found - result = calloc(1, sizeof(GLFWvidmode)); + result = _glfw_calloc(1, sizeof(GLFWvidmode)); _glfwPlatformGetVideoMode(monitor, result); *count = 1; } diff --git a/src/win32_window.c b/src/win32_window.c index 13126292..80ae7c79 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -31,7 +31,6 @@ #include #include -#include #include #include #include @@ -884,8 +883,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); if (size > (UINT) _glfw.win32.rawInputSize) { - free(_glfw.win32.rawInput); - _glfw.win32.rawInput = calloc(size, 1); + _glfw_free(_glfw.win32.rawInput); + _glfw.win32.rawInput = _glfw_calloc(size, 1); _glfw.win32.rawInputSize = size; } @@ -1185,7 +1184,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, int i; const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); - char** paths = calloc(count, sizeof(char*)); + char** paths = _glfw_calloc(count, sizeof(char*)); // Move the mouse to the position of the drop DragQueryPoint(drop, &pt); @@ -1194,19 +1193,19 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, for (i = 0; i < count; i++) { const UINT length = DragQueryFileW(drop, i, NULL, 0); - WCHAR* buffer = calloc((size_t) length + 1, sizeof(WCHAR)); + WCHAR* buffer = _glfw_calloc((size_t) length + 1, sizeof(WCHAR)); DragQueryFileW(drop, i, buffer, length + 1); paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); - free(buffer); + _glfw_free(buffer); } _glfwInputDrop(window, count, (const char**) paths); for (i = 0; i < count; i++) - free(paths[i]); - free(paths); + _glfw_free(paths[i]); + _glfw_free(paths); DragFinish(drop); return 0; @@ -1269,7 +1268,7 @@ static int createNativeWindow(_GLFWwindow* window, GetModuleHandleW(NULL), (LPVOID) wndconfig); - free(wideTitle); + _glfw_free(wideTitle); if (!window->win32.handle) { @@ -1469,7 +1468,7 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) return; SetWindowTextW(window->win32.handle, wideTitle); - free(wideTitle); + _glfw_free(wideTitle); } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, @@ -2270,7 +2269,7 @@ const char* _glfwPlatformGetClipboardString(void) return NULL; } - free(_glfw.win32.clipboardString); + _glfw_free(_glfw.win32.clipboardString); _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer); GlobalUnlock(object); @@ -2309,7 +2308,7 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) if (type) { - *attribs = calloc(3, sizeof(EGLint)); + *attribs = _glfw_calloc(3, sizeof(EGLint)); (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE; (*attribs)[1] = type; (*attribs)[2] = EGL_NONE; diff --git a/src/window.c b/src/window.c index 518b27fd..93d8a14c 100644 --- a/src/window.c +++ b/src/window.c @@ -186,7 +186,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, if (!_glfwIsValidContextConfig(&ctxconfig)) return NULL; - window = calloc(1, sizeof(_GLFWwindow)); + window = _glfw_calloc(1, sizeof(_GLFWwindow)); window->next = _glfw.windowListHead; _glfw.windowListHead = window; @@ -480,7 +480,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) *prev = window->next; } - free(window); + _glfw_free(window); } GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) diff --git a/src/wl_init.c b/src/wl_init.c index 3d0236f2..f80b4950 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -1273,7 +1273,7 @@ int _glfwPlatformInit(void) wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat); wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); - _glfw.wl.clipboardString = malloc(4096); + _glfw.wl.clipboardString = _glfw_calloc(4096, 1); if (!_glfw.wl.clipboardString) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1369,9 +1369,9 @@ void _glfwPlatformTerminate(void) close(_glfw.wl.cursorTimerfd); if (_glfw.wl.clipboardString) - free(_glfw.wl.clipboardString); + _glfw_free(_glfw.wl.clipboardString); if (_glfw.wl.clipboardSendString) - free(_glfw.wl.clipboardSendString); + _glfw_free(_glfw.wl.clipboardSendString); } const char* _glfwPlatformGetVersionString(void) diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 74907c79..11f586ad 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -77,7 +77,7 @@ static void outputHandleMode(void* data, monitor->modeCount++; monitor->modes = - realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); + _glfw_realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); monitor->modes[monitor->modeCount - 1] = mode; if (flags & WL_OUTPUT_MODE_CURRENT) diff --git a/src/wl_window.c b/src/wl_window.c index f8b50df1..dd6f26d5 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -112,12 +112,12 @@ static int createAnonymousFile(off_t size) return -1; } - name = calloc(strlen(path) + sizeof(template), 1); + name = _glfw_calloc(strlen(path) + sizeof(template), 1); strcpy(name, path); strcat(name, template); fd = createTmpfileCloexec(name); - free(name); + _glfw_free(name); if (fd < 0) return -1; } @@ -382,8 +382,8 @@ static void surfaceHandleEnter(void *data, { ++window->wl.monitorsSize; window->wl.monitors = - realloc(window->wl.monitors, - window->wl.monitorsSize * sizeof(_GLFWmonitor*)); + _glfw_realloc(window->wl.monitors, + window->wl.monitorsSize * sizeof(_GLFWmonitor*)); } window->wl.monitors[window->wl.monitorsCount++] = monitor; @@ -837,7 +837,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, window->wl.currentCursor = NULL; - window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*)); + window->wl.monitors = _glfw_calloc(1, sizeof(_GLFWmonitor*)); window->wl.monitorsCount = 0; window->wl.monitorsSize = 1; @@ -882,14 +882,14 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->wl.surface) wl_surface_destroy(window->wl.surface); - free(window->wl.title); - free(window->wl.monitors); + _glfw_free(window->wl.title); + _glfw_free(window->wl.monitors); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { if (window->wl.title) - free(window->wl.title); + _glfw_free(window->wl.title); window->wl.title = _glfw_strdup(title); if (window->wl.xdg.toplevel) xdg_toplevel_set_title(window->wl.xdg.toplevel, title); @@ -1608,11 +1608,11 @@ void _glfwPlatformSetClipboardString(const char* string) if (_glfw.wl.clipboardSendString) { - free(_glfw.wl.clipboardSendString); + _glfw_free(_glfw.wl.clipboardSendString); _glfw.wl.clipboardSendString = NULL; } - _glfw.wl.clipboardSendString = strdup(string); + _glfw.wl.clipboardSendString = _glfw_strdup(string); if (!_glfw.wl.clipboardSendString) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1626,7 +1626,7 @@ void _glfwPlatformSetClipboardString(const char* string) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Impossible to create clipboard source"); - free(_glfw.wl.clipboardSendString); + _glfw_free(_glfw.wl.clipboardSendString); return; } wl_data_source_add_listener(_glfw.wl.dataSource, @@ -1642,7 +1642,7 @@ static GLFWbool growClipboardString(void) { char* clipboard = _glfw.wl.clipboardString; - clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2); + clipboard = _glfw_realloc(clipboard, _glfw.wl.clipboardSize * 2); if (!clipboard) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/x11_init.c b/src/x11_init.c index 6287514b..5a5154cc 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1407,8 +1407,8 @@ void _glfwPlatformTerminate(void) _glfw.x11.hiddenCursorHandle = (Cursor) 0; } - free(_glfw.x11.primarySelectionString); - free(_glfw.x11.clipboardString); + _glfw_free(_glfw.x11.primarySelectionString); + _glfw_free(_glfw.x11.clipboardString); XUnregisterIMInstantiateCallback(_glfw.x11.display, NULL, NULL, NULL, diff --git a/src/x11_monitor.c b/src/x11_monitor.c index 8de86596..97da48db 100644 --- a/src/x11_monitor.c +++ b/src/x11_monitor.c @@ -116,7 +116,7 @@ void _glfwPollMonitorsX11(void) disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { - disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); @@ -209,7 +209,7 @@ void _glfwPollMonitorsX11(void) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } - free(disconnected); + _glfw_free(disconnected); } else { @@ -450,7 +450,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); - result = calloc(oi->nmode, sizeof(GLFWvidmode)); + result = _glfw_calloc(oi->nmode, sizeof(GLFWvidmode)); for (int i = 0; i < oi->nmode; i++) { @@ -482,7 +482,7 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) else { *count = 1; - result = calloc(1, sizeof(GLFWvidmode)); + result = _glfw_calloc(1, sizeof(GLFWvidmode)); _glfwPlatformGetVideoMode(monitor, result); } diff --git a/src/x11_window.c b/src/x11_window.c index a360c15c..297b975d 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -406,8 +406,8 @@ static char** parseUriList(char* text, int* count) (*count)++; - char* path = calloc(strlen(line) + 1, 1); - paths = realloc(paths, *count * sizeof(char*)); + char* path = _glfw_calloc(strlen(line) + 1, 1); + paths = _glfw_realloc(paths, *count * sizeof(char*)); paths[*count - 1] = path; while (*line) @@ -493,7 +493,7 @@ static char* convertLatin1toUTF8(const char* source) for (sp = source; *sp; sp++) size += (*sp & 0x80) ? 2 : 1; - char* target = calloc(size, 1); + char* target = _glfw_calloc(size, 1); char* tp = target; for (sp = source; *sp; sp++) @@ -924,12 +924,12 @@ static void handleSelectionClear(XEvent* event) { if (event->xselectionclear.selection == _glfw.x11.PRIMARY) { - free(_glfw.x11.primarySelectionString); + _glfw_free(_glfw.x11.primarySelectionString); _glfw.x11.primarySelectionString = NULL; } else { - free(_glfw.x11.clipboardString); + _glfw_free(_glfw.x11.clipboardString); _glfw.x11.clipboardString = NULL; } } @@ -968,7 +968,7 @@ static const char* getSelectionString(Atom selection) return *selectionString; } - free(*selectionString); + _glfw_free(*selectionString); *selectionString = NULL; for (size_t i = 0; i < targetCount; i++) @@ -1047,7 +1047,7 @@ static const char* getSelectionString(Atom selection) if (itemCount) { size += itemCount; - string = realloc(string, size); + string = _glfw_realloc(string, size); string[size - itemCount - 1] = '\0'; strcat(string, data); } @@ -1057,7 +1057,7 @@ static const char* getSelectionString(Atom selection) if (targets[i] == XA_STRING) { *selectionString = convertLatin1toUTF8(string); - free(string); + _glfw_free(string); } else *selectionString = string; @@ -1293,7 +1293,7 @@ static void processEvent(XEvent *event) if (status == XBufferOverflow) { - chars = calloc(count + 1, 1); + chars = _glfw_calloc(count + 1, 1); count = Xutf8LookupString(window->x11.ic, &event->xkey, chars, count, @@ -1309,7 +1309,7 @@ static void processEvent(XEvent *event) } if (chars != buffer) - free(chars); + _glfw_free(chars); } } else @@ -1724,8 +1724,8 @@ static void processEvent(XEvent *event) _glfwInputDrop(window, count, (const char**) paths); for (i = 0; i < count; i++) - free(paths[i]); - free(paths); + _glfw_free(paths[i]); + _glfw_free(paths); } if (data) @@ -2108,7 +2108,7 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, for (i = 0; i < count; i++) longCount += 2 + images[i].width * images[i].height; - long* icon = calloc(longCount, sizeof(long)); + long* icon = _glfw_calloc(longCount, sizeof(long)); long* target = icon; for (i = 0; i < count; i++) @@ -2132,7 +2132,7 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, (unsigned char*) icon, longCount); - free(icon); + _glfw_free(icon); } else { @@ -3045,7 +3045,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) void _glfwPlatformSetClipboardString(const char* string) { char* copy = _glfw_strdup(string); - free(_glfw.x11.clipboardString); + _glfw_free(_glfw.x11.clipboardString); _glfw.x11.clipboardString = copy; XSetSelectionOwner(_glfw.x11.display, @@ -3086,7 +3086,7 @@ EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs) if (type) { - *attribs = calloc(5, sizeof(EGLint)); + *attribs = _glfw_calloc(5, sizeof(EGLint)); (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE; (*attribs)[1] = type; (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE; @@ -3286,7 +3286,7 @@ GLFWAPI void glfwSetX11SelectionString(const char* string) { _GLFW_REQUIRE_INIT(); - free(_glfw.x11.primarySelectionString); + _glfw_free(_glfw.x11.primarySelectionString); _glfw.x11.primarySelectionString = _glfw_strdup(string); XSetSelectionOwner(_glfw.x11.display, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a704bc07..0cc9db44 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,6 +21,7 @@ set(GETOPT "${GLFW_SOURCE_DIR}/deps/getopt.h" set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h" "${GLFW_SOURCE_DIR}/deps/tinycthread.c") +add_executable(allocator allocator.c ${GLAD_GL}) add_executable(clipboard clipboard.c ${GETOPT} ${GLAD_GL}) add_executable(events events.c ${GETOPT} ${GLAD_GL}) add_executable(msaa msaa.c ${GETOPT} ${GLAD_GL}) @@ -51,8 +52,8 @@ endif() set(GUI_ONLY_BINARIES empty gamma icon inputlag joysticks tearing threads timeout title triangle-vulkan window) -set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen - cursor) +set(CONSOLE_BINARIES allocator clipboard events msaa glfwinfo iconify monitors + reopen cursor) set_target_properties(${GUI_ONLY_BINARIES} ${CONSOLE_BINARIES} PROPERTIES C_STANDARD 99 diff --git a/tests/allocator.c b/tests/allocator.c new file mode 100644 index 00000000..ea9b6409 --- /dev/null +++ b/tests/allocator.c @@ -0,0 +1,141 @@ +//======================================================================== +// Custom heap allocator test +// Copyright (c) Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include +#define GLFW_INCLUDE_NONE +#include + +#include +#include +#include + +#define CALL(x) (function_name = #x, x) +static const char* function_name = NULL; + +struct allocator_stats +{ + size_t total; + size_t current; + size_t maximum; +}; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void* allocate(size_t size, void* user) +{ + struct allocator_stats* stats = user; + assert(size > 0); + + stats->total += size; + stats->current += size; + if (stats->current > stats->maximum) + stats->maximum = stats->current; + + printf("%s: allocate %zu bytes (current %zu maximum %zu total %zu)\n", + function_name, size, stats->current, stats->maximum, stats->total); + + size_t* real_block = malloc(size + sizeof(size_t)); + assert(real_block != NULL); + *real_block = size; + return real_block + 1; +} + +static void deallocate(void* block, void* user) +{ + struct allocator_stats* stats = user; + assert(block != NULL); + + size_t* real_block = (size_t*) block - 1; + stats->current -= *real_block; + + printf("%s: deallocate %zu bytes (current %zu maximum %zu total %zu)\n", + function_name, *real_block, stats->current, stats->maximum, stats->total); + + free(real_block); +} + +static void* reallocate(void* block, size_t size, void* user) +{ + struct allocator_stats* stats = user; + assert(block != NULL); + assert(size > 0); + + size_t* real_block = (size_t*) block - 1; + stats->total += size; + stats->current += size - *real_block; + if (stats->current > stats->maximum) + stats->maximum = stats->current; + + printf("%s: reallocate %zu bytes to %zu bytes (current %zu maximum %zu total %zu)\n", + function_name, *real_block, size, stats->current, stats->maximum, stats->total); + + real_block = realloc(real_block, size + sizeof(size_t)); + assert(real_block != NULL); + *real_block = size; + return real_block + 1; +} + +int main(void) +{ + struct allocator_stats stats = {0}; + const GLFWallocator allocator = + { + .allocate = allocate, + .deallocate = deallocate, + .reallocate = reallocate, + .user = &stats + }; + + glfwSetErrorCallback(error_callback); + glfwInitAllocator(&allocator); + + if (!CALL(glfwInit)()) + exit(EXIT_FAILURE); + + GLFWwindow* window = CALL(glfwCreateWindow)(400, 400, "Custom allocator test", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + CALL(glfwMakeContextCurrent)(window); + gladLoadGL(glfwGetProcAddress); + CALL(glfwSwapInterval)(1); + + while (!CALL(glfwWindowShouldClose)(window)) + { + glClear(GL_COLOR_BUFFER_BIT); + CALL(glfwSwapBuffers)(window); + CALL(glfwWaitEvents)(); + } + + CALL(glfwTerminate)(); + exit(EXIT_SUCCESS); +} +