Browse Source

Submitting rmem memory and object pool module (#898)

* Submitting rmem memory and object pool module

* changed 'restrict' to '__restrict' so it can compile for MSVC

Added `const` to parameters for `MemPool_Realloc`

* Update and rename mempool README.txt to mempool_README.md

* Update mempool_README.md

* Update mempool_README.md

* Update and rename objpool README.txt to objpool_README.md

* implementing changes

* updating header for changes.

* forgot to change _RemoveNode to __RemoveNode

* removing l

* removing l

* Updating documentation on MemPool_CleanUp function

* Updating documentation on ObjPool_CleanUp function

* changed *_CleanUp function parameter

Replaced `void*` pointer to pointer param to `void**` so it's more explicit.

* Updating header to reflect changes to the *_CleanUp functions

* A single change for the mempool and a patch for the objpool.

Object Pool Patch: if you deplete the object pool to 0 free blocks and then free back one block, the last given block will be rejected because it was exactly at the memory holding the entire pool.
Mempool change: switched memory aligning the size from the constructor to when allocating.
pull/914/head
Kevin Yonan 5 years ago
committed by Ray
parent
commit
c563b53afb
4 changed files with 751 additions and 0 deletions
  1. +162
    -0
      mempool_README.md
  2. +81
    -0
      objpool_README.md
  3. +433
    -0
      src/rmem.c
  4. +75
    -0
      src/rmem.h

+ 162
- 0
mempool_README.md View File

@ -0,0 +1,162 @@
Raylib Memory Pool
By Kevin 'Assyrianic' Yonan @ https://github.com/assyrianic
About:
The Raylib Memory Pool is a quick, efficient, and minimal free list & stack-based allocator.
Purpose:
Raylib Memory Pool's purpose is the following list:
* A quicker, 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.
Data Implementation:
the memory pool encapsulates two public structs:
`freeList` which is an abstracted doubly linked list consisting of a `head` and `tail` pointer to `struct MemNode`, a `len` that tracks the amount of nodes the linked list holds, `maxNodes` which is used for auto-defragging (explained below), and `autoDefrag` which controls whether auto-defragging will execute or not.
```c
typedef struct MemNode {
size_t size;
struct MemNode *next, *prev;
} MemNode;
typedef struct AllocList {
struct MemNode *head, *tail;
size_t len, maxNodes;
bool autoDefrag : 1;
} AllocList;
```
`Stack` which is an array-based stack consisting of a byte pointer to the entire array `mem`, the base pointer `base` which changes position when allocating, deallocating, or defragging, and `size` which tracks how large the entire stack buffer is.
```c
typedef struct Stack {
uint8_t *mem, *base;
size_t size;
} Stack;
```
```c
typedef struct MemPool {
struct AllocList freeList;
struct Stack stack;
} MemPool;
```
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);
```
To which you create a `struct MemPool` instance and give the function a max amount of memory you wish or require for your data.
Remember not to exceed that memory amount or the allocation functions of the allocator will give you a NULL pointer.
So we create a pool that will malloc 10K bytes.
```c
struct MemPool pool = MemPool_Create(10000);
```
When we finish using the pool's memory, we clean it up by using `MemPool_Destroy`.
```c
MemPool_Destroy(&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);
```
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);
```
`MemPool_Alloc` returns a (zeroed) pointer to a memory block.
```c
// allocate an int pointer.
int *i = MemPool_Alloc(&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`.
```c
// allocate an int pointer.
int *i = MemPool_Realloc(&pool, NULL, sizeof *i);
// resize the pointer into an int array of 10 cells!
i = MemPool_Realloc(&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);
```
`MemPool_Free` 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);
```
`MemPool_CleanUp` instead takes a pointer to an allocated pointer and then calls `MemPool_Free` for that pointer and then sets it to NULL.
```c
// deallocates i and sets the pointer to NULL.
MemPool_CleanUp(&pool, (void **)&i);
// i is now NULL.
```
Using `MemPool_CleanUp` is basically a shorthand way of doing this code:
```c
// i is now deallocated! Remember that i is NOT NULL!
MemPool_Free(&pool, i);
// i is now NULL obviously.
i = NULL;
```
Given that the memory pool is based on a stack allocator, that means to refill the stack, you must free in the opposite order you allocate memory.
You can still free memory out of order but that'll create memory block fragments.
Freed memory blocks that are not absorbed into the stack allocator are placed into the allocator's free list. If you have too many nodes, the allocation functions might take a while to calculate as the allocating functions first check the free list for any memory blocks it can give you before falling back to the stack allocator.
If you have too many nodes (You can check by accessing the `freeList` struct and its `len` member like: `pool.freeList.len`), then you'll have to defragment the free list.
Defragmenting consists of merging together memory blocks that are physically near one another.
you have two options in terms of defragging:
You can manually call the defrag function:
```c
bool MemPool_DeFrag(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);
```
or set it directly from the `freeList` struct member:
```c
pool.freeList.autoDefrag = true; // set to on.
pool.freeList.autoDefrag = false; // set to off.
pool.freeList.autoDefrag ^= true; // toggle on or off.
```
For auto defragging to work, you must also set a maximum memory block node limit.
you can set it directly with the `maxNodes` member in the `freeList` struct member:
```c
// set free memory block node limit to 100!
pool.freeList.maxNodes = 100UL;
```
If auto defragging is not enabled, then the node limit is ignored of course.
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);
```

+ 81
- 0
objpool_README.md View File

@ -0,0 +1,81 @@
Raylib Object Pool
By Kevin 'Assyrianic' Yonan @ https://github.com/assyrianic
About:
The Raylib Object Pool is a fast and minimal fixed-size allocator.
Purpose:
Raylib Object Pool was created as a complement to the Raylib Memory Pool.
Due to the general purpose nature of Raylib Memory Pool, memory block fragmentations can affect allocation and deallocation speeds.
Because of this, the Raylib Object pool succeeds by having no fragmentation and accomodating for allocating fixed-size data while the Raylib memory pool accomodates for allocating variadic/differently sized data.
Implementation:
The object pool is implemented as a hybrid array-stack of cells that are large enough to hold the size of your data at initialization:
```c
typedef struct ObjPool {
struct Stack stack;
size_t objSize, freeBlocks;
} ObjPool;
```
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);
```
To which you create a `struct ObjPool` instance and give the size of your object and how many objects for the pool to hold.
So assume we have a vector struct like:
```c
typedef struct vec3D {
float x,y,z;
} vec3D_t;
```
which will have a size of 12 bytes.
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);
```
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]);
```
The buffer MUST be aligned to the size of `size_t` AND the object size must not be smaller than a `size_t` either.
Next, we start our operations by allocating which will always allocate ONE object...
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);
origin->x = -0.5f;
origin->y = +0.5f;
origin->z = 0.f;
```
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);
```
`ObjPool_Free` will deallocate the object pointer data back to the memory pool.
```c
ObjPool_Free(&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);
```
Which of course is equivalent to:
```c
ObjPool_Free(&vector_pool, origin), origin = NULL;
```

+ 433
- 0
src/rmem.c View File

@ -0,0 +1,433 @@
#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;
}
}
/***************************************************/

+ 75
- 0
src/rmem.h View File

@ -0,0 +1,75 @@
#ifndef RAYLIB_MEMORY_INCLUDED
# define RAYLIB_MEMORY_INCLUDED
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
/************* Memory Pool (mempool.c) *************/
typedef struct MemNode {
size_t size;
struct MemNode *next, *prev;
} MemNode;
typedef struct AllocList {
struct MemNode *head, *tail;
size_t len, maxNodes;
bool autoDefrag : 1;
} AllocList;
typedef struct Stack {
uint8_t *mem, *base;
size_t size;
} Stack;
typedef struct MemPool {
struct AllocList freeList;
struct Stack stack;
} MemPool;
/***************************************************/
/************* Object Pool *************/
typedef struct ObjPool {
struct Stack stack;
size_t objSize, freeBlocks;
} ObjPool;
/***************************************************/
#ifdef __cplusplus
extern "C" {
#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);
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);
size_t MemPool_MemoryRemaining(const struct MemPool mempool);
bool MemPool_DeFrag(struct MemPool *mempool);
void MemPool_ToggleAutoDefrag(struct 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);
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 */

Loading…
Cancel
Save