Review rmem functions naming and coding conventions

This commit is contained in:
raysan5 2019-07-15 19:25:55 +02:00
parent c563b53afb
commit e8829538c9
4 changed files with 630 additions and 508 deletions

View File

@ -45,8 +45,8 @@ Explanation & Usage:
The memory pool is designed to be used as a direct object.
We have two constructor functions:
```c
struct MemPool MemPool_Create(size_t bytes);
struct MemPool MemPool_FromBuffer(void *buf, size_t bytes);
struct MemPool CreateMemPool(size_t bytes);
struct MemPool CreateMemPoolFromBuffer(void *buf, size_t bytes);
```
To which you create a `struct MemPool` instance and give the function a max amount of memory you wish or require for your data.
@ -55,65 +55,65 @@ Explanation & Usage:
So we create a pool that will malloc 10K bytes.
```c
struct MemPool pool = MemPool_Create(10000);
struct MemPool pool = CreateMemPool(10000);
```
When we finish using the pool's memory, we clean it up by using `MemPool_Destroy`.
When we finish using the pool's memory, we clean it up by using `DestroyMemPool`.
```c
MemPool_Destroy(&pool);
DestroyMemPool(&pool);
```
Alternatively, if you're not in a position to use any kind of dynamic allocation from the operating system, you have the option to utilize an existing buffer as memory for the mempool:
```c
char mem[64000];
struct MemPool pool = MemPool_FromBuffer(mem, sizeof mem);
struct MemPool pool = CreateMemPoolFromBuffer(mem, sizeof mem);
```
To allocate from the pool, we have two functions:
```c
void *MemPool_Alloc(struct MemPool *mempool, size_t bytes);
void *MemPool_Realloc(struct MemPool *mempool, void *ptr, size_t bytes);
void *MemPoolAlloc(struct MemPool *mempool, size_t bytes);
void *MemPoolRealloc(struct MemPool *mempool, void *ptr, size_t bytes);
```
`MemPool_Alloc` returns a (zeroed) pointer to a memory block.
`MemPoolAlloc` returns a (zeroed) pointer to a memory block.
```c
// allocate an int pointer.
int *i = MemPool_Alloc(&pool, sizeof *i);
int *i = MemPoolAlloc(&pool, sizeof *i);
```
`MemPool_Realloc` works similar but it takes an existing pointers and resizes its data, it does NOT zero the memory as it exists for resizing existing data. Please note that if you resize a smaller size, the data WILL BE TRUNCATED/CUT OFF.
If the `ptr` argument for `MemPool_Realloc`, it will work just like a call to `MemPool_Alloc`.
`MemPoolRealloc` works similar but it takes an existing pointers and resizes its data, it does NOT zero the memory as it exists for resizing existing data. Please note that if you resize a smaller size, the data WILL BE TRUNCATED/CUT OFF.
If the `ptr` argument for `MemPoolRealloc`, it will work just like a call to `MemPoolAlloc`.
```c
// allocate an int pointer.
int *i = MemPool_Realloc(&pool, NULL, sizeof *i);
int *i = MemPoolRealloc(&pool, NULL, sizeof *i);
// resize the pointer into an int array of 10 cells!
i = MemPool_Realloc(&pool, i, sizeof *i * 10);
i = MemPoolRealloc(&pool, i, sizeof *i * 10);
```
To deallocate memory back to the pool, there's also two functions:
```c
void MemPool_Free(struct MemPool *mempool, void *ptr);
void MemPool_CleanUp(struct MemPool *mempool, void **ptrref);
void MemPoolFree(struct MemPool *mempool, void *ptr);
void MemPoolCleanUp(struct MemPool *mempool, void **ptrref);
```
`MemPool_Free` will deallocate the pointer data back to the memory pool.
`MemPoolFree` will deallocate the pointer data back to the memory pool.
```c
// i is now deallocated! Remember that i is NOT NULL!
MemPool_Free(&pool, i);
MemPoolFree(&pool, i);
```
`MemPool_CleanUp` instead takes a pointer to an allocated pointer and then calls `MemPool_Free` for that pointer and then sets it to NULL.
`MemPoolCleanUp` instead takes a pointer to an allocated pointer and then calls `MemPoolFree` for that pointer and then sets it to NULL.
```c
// deallocates i and sets the pointer to NULL.
MemPool_CleanUp(&pool, (void **)&i);
MemPoolCleanUp(&pool, (void **)&i);
// i is now NULL.
```
Using `MemPool_CleanUp` is basically a shorthand way of doing this code:
Using `MemPoolCleanUp` is basically a shorthand way of doing this code:
```c
// i is now deallocated! Remember that i is NOT NULL!
MemPool_Free(&pool, i);
MemPoolFree(&pool, i);
// i is now NULL obviously.
i = NULL;
@ -131,14 +131,14 @@ Explanation & Usage:
you have two options in terms of defragging:
You can manually call the defrag function:
```c
bool MemPool_DeFrag(struct MemPool *mempool);
bool MemPoolDefrag(struct MemPool *mempool);
```
which will return true or false depending if it was able to merge any nodes.
or you can set a node limit and enable auto defragging.
Auto defragging is disabled by default, you can turn it on or off either by calling:
```c
void MemPool_ToggleAutoDefrag(struct MemPool *mempool);
void ToggleMemPoolAutoDefrag(struct MemPool *mempool);
```
or set it directly from the `freeList` struct member:
```c
@ -157,6 +157,6 @@ Explanation & Usage:
Finally, to get the total amount of memory remaining (to make sure you don't accidentally over-allocate), you utilize this function:
```c
size_t MemPool_MemoryRemaining(const struct MemPool mempool);
size_t GetMemPoolFreeMemory(const struct MemPool mempool);
```

View File

@ -22,8 +22,8 @@ Explanation & Usage:
The object pool is designed to be used as a direct object.
We have two constructor functions:
```c
struct ObjPool ObjPool_Create(size_t objsize, size_t len);
struct ObjPool ObjPool_FromBuffer(void *buf, size_t objsize, size_t len);
struct ObjPool CreateObjPool(size_t objsize, size_t len);
struct ObjPool CreateObjPoolFromBuffer(void *buf, size_t objsize, size_t len);
```
To which you create a `struct ObjPool` instance and give the size of your object and how many objects for the pool to hold.
@ -37,13 +37,13 @@ Explanation & Usage:
Now let's create a pool of 3D vectors that holds about 100 3D vectors.
```c
struct ObjPool vector_pool = ObjPool_Create(sizeof(struct vec3D), 100);
struct ObjPool vector_pool = CreateObjPool(sizeof(struct vec3D), 100);
```
Alternatively, if for any reason that you cannot use dynamic memory allocation, you have the option of using an existing buffer for the object pool:
```c
struct vec3D vectors[100];
struct ObjPool vector_pool = ObjPool_FromBuffer(vectors, sizeof(struct vec3D), 1[&vector] - 0[&vector]);
struct ObjPool vector_pool = CreateObjPoolFromBuffer(vectors, sizeof(struct vec3D), 1[&vector] - 0[&vector]);
```
The buffer MUST be aligned to the size of `size_t` AND the object size must not be smaller than a `size_t` either.
@ -52,7 +52,7 @@ Explanation & Usage:
If you need to allocate something like an array of these objects, then you'll have to make an object pool for the array of objects or use Raylib Memory Pool.
Allocation is very simple nonetheless!
```c
struct vec3D *origin = ObjPool_Alloc(&vector_pool);
struct vec3D *origin = ObjPoolAlloc(&vector_pool);
origin->x = -0.5f;
origin->y = +0.5f;
origin->z = 0.f;
@ -61,21 +61,21 @@ Explanation & Usage:
Deallocation itself is also very simple.
There's two deallocation functions available:
```c
void ObjPool_Free(struct ObjPool *objpool, void *ptr);
void ObjPool_CleanUp(struct ObjPool *objpool, void **ptrref);
void ObjPoolFree(struct ObjPool *objpool, void *ptr);
void ObjPoolCleanUp(struct ObjPool *objpool, void **ptrref);
```
`ObjPool_Free` will deallocate the object pointer data back to the memory pool.
`ObjPoolFree` will deallocate the object pointer data back to the memory pool.
```c
ObjPool_Free(&vector_pool, origin);
ObjPoolFree(&vector_pool, origin);
```
Like Raylib memory pool, the Raylib object pool also comes with a convenient clean up function that takes a pointer to an allocated pointer, frees it, and sets the pointer to NULL for you!
```c
ObjPool_CleanUp(&vector_pool, (void **)&origin);
ObjPoolCleanUp(&vector_pool, (void **)&origin);
```
Which of course is equivalent to:
```c
ObjPool_Free(&vector_pool, origin), origin = NULL;
ObjPoolFree(&vector_pool, origin), origin = NULL;
```

View File

@ -1,433 +0,0 @@
#include "rmem.h"
// excessive but just in case.
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) || defined(_MSC_VER)
# ifndef restrict
# define restrict __restrict
# endif
#endif
static inline size_t __AlignSize(const size_t size, const size_t align)
{
return (size + (align-1)) & -align;
}
/************* Memory Pool *************/
static void __RemoveNode(struct MemNode **const node)
{
((*node)->prev != NULL)? ((*node)->prev->next = (*node)->next) : (*node = (*node)->next);
((*node)->next != NULL)? ((*node)->next->prev = (*node)->prev) : (*node = (*node)->prev);
}
struct MemPool MemPool_Create(const size_t size)
{
struct MemPool mempool = {0};
if (size==0UL)
return mempool;
else {
// align the mempool size to at least the size of an alloc node.
mempool.stack.size = size;
mempool.stack.mem = malloc(1 + mempool.stack.size*sizeof *mempool.stack.mem);
if (mempool.stack.mem==NULL) {
mempool.stack.size = 0UL;
return mempool;
} else {
mempool.stack.base = mempool.stack.mem + mempool.stack.size;
return mempool;
}
}
}
struct MemPool MemPool_FromBuffer(void *buf, const size_t size)
{
struct MemPool mempool = {0};
if (size==0UL || buf==NULL || size<=sizeof(struct MemNode))
return mempool;
else {
mempool.stack.size = size;
mempool.stack.mem = buf;
mempool.stack.base = mempool.stack.mem + mempool.stack.size;
return mempool;
}
}
void MemPool_Destroy(struct MemPool *const mempool)
{
if (mempool==NULL || mempool->stack.mem==NULL)
return;
else {
free(mempool->stack.mem);
*mempool = (struct MemPool){0};
}
}
void *MemPool_Alloc(struct MemPool *const mempool, const size_t size)
{
if (mempool==NULL || size==0UL || size > mempool->stack.size)
return NULL;
else {
struct MemNode *new_mem = NULL;
const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t));
if (mempool->freeList.head != NULL) {
const size_t MEM_SPLIT_THRESHOLD = sizeof(intptr_t);
// if the freelist is valid, let's allocate FROM the freelist then!
for (struct MemNode **inode = &mempool->freeList.head; *inode != NULL; inode = &(*inode)->next) {
if ((*inode)->size < ALLOC_SIZE)
continue;
else if ((*inode)->size <= ALLOC_SIZE + MEM_SPLIT_THRESHOLD) {
// close in size - reduce fragmentation by not splitting.
new_mem = *inode;
__RemoveNode(inode);
mempool->freeList.len--;
new_mem->next = new_mem->prev = NULL;
break;
} else {
// split the memory chunk.
new_mem = (struct MemNode *)( (uint8_t *)*inode + ((*inode)->size - ALLOC_SIZE));
(*inode)->size -= ALLOC_SIZE;
new_mem->size = ALLOC_SIZE;
new_mem->next = new_mem->prev = NULL;
break;
}
}
}
if (new_mem==NULL) {
// not enough memory to support the size!
if (mempool->stack.base - ALLOC_SIZE < mempool->stack.mem)
return NULL;
else {
// couldn't allocate from a freelist, allocate from available mempool.
// subtract allocation size from the mempool.
mempool->stack.base -= ALLOC_SIZE;
// use the available mempool space as the new node.
new_mem = (struct MemNode *)mempool->stack.base;
new_mem->size = ALLOC_SIZE;
new_mem->next = new_mem->prev = NULL;
}
}
// visual of the allocation block.
// --------------
// | mem size | lowest addr of block
// | next node |
// --------------
// | alloc'd |
// | memory |
// | space | highest addr of block
// --------------
uint8_t *const final_mem = (uint8_t *)new_mem + sizeof *new_mem;
memset(final_mem, 0, new_mem->size - sizeof *new_mem);
return final_mem;
}
}
void *MemPool_Realloc(struct MemPool *const restrict mempool, void *ptr, const size_t size)
{
if (mempool==NULL || size > mempool->stack.size)
return NULL;
// NULL ptr should make this work like regular Allocation.
else if (ptr==NULL)
return MemPool_Alloc(mempool, size);
else if ((uintptr_t)ptr <= (uintptr_t)mempool->stack.mem)
return NULL;
else {
struct MemNode *node = (struct MemNode *)((uint8_t *)ptr - sizeof *node);
const size_t NODE_SIZE = sizeof *node;
uint8_t *resized_block = MemPool_Alloc(mempool, size);
if (resized_block==NULL)
return NULL;
else {
struct MemNode *resized = (struct MemNode *)(resized_block - sizeof *resized);
memmove(resized_block, ptr, (node->size > resized->size)? (resized->size - NODE_SIZE) : (node->size - NODE_SIZE));
MemPool_Free(mempool, ptr);
return resized_block;
}
}
}
void MemPool_Free(struct MemPool *const restrict mempool, void *ptr)
{
if (mempool==NULL || ptr==NULL || (uintptr_t)ptr <= (uintptr_t)mempool->stack.mem)
return;
else {
// behind the actual pointer data is the allocation info.
struct MemNode *mem_node = (struct MemNode *)((uint8_t *)ptr - sizeof *mem_node);
// make sure the pointer data is valid.
if ((uintptr_t)mem_node < (uintptr_t)mempool->stack.base || ((uintptr_t)mem_node - (uintptr_t)mempool->stack.mem) > mempool->stack.size || mem_node->size==0UL || mem_node->size > mempool->stack.size)
return;
// if the mem_node is right at the stack base ptr, then add it to the stack.
else if ((uintptr_t)mem_node == (uintptr_t)mempool->stack.base) {
mempool->stack.base += mem_node->size;
}
// otherwise, we add it to the free list.
// We also check if the freelist already has the pointer so we can prevent double frees.
else if (mempool->freeList.len==0UL || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size)) {
for (struct MemNode *n = mempool->freeList.head; n != NULL; n = n->next)
if (n==mem_node)
return;
// this code inserts at head.
/*
( mempool->freeList.head==NULL)? (mempool->freeList.tail = mem_node) : (mempool->freeList.head->prev = mem_node);
mem_node->next = mempool->freeList.head;
mempool->freeList.head = mem_node;
mempool->freeList.len++;
*/
// this code insertion sorts where largest size is first.
if (mempool->freeList.head==NULL) {
mempool->freeList.head = mempool->freeList.tail = mem_node;
mempool->freeList.len++;
} else if (mempool->freeList.head->size <= mem_node->size) {
mem_node->next = mempool->freeList.head;
mem_node->next->prev = mem_node;
mempool->freeList.head = mem_node;
mempool->freeList.len++;
} else if (mempool->freeList.tail->size > mem_node->size) {
mem_node->prev = mempool->freeList.tail;
mempool->freeList.tail->next = mem_node;
mempool->freeList.tail = mem_node;
mempool->freeList.len++;
} else {
struct MemNode *n = mempool->freeList.head;
while (n->next != NULL && n->next->size > mem_node->size)
n = n->next;
mem_node->next = n->next;
if (n->next != NULL)
mem_node->next->prev = mem_node;
n->next = mem_node;
mem_node->prev = n;
mempool->freeList.len++;
}
if (mempool->freeList.autoDefrag && mempool->freeList.maxNodes != 0UL && mempool->freeList.len > mempool->freeList.maxNodes)
MemPool_DeFrag(mempool);
}
}
}
void MemPool_CleanUp(struct MemPool *const restrict mempool, void **ptrref)
{
if (mempool==NULL || ptrref==NULL || *ptrref==NULL)
return;
else {
MemPool_Free(mempool, *ptrref);
*ptrref = NULL;
}
}
size_t MemPool_MemoryRemaining(const MemPool mempool)
{
size_t total_remaining = (uintptr_t)mempool.stack.base - (uintptr_t)mempool.stack.mem;
for (struct MemNode *n=mempool.freeList.head; n != NULL; n = n->next)
total_remaining += n->size;
return total_remaining;
}
bool MemPool_DeFrag(struct MemPool *const mempool)
{
if (mempool==NULL)
return false;
else {
// if the memory pool has been entirely released, fully defrag it.
if (mempool->stack.size == MemPool_MemoryRemaining(*mempool)) {
memset(&mempool->freeList, 0, sizeof mempool->freeList);
mempool->stack.base = mempool->stack.mem + mempool->stack.size;
return true;
} else {
const size_t PRE_DEFRAG_LEN = mempool->freeList.len;
struct MemNode **node = &mempool->freeList.head;
while (*node != NULL) {
if ((uintptr_t)*node == (uintptr_t)mempool->stack.base) {
// if node is right at the stack, merge it back into the stack.
mempool->stack.base += (*node)->size;
(*node)->size = 0UL;
__RemoveNode(node);
mempool->freeList.len--;
node = &mempool->freeList.head;
} else if ((uintptr_t)*node + (*node)->size == (uintptr_t)(*node)->next) {
// next node is at a higher address.
(*node)->size += (*node)->next->size;
(*node)->next->size = 0UL;
// <-[P Curr N]-> <-[P Next N]-> <-[P NextNext N]->
//
// |--------------------|
// <-[P Curr N]-> <-[P Next N]-> [P NextNext N]->
if ((*node)->next->next != NULL)
(*node)->next->next->prev = *node;
// <-[P Curr N]-> <-[P NextNext N]->
(*node)->next = (*node)->next->next;
mempool->freeList.len--;
node = &mempool->freeList.head;
} else if ((uintptr_t)*node + (*node)->size == (uintptr_t)(*node)->prev && (*node)->prev->prev != NULL) {
// prev node is at a higher address.
(*node)->size += (*node)->prev->size;
(*node)->prev->size = 0UL;
// <-[P PrevPrev N]-> <-[P Prev N]-> <-[P Curr N]->
//
// |--------------------|
// <-[P PrevPrev N] <-[P Prev N]-> <-[P Curr N]->
(*node)->prev->prev->next = *node;
// <-[P PrevPrev N]-> <-[P Curr N]->
(*node)->prev = (*node)->prev->prev;
mempool->freeList.len--;
node = &mempool->freeList.head;
} else if ((*node)->prev != NULL && (*node)->next != NULL && (uintptr_t)*node - (*node)->next->size == (uintptr_t)(*node)->next) {
// next node is at a lower address.
(*node)->next->size += (*node)->size;
(*node)->size = 0UL;
(*node)->next->prev = (*node)->prev;
(*node)->prev->next = (*node)->next;
mempool->freeList.len--;
node = &mempool->freeList.head;
} else if ((*node)->prev != NULL && (*node)->next != NULL && (uintptr_t)*node - (*node)->prev->size == (uintptr_t)(*node)->prev) {
// prev node is at a lower address.
(*node)->prev->size += (*node)->size;
(*node)->size = 0UL;
(*node)->next->prev = (*node)->prev;
(*node)->prev->next = (*node)->next;
mempool->freeList.len--;
node = &mempool->freeList.head;
} else {
node = &(*node)->next;
}
}
return PRE_DEFRAG_LEN > mempool->freeList.len;
}
}
}
void MemPool_ToggleAutoDefrag(struct MemPool *const mempool)
{
if (mempool==NULL)
return;
else mempool->freeList.autoDefrag ^= true;
}
/***************************************************/
/************* Object Pool *************/
union ObjInfo {
uint8_t *const byte;
size_t *const size;
};
struct ObjPool ObjPool_Create(const size_t objsize, const size_t len)
{
struct ObjPool objpool = {0};
if (len==0UL || objsize==0UL)
return objpool;
else {
objpool.objSize = __AlignSize(objsize, sizeof(size_t));
objpool.stack.size = objpool.freeBlocks = len;
objpool.stack.mem = calloc(objpool.stack.size, objpool.objSize);
if (objpool.stack.mem==NULL) {
objpool.stack.size = 0UL;
return objpool;
} else {
for (size_t i=0; i<objpool.freeBlocks; i++) {
union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] };
*block.size = i + 1;
}
objpool.stack.base = objpool.stack.mem;
return objpool;
}
}
}
struct ObjPool ObjPool_FromBuffer(void *const buf, const size_t objsize, const size_t len)
{
struct ObjPool objpool = {0};
// If the object size isn't large enough to align to a size_t, then we can't use it.
if (buf==NULL || len==0UL || objsize<sizeof(size_t) || objsize*len != __AlignSize(objsize, sizeof(size_t))*len)
return objpool;
else {
objpool.objSize = __AlignSize(objsize, sizeof(size_t));
objpool.stack.size = objpool.freeBlocks = len;
objpool.stack.mem = buf;
for (size_t i=0; i<objpool.freeBlocks; i++) {
union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] };
*block.size = i + 1;
}
objpool.stack.base = objpool.stack.mem;
return objpool;
}
}
void ObjPool_Destroy(struct ObjPool *const objpool)
{
if (objpool==NULL || objpool->stack.mem==NULL)
return;
else {
free(objpool->stack.mem);
*objpool = (struct ObjPool){0};
}
}
void *ObjPool_Alloc(struct ObjPool *const objpool)
{
if (objpool==NULL)
return NULL;
else {
if (objpool->freeBlocks>0UL) {
// for first allocation, head points to the very first index.
// Head = &pool[0];
// ret = Head == ret = &pool[0];
union ObjInfo ret = { .byte = objpool->stack.base };
objpool->freeBlocks--;
// after allocating, we set head to the address of the index that *Head holds.
// Head = &pool[*Head * pool.objsize];
objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + ( *ret.size*objpool->objSize) : NULL;
memset(ret.byte, 0, objpool->objSize);
return ret.byte;
}
else return NULL;
}
}
void ObjPool_Free(struct ObjPool *const restrict objpool, void *ptr)
{
union ObjInfo p = { .byte = ptr };
if (objpool==NULL || ptr==NULL || p.byte < objpool->stack.mem || p.byte > objpool->stack.mem + objpool->stack.size*objpool->objSize)
return;
else {
// when we free our Bointer, we recycle the pointer space to store the previous index
// and then we push it as our new head.
// *p = index of Head in relation to the buffer;
// Head = p;
*p.size = (objpool->stack.base != NULL)? (objpool->stack.base - objpool->stack.mem)/objpool->objSize : objpool->stack.size;
objpool->stack.base = p.byte;
objpool->freeBlocks++;
}
}
void ObjPool_CleanUp(struct ObjPool *const restrict objpool, void **ptrref)
{
if (objpool==NULL || ptrref==NULL || *ptrref==NULL)
return;
else {
ObjPool_Free(objpool, *ptrref);
*ptrref = NULL;
}
}
/***************************************************/

View File

@ -1,20 +1,73 @@
#ifndef RAYLIB_MEMORY_INCLUDED
# define RAYLIB_MEMORY_INCLUDED
/**********************************************************************************************
*
* rmem - raylib memory pool and objects pool
*
* A quick, efficient, and minimal free list and stack-based allocator
*
* PURPOSE:
* - Aquicker, efficient memory allocator alternative to 'malloc' and friends.
* - Reduce the possibilities of memory leaks for beginner developers using Raylib.
* - Being able to flexibly range check memory if necessary.
*
* CONFIGURATION:
*
* #define RMEM_IMPLEMENTATION
* Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation.
*
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2019 Kevin 'Assyrianic' Yonan (@assyrianic) and reviewed by Ramon Santamaria (@raysan5)
*
* 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.
*
**********************************************************************************************/
#ifndef RMEM_H
#define RMEM_H
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
/************* Memory Pool (mempool.c) *************/
typedef struct MemNode {
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
#define RMEMAPI __declspec(dllexport) // We are building library as a Win32 shared library (.dll)
#elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)
#define RMEMAPI __declspec(dllimport) // We are using library as a Win32 shared library (.dll)
#else
#define RMEMAPI // We are building or using library as a static library (or Linux shared library)
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// Memory Pool
typedef struct MemNode MemNode;
struct MemNode {
size_t size;
struct MemNode *next, *prev;
} MemNode;
MemNode *next, *prev;
};
typedef struct AllocList {
struct MemNode *head, *tail;
MemNode *head, *tail;
size_t len, maxNodes;
bool autoDefrag : 1;
} AllocList;
@ -25,51 +78,553 @@ typedef struct Stack {
} Stack;
typedef struct MemPool {
struct AllocList freeList;
struct Stack stack;
AllocList freeList;
Stack stack;
} MemPool;
/***************************************************/
/************* Object Pool *************/
// Object Pool
typedef struct ObjPool {
struct Stack stack;
Stack stack;
size_t objSize, freeBlocks;
} ObjPool;
/***************************************************/
#ifdef __cplusplus
extern "C" {
#if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions
#endif
/************* Memory Pool *************/
struct MemPool MemPool_Create(size_t bytes);
struct MemPool MemPool_FromBuffer(void *buf, size_t bytes);
void MemPool_Destroy(struct MemPool *mempool);
//------------------------------------------------------------------------------------
// Functions Declaration - Memory Pool
//------------------------------------------------------------------------------------
RMEMAPI MemPool CreateMemPool(size_t bytes);
RMEMAPI MemPool CreateMemPoolFromBuffer(void *buf, size_t bytes);
RMEMAPI void DestroyMemPool(MemPool *mempool);
void *MemPool_Alloc(struct MemPool *mempool, size_t bytes);
void *MemPool_Realloc(struct MemPool *mempool, void *ptr, size_t bytes);
void MemPool_Free(struct MemPool *mempool, void *ptr);
void MemPool_CleanUp(struct MemPool *mempool, void **ptrref);
RMEMAPI void *MemPoolAlloc(MemPool *mempool, size_t bytes);
RMEMAPI void *MemPoolRealloc(MemPool *mempool, void *ptr, size_t bytes);
RMEMAPI void MemPoolFree(MemPool *mempool, void *ptr);
RMEMAPI void MemPoolCleanUp(MemPool *mempool, void **ptrref);
RMEMAPI bool MemPoolDefrag(MemPool *mempool);
size_t MemPool_MemoryRemaining(const struct MemPool mempool);
bool MemPool_DeFrag(struct MemPool *mempool);
void MemPool_ToggleAutoDefrag(struct MemPool *mempool);
/***************************************************/
RMEMAPI size_t GetMemPoolFreeMemory(const MemPool mempool);
RMEMAPI void ToggleMemPoolAutoDefrag(MemPool *mempool);
/************* Object Pool (objpool.c) *************/
struct ObjPool ObjPool_Create(size_t objsize, size_t len);
struct ObjPool ObjPool_FromBuffer(void *buf, size_t objsize, size_t len);
void ObjPool_Destroy(struct ObjPool *objpool);
//------------------------------------------------------------------------------------
// Functions Declaration - Object Pool
//------------------------------------------------------------------------------------
RMEMAPI ObjPool CreateObjPool(size_t objsize, size_t len);
RMEMAPI ObjPool CreateObjPoolFromBuffer(void *buf, size_t objsize, size_t len);
RMEMAPI void DestroyObjPool(ObjPool *objpool);
RMEMAPI void *ObjPoolAlloc(ObjPool *objpool);
RMEMAPI void ObjPoolFree(ObjPool *objpool, void *ptr);
RMEMAPI void ObjPoolCleanUp(ObjPool *objpool, void **ptrref);
void *ObjPool_Alloc(struct ObjPool *objpool);
void ObjPool_Free(struct ObjPool *objpool, void *ptr);
void ObjPool_CleanUp(struct ObjPool *objpool, void **ptrref);
/***************************************************/
#ifdef __cplusplus
}
#endif
#endif /* RAYLIB_MEMORY_INCLUDED */
#endif // RMEM_H
/***********************************************************************************
*
* RMEM IMPLEMENTATION
*
************************************************************************************/
#if defined(RMEM_IMPLEMENTATION)
#include <stdio.h> // Required for:
#include <stdlib.h> // Required for:
#include <string.h> // Required for:
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// Make sure restrict type qualifier for pointers is defined
// NOTE: Not supported by C++, it is a C only keyword
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) || defined(_MSC_VER)
#ifndef restrict
#define restrict __restrict
#endif
#endif
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static inline size_t __AlignSize(const size_t size, const size_t align)
{
return (size + (align - 1)) & -align;
}
static void __RemoveNode(MemNode **const node)
{
((*node)->prev != NULL)? ((*node)->prev->next = (*node)->next) : (*node = (*node)->next);
((*node)->next != NULL)? ((*node)->next->prev = (*node)->prev) : (*node = (*node)->prev);
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Memory Pool
//----------------------------------------------------------------------------------
MemPool CreateMemPool(const size_t size)
{
MemPool mempool = { 0 };
if (size == 0UL) return mempool;
else
{
// Align the mempool size to at least the size of an alloc node.
mempool.stack.size = size;
mempool.stack.mem = malloc(1 + mempool.stack.size*sizeof *mempool.stack.mem);
if (mempool.stack.mem==NULL)
{
mempool.stack.size = 0UL;
return mempool;
}
else
{
mempool.stack.base = mempool.stack.mem + mempool.stack.size;
return mempool;
}
}
}
MemPool CreateMemPoolFromBuffer(void *buf, const size_t size)
{
MemPool mempool = { 0 };
if ((size == 0UL) || (buf == NULL) || (size <= sizeof(MemNode))) return mempool;
else
{
mempool.stack.size = size;
mempool.stack.mem = buf;
mempool.stack.base = mempool.stack.mem + mempool.stack.size;
return mempool;
}
}
void DestroyMemPool(MemPool *const mempool)
{
if ((mempool == NULL) || (mempool->stack.mem == NULL)) return;
else
{
free(mempool->stack.mem);
*mempool = (MemPool){ 0 };
}
}
void *MemPoolAlloc(MemPool *const mempool, const size_t size)
{
if ((mempool == NULL) || (size == 0UL) || (size > mempool->stack.size)) return NULL;
else
{
MemNode *new_mem = NULL;
const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t));
if (mempool->freeList.head != NULL)
{
const size_t MEM_SPLIT_THRESHOLD = sizeof(intptr_t);
// If the freelist is valid, let's allocate FROM the freelist then!
for (MemNode **inode = &mempool->freeList.head; *inode != NULL; inode = &(*inode)->next)
{
if ((*inode)->size < ALLOC_SIZE) continue;
else if ((*inode)->size <= (ALLOC_SIZE + MEM_SPLIT_THRESHOLD))
{
// Close in size - reduce fragmentation by not splitting.
new_mem = *inode;
__RemoveNode(inode);
mempool->freeList.len--;
new_mem->next = new_mem->prev = NULL;
break;
}
else
{
// Split the memory chunk.
new_mem = (MemNode *)((uint8_t *)*inode + ((*inode)->size - ALLOC_SIZE));
(*inode)->size -= ALLOC_SIZE;
new_mem->size = ALLOC_SIZE;
new_mem->next = new_mem->prev = NULL;
break;
}
}
}
if (new_mem == NULL)
{
// not enough memory to support the size!
if ((mempool->stack.base - ALLOC_SIZE) < mempool->stack.mem) return NULL;
else
{
// Couldn't allocate from a freelist, allocate from available mempool.
// Subtract allocation size from the mempool.
mempool->stack.base -= ALLOC_SIZE;
// Use the available mempool space as the new node.
new_mem = (MemNode *)mempool->stack.base;
new_mem->size = ALLOC_SIZE;
new_mem->next = new_mem->prev = NULL;
}
}
// Visual of the allocation block.
// --------------
// | mem size | lowest addr of block
// | next node |
// --------------
// | alloc'd |
// | memory |
// | space | highest addr of block
// --------------
uint8_t *const final_mem = (uint8_t *)new_mem + sizeof *new_mem;
memset(final_mem, 0, new_mem->size - sizeof *new_mem);
return final_mem;
}
}
void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t size)
{
if ((mempool == NULL) || (size > mempool->stack.size)) return NULL;
// NULL ptr should make this work like regular Allocation.
else if (ptr == NULL) return MemPoolAlloc(mempool, size);
else if ((uintptr_t)ptr <= (uintptr_t)mempool->stack.mem) return NULL;
else
{
MemNode *node = (MemNode *)((uint8_t *)ptr - sizeof *node);
const size_t NODE_SIZE = sizeof *node;
uint8_t *resized_block = MemPoolAlloc(mempool, size);
if (resized_block == NULL) return NULL;
else
{
MemNode *resized = (MemNode *)(resized_block - sizeof *resized);
memmove(resized_block, ptr, (node->size > resized->size)? (resized->size - NODE_SIZE) : (node->size - NODE_SIZE));
MemPoolFree(mempool, ptr);
return resized_block;
}
}
}
void MemPoolFree(MemPool *const restrict mempool, void *ptr)
{
if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr <= (uintptr_t)mempool->stack.mem)) return;
else
{
// Behind the actual pointer data is the allocation info.
MemNode *mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node);
// Make sure the pointer data is valid.
if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) ||
(((uintptr_t)mem_node - (uintptr_t)mempool->stack.mem) > mempool->stack.size) ||
(mem_node->size == 0UL) ||
(mem_node->size > mempool->stack.size)) return;
// If the mem_node is right at the stack base ptr, then add it to the stack.
else if ((uintptr_t)mem_node == (uintptr_t)mempool->stack.base)
{
mempool->stack.base += mem_node->size;
}
// Otherwise, we add it to the free list.
// We also check if the freelist already has the pointer so we can prevent double frees.
else if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size))
{
for (MemNode *n = mempool->freeList.head; n != NULL; n = n->next) if (n == mem_node) return;
// This code inserts at head.
/*
( mempool->freeList.head==NULL)? (mempool->freeList.tail = mem_node) : (mempool->freeList.head->prev = mem_node);
mem_node->next = mempool->freeList.head;
mempool->freeList.head = mem_node;
mempool->freeList.len++;
*/
// This code insertion sorts where largest size is first.
if (mempool->freeList.head == NULL)
{
mempool->freeList.head = mempool->freeList.tail = mem_node;
mempool->freeList.len++;
}
else if (mempool->freeList.head->size <= mem_node->size)
{
mem_node->next = mempool->freeList.head;
mem_node->next->prev = mem_node;
mempool->freeList.head = mem_node;
mempool->freeList.len++;
}
else if (mempool->freeList.tail->size > mem_node->size)
{
mem_node->prev = mempool->freeList.tail;
mempool->freeList.tail->next = mem_node;
mempool->freeList.tail = mem_node;
mempool->freeList.len++;
}
else
{
MemNode *n = mempool->freeList.head;
while ((n->next != NULL) && (n->next->size > mem_node->size)) n = n->next;
mem_node->next = n->next;
if (n->next != NULL) mem_node->next->prev = mem_node;
n->next = mem_node;
mem_node->prev = n;
mempool->freeList.len++;
}
if (mempool->freeList.autoDefrag && (mempool->freeList.maxNodes != 0UL) && (mempool->freeList.len > mempool->freeList.maxNodes)) MemPoolDefrag(mempool);
}
}
}
void MemPoolCleanUp(MemPool *const restrict mempool, void **ptrref)
{
if ((mempool == NULL) || (ptrref == NULL) || (*ptrref == NULL)) return;
else
{
MemPoolFree(mempool, *ptrref);
*ptrref = NULL;
}
}
size_t GetMemPoolFreeMemory(const MemPool mempool)
{
size_t total_remaining = (uintptr_t)mempool.stack.base - (uintptr_t)mempool.stack.mem;
for (MemNode *n=mempool.freeList.head; n != NULL; n = n->next) total_remaining += n->size;
return total_remaining;
}
bool MemPoolDefrag(MemPool *const mempool)
{
if (mempool == NULL) return false;
else
{
// If the memory pool has been entirely released, fully defrag it.
if (mempool->stack.size == GetMemPoolFreeMemory(*mempool))
{
memset(&mempool->freeList, 0, sizeof mempool->freeList);
mempool->stack.base = mempool->stack.mem + mempool->stack.size;
return true;
}
else
{
const size_t PRE_DEFRAG_LEN = mempool->freeList.len;
MemNode **node = &mempool->freeList.head;
while (*node != NULL)
{
if ((uintptr_t)*node == (uintptr_t)mempool->stack.base)
{
// If node is right at the stack, merge it back into the stack.
mempool->stack.base += (*node)->size;
(*node)->size = 0UL;
__RemoveNode(node);
mempool->freeList.len--;
node = &mempool->freeList.head;
}
else if (((uintptr_t)*node + (*node)->size) == (uintptr_t)(*node)->next)
{
// Next node is at a higher address.
(*node)->size += (*node)->next->size;
(*node)->next->size = 0UL;
// <-[P Curr N]-> <-[P Next N]-> <-[P NextNext N]->
//
// |--------------------|
// <-[P Curr N]-> <-[P Next N]-> [P NextNext N]->
if ((*node)->next->next != NULL) (*node)->next->next->prev = *node;
// <-[P Curr N]-> <-[P NextNext N]->
(*node)->next = (*node)->next->next;
mempool->freeList.len--;
node = &mempool->freeList.head;
}
else if ((((uintptr_t)*node + (*node)->size) == (uintptr_t)(*node)->prev) && ((*node)->prev->prev != NULL))
{
// Prev node is at a higher address.
(*node)->size += (*node)->prev->size;
(*node)->prev->size = 0UL;
// <-[P PrevPrev N]-> <-[P Prev N]-> <-[P Curr N]->
//
// |--------------------|
// <-[P PrevPrev N] <-[P Prev N]-> <-[P Curr N]->
(*node)->prev->prev->next = *node;
// <-[P PrevPrev N]-> <-[P Curr N]->
(*node)->prev = (*node)->prev->prev;
mempool->freeList.len--;
node = &mempool->freeList.head;
}
else if ((*node)->prev != NULL && (*node)->next != NULL && (uintptr_t)*node - (*node)->next->size == (uintptr_t)(*node)->next)
{
// Next node is at a lower address.
(*node)->next->size += (*node)->size;
(*node)->size = 0UL;
(*node)->next->prev = (*node)->prev;
(*node)->prev->next = (*node)->next;
mempool->freeList.len--;
node = &mempool->freeList.head;
}
else if ((*node)->prev != NULL && (*node)->next != NULL && (uintptr_t)*node - (*node)->prev->size == (uintptr_t)(*node)->prev)
{
// Prev node is at a lower address.
(*node)->prev->size += (*node)->size;
(*node)->size = 0UL;
(*node)->next->prev = (*node)->prev;
(*node)->prev->next = (*node)->next;
mempool->freeList.len--;
node = &mempool->freeList.head;
}
else
{
node = &(*node)->next;
}
}
return PRE_DEFRAG_LEN > mempool->freeList.len;
}
}
}
void ToggleMemPoolAutoDefrag(MemPool *const mempool)
{
if (mempool == NULL) return;
else mempool->freeList.autoDefrag ^= true;
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Object Pool
//----------------------------------------------------------------------------------
union ObjInfo {
uint8_t *const byte;
size_t *const size;
};
ObjPool CreateObjPool(const size_t objsize, const size_t len)
{
ObjPool objpool = { 0 };
if ((len == 0UL) || (objsize == 0UL)) return objpool;
else
{
objpool.objSize = __AlignSize(objsize, sizeof(size_t));
objpool.stack.size = objpool.freeBlocks = len;
objpool.stack.mem = calloc(objpool.stack.size, objpool.objSize);
if (objpool.stack.mem == NULL)
{
objpool.stack.size = 0UL;
return objpool;
}
else
{
for (size_t i=0; i<objpool.freeBlocks; i++)
{
union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] };
*block.size = i + 1;
}
objpool.stack.base = objpool.stack.mem;
return objpool;
}
}
}
ObjPool CreateObjPoolFromBuffer(void *const buf, const size_t objsize, const size_t len)
{
ObjPool objpool = { 0 };
// If the object size isn't large enough to align to a size_t, then we can't use it.
if ((buf == NULL) || (len == 0UL) || (objsize < sizeof(size_t)) || (objsize*len != __AlignSize(objsize, sizeof(size_t))*len)) return objpool;
else
{
objpool.objSize = __AlignSize(objsize, sizeof(size_t));
objpool.stack.size = objpool.freeBlocks = len;
objpool.stack.mem = buf;
for (size_t i=0; i<objpool.freeBlocks; i++)
{
union ObjInfo block = { .byte = &objpool.stack.mem[i*objpool.objSize] };
*block.size = i + 1;
}
objpool.stack.base = objpool.stack.mem;
return objpool;
}
}
void DestroyObjPool(ObjPool *const objpool)
{
if ((objpool == NULL) || (objpool->stack.mem == NULL)) return;
else
{
free(objpool->stack.mem);
*objpool = (ObjPool){0};
}
}
void *ObjPoolAlloc(ObjPool *const objpool)
{
if (objpool == NULL) return NULL;
else
{
if (objpool->freeBlocks > 0UL)
{
// For first allocation, head points to the very first index.
// Head = &pool[0];
// ret = Head == ret = &pool[0];
union ObjInfo ret = { .byte = objpool->stack.base };
objpool->freeBlocks--;
// after allocating, we set head to the address of the index that *Head holds.
// Head = &pool[*Head * pool.objsize];
objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.size*objpool->objSize) : NULL;
memset(ret.byte, 0, objpool->objSize);
return ret.byte;
}
else return NULL;
}
}
void ObjPoolFree(ObjPool *const restrict objpool, void *ptr)
{
union ObjInfo p = { .byte = ptr };
if ((objpool == NULL) || (ptr == NULL) || (p.byte < objpool->stack.mem) || (p.byte > objpool->stack.mem + objpool->stack.size*objpool->objSize)) return;
else
{
// When we free our pointer, we recycle the pointer space to store the previous index and then we push it as our new head.
// *p = index of Head in relation to the buffer;
// Head = p;
*p.size = (objpool->stack.base != NULL)? (objpool->stack.base - objpool->stack.mem)/objpool->objSize : objpool->stack.size;
objpool->stack.base = p.byte;
objpool->freeBlocks++;
}
}
void ObjPoolCleanUp(ObjPool *const restrict objpool, void **ptrref)
{
if ((objpool == NULL) || (ptrref == NULL) || (*ptrref == NULL)) return;
else
{
ObjPoolFree(objpool, *ptrref);
*ptrref = NULL;
}
}
#endif // RMEM_IMPLEMENTATION