When FERRET_MEMORY_POOL_SIZE is defined Ferret programs will use a memory pool called memory::allocator::program_memory instead of mallac , free for memory allocation, depending on the pool size Ferret will allocate N bytes of memory on stack and all memory allocation happens in this memory pool useful when working with very limited amount of memory, such as micro controllers where you want complete control over the memory and you need deterministic timing requirements.

This allocator uses a bit-map to keep track of the used and unused memory locations for its book-keeping purposes. This allocator will make use of 1 single bit to keep track of whether it has been allocated or not. A bit 0 indicates free, while 1 indicates allocated. This has been done so that you can easily check a collection of bits for a free block. This kind of Bitmapped strategy works best for single object allocations ,we do not need to choose any size for the block which will be represented by a single bit. This will be the size of the parameter around which the allocator has been parameterized. Thus, close to optimal performance will result.

#if defined (FERRET_MEMORY_POOL_SIZE) && ! defined (FERRET_ALLOCATOR) #define FERRET_ALLOCATOR memory :: allocator ::pool #if ! defined (FERRET_MEMORY_POOL_PAGE_TYPE) #define FERRET_MEMORY_POOL_PAGE_TYPE size_t #endif namespace memory { namespace allocator { memory_pool <FERRET_MEMORY_POOL_PAGE_TYPE, FERRET_MEMORY_POOL_SIZE> program_memory ; class pool { public : static void init (){ } static inline void * allocate ( size_t s ){ return program_memory.allocate(s); } template < typename FT > static inline void * allocate (){ return allocate( sizeof ( FT )); } static inline void free ( void * ptr ){ program_memory.free(ptr); } }; } } #endif

Pool allocator uses circular first-fit strategy, when allocate is called the pool will scan the memory pool using the used bit array to find a block of memory big enough to satisfy the request. If found, it will the mark the region as used and return a pointer from pool array to the user which points to the memory block.

When a free request is received, we resolve the pointer in to the memory pool read the book keeping information on how much memory is allocated to this pointer and set these pages to unused.

Memory pool has several advantages, it will avoid fragmentation, function related to each other will always keep their data close to each other in the array which improves data locality.

#define FERRET_MEMORY_POOL_SIZE 4_MB #define FERRET_BITSET_WORD_TYPE unsigned int #include <cassert> #include <runtime.h> int main () { using namespace ferret :: memory ; using namespace allocator ; assert(0 == align_req(0,8)); assert(7 == align_req(1,8)); assert(0 == align_req(8,8)); assert(0 == align_of(0,8)); assert(8 == align_of(1,8)); assert(8 == align_of(8,8)); alignas(16) int buff [4]; assert(0 == align_req< int16_t >(buff)); assert( reinterpret_cast < std ::uintptr_t>(buff) == align_of< int16_t >(buff)); size_t byte_s = sizeof ( ferret ::byte); memory_pool < ferret ::byte, 8, unsigned char > nano_pool ; void * a = nano_pool.allocate(byte_s); assert( nullptr != a); assert(2 == nano_pool.used.ffr(0)); assert( nullptr != nano_pool.allocate(byte_s)); assert(4 == nano_pool.used.ffr(0)); void * c = nano_pool.allocate(byte_s); assert( nullptr != c); assert(6 == nano_pool.used.ffr(0)); assert( nullptr != nano_pool.allocate(byte_s)); nano_pool.free(c); assert(4 == nano_pool.used.ffr(0)); assert(6 == nano_pool.used.ffs(4)); assert( nullptr != nano_pool.allocate(byte_s)); memory_pool < ferret ::byte, 16, unsigned char > tiny_pool ; assert(0 == tiny_pool.used.ffr(0)); assert( nullptr != tiny_pool.allocate( byte_s * 2)); assert(3 == tiny_pool.used.ffr(0)); void * p = tiny_pool.allocate( byte_s * 4); assert( nullptr != p); assert(8 == tiny_pool.used.ffr(0)); tiny_pool.free(p); assert(3 == tiny_pool.used.ffr(0)); assert( nullptr == tiny_pool.allocate( byte_s * 40)); assert( nullptr != tiny_pool.allocate( byte_s * 6)); assert( nullptr != tiny_pool.allocate( byte_s * 1)); assert( nullptr != tiny_pool.allocate( byte_s * 1)); assert( nullptr == tiny_pool.allocate( byte_s * 10)); memory_pool < uint64_t , 256> big_pool ; assert(0 == big_pool.used.ffr(0)); p = big_pool.allocate(1); assert( nullptr != p); assert(2 == big_pool.used.ffr(0)); big_pool.free(p); assert(0 == big_pool.used.ffr(0)); assert( nullptr == big_pool.allocate(2048)); assert(0 == big_pool.used.ffr(0)); return 0; }