Skip to content

Instantly share code, notes, and snippets.

@Voltra
Last active February 16, 2019 18:00
Show Gist options
  • Save Voltra/ea60e7d1187ac72b34f1a4b2d5731684 to your computer and use it in GitHub Desktop.
Save Voltra/ea60e7d1187ac72b34f1a4b2d5731684 to your computer and use it in GitHub Desktop.
Dank memory pool in C
#include "./memory_pool.h"
#include <string.h>
struct memory_pool{
size_t batchSize, allocSize, elemSize;
void** available;
void** inUse;
int availableCursor, inUseCursor;
/*
Semantically, the available vector is used as a stack (from end to begin)
inUse is used a a regular vector but would be more performant as a doubly linked list
*/
};
memory_pool make_memory_pool(size_t batchSize, size_t elemSize){
memory_pool this;
this.batchSize = batchSize;
this.allocSize = 0;
this.elemSize = elemSize;
this.available = malloc(sizeof(void*)); //start with a nullptr at the end
this.inUse = malloc(sizeof(void*)); //start with a nullptr at the end
memset(this.available, 0, sizeof(void*));
memset(this.inUse, 0, sizeof(void*));
this.availableCursor = -1;
this.inUseCursor = 0;
return this;
}
void destroy_memory_pool(memory_pool this){
if(this.available != NULL){
for(int i = 0 ; this.available[i] != NULL ; ++i)
free(this.available[i]);
}
if(this.inUse != NULL){
for(int i = 0 ; this.inUse[i] != NULL ; ++i)
free(this.inUse[i]);
}
}
memory_pool* new_memory_pool(size_t batchSize, size_t elemSize){
memory_pool* this = malloc(sizeof(memory_pool));
*this = make_memory_pool(batchSize, elemSize);
return this;
}
void delete_memory_pool(memory_pool* this){
destroy_memory_pool(*this);
free(this);
}
/**
* Allocates memory for this memory_pool
* @param this being the memory_pool to allocate memory for
* @pre mustn't allocate if there's still available memory
*/
void allocate(memory_pool* this){ //TODO: handle realloc failures
//+1 for the NULL terminator
size_t size = this->allocSize + (this->batchSize + 1);
this->available = realloc(this->available, size * sizeof(void*));
this->inUse = realloc(this->inUse, size * sizeof(void*));
for(size_t i = 0 ; i < this->batchSize ; ++i){
this->available[i] = malloc(this->elemSize);
memset(this->available[i], 0 , this->elemSize);
}
this->allocSize = size - 1;
this->availableCursor = this->batchSize - 1;
}
void* acquire(memory_pool* this){
if(this->allocSize == 0 || this->availableCursor < 0)
allocate(this);
void* ptr = this->available[this->availableCursor];
this->available[this->availableCursor] = NULL;
this->availableCursor -= 1;
this->inUse[this->inUseCursor] = ptr;
this->inUseCursor += 1;
return ptr;
}
bool release(memory_pool* this, void* ptr){ //TODO: Fix pb where ptr appears both in available and inUse
if(this->allocSize == 0 || ptr == NULL)
return false;
if(ptr != NULL)
memset(ptr, 0, this->elemSize);
if(this->inUseCursor > 0 && this->inUse[this->inUseCursor-1] == ptr){
//case when releasing the last acquired
this->inUseCursor -= 1;
this->inUse[this->inUseCursor] = NULL;
this->availableCursor += 1;
this->available[this->availableCursor] = ptr;
return true;
}
//case when not releasing the last acquired
//shift everything on its right to the left
void* cur;
size_t i = 0;
do{ //Look for its index
cur = this->inUse[i];
i += 1;
}while(cur != NULL && cur != ptr);
if(cur == NULL)
return false;
size_t j;//i-1 bc increment in do_while even if found
for(j = i-1 ; j < this->allocSize-1 && this->inUse[j+1] != NULL ; ++j)
//in case of early NULL, exit early
this->inUse[j] = this->inUse[j+1];
this->inUse[j+1] = NULL;
this->inUseCursor -= 1;
this->inUse[this->inUseCursor] = NULL;
this->availableCursor += 1;
this->available[this->availableCursor] = ptr;
/*
We can save a lot of time and performance by
replacing inUse with a doubly linked list
*/
return true;
}
#pragma once
#include <stdlib.h>
#include <stdbool.h>
typedef struct memory_pool memory_pool;
/**
* Constructor for a memory pool
* @param batchSize being the amount of allocation per batch
* @param elemSize being the size of a single elements
* @return the constructed memory_pool
*
* @pre batchSize > 0
*/
memory_pool make_memory_pool(size_t batchSize, size_t elemSize);
/**
* Destructor for a memory pool
* @param this being the memory pool to destroy
*/
void destroy_memory_pool(memory_pool this);
/**
* Allocate a pointer to a memory pool
* @param batchSize being the amount of allocation per batch
* @param elemSize being the size of a single elements
* @return the constructed memory_pool pointer
*/
memory_pool* new_memory_pool(size_t batchSize, size_t elemSize);
/**
* Free memory occupied by the memory_pool
* @param this being the memory pool to destroy
*/
void delete_memory_pool(memory_pool* this);
// void allocate(memory_pool*);
/**
* Demand a pointer from the pool
* @param this being the memory pool to use
* @return the allocated pointer
*/
void* acquire(memory_pool* this);
/**
* Release memory acquired from this memory pool
* @param this being the memory pool to use
* @param ptr being the pointer to release
* @return TRUE if everything went well, FALSE otherwise
*
* @pre ptr must have been acquired using the acquire function
* with the given memory_pool
*/
bool release(memory_pool* this, void* ptr);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment