mirror of
https://github.com/glfw/glfw.git
synced 2026-09-17 22:32:50 +00:00
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.
This commit is contained in:
+101
-5
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user