/* ZSlabAllocator.hpp Author: James Russell Created: 9/12/2011 Purpose: Used to pre-allocate a large pool of objects whose size is fixed. These objects can then be allocated and deallocated as needed, which will return a pointer to an already allocated member. Will correctly handle allocation of more objects than is allocated at compile time, but performance will be impacted as another slab of equivalent size to the original is allocated. This is not a multithreaded access allocator. The user of this is required to provide their own thread safety mechanisms. License: TODO */ #pragma once #ifndef _ZSLABALLOCATOR_H #define _ZSLABALLOCATOR_H #include #include #include #include /* Slab Allocator, which holds a contiguous array of objects and leases them out for use. The template parameter T is the type of object to hold. The template parameter N is the size of the slab (how many objects to allocate statically). */ template class ZSlabAllocator { private: DISABLE_COPY_AND_ASSIGN(ZSlabAllocator); protected: /* Allocated slab of objects, that links to the next slab of objects. */ class Slab { private: T Pool[N]; //The object pool T* Handles[N]; //Handles to objects in the object pool size_t HandleIndex; //Handle index to the next available handle Slab* NextPool; //Next pool public: Slab() : HandleIndex(0), NextPool(NULL) { //Populate our handles pool for (size_t i = 0; i < N; i++) Handles[i] = &(Pool[i]); } //Used to verify that a handle belongs to the pool of objects bool IsPoolMember(T* _handle) { uintptr_t handle = (uintptr_t) _handle; uintptr_t poolLower = (uintptr_t) &Pool[0]; uintptr_t poolUpper = (uintptr_t) &Pool[N - 1]; if ((handle >= poolLower) && (handle <= poolUpper)) //Checks to see if handle is within the range of the pool if (((handle - poolLower) % sizeof(T)) == 0) //Checks to see if handle is aligned on an object in the pool return true; return false; } T* Allocate() { T* object = NULL; if (HandleIndex < N) { //Grab an object from the object pool object = Handles[HandleIndex++]; } return object; } void Deallocate(T* object) { Handles[--HandleIndex] = object; } Slab* GetNextPool() { return NextPool; } void SetNextPool(Slab* p) { NextPool = p; } }; //ZSlabAllocator members Slab FirstPool; size_t NrAllocated; size_t NrSlabs; public: /* Default Constructor. */ ZSlabAllocator() : NrAllocated(0), NrSlabs(1) { } /* Destructor. */ ~ZSlabAllocator() { Slab* slab = FirstPool.GetNextPool(); while(slab) { Slab* next = slab->GetNextPool(); zdelete slab; slab = next; } } /* public ZSlabAllocator::Allocate Gets a valid object of type T and returns a pointer. @return (T*) - a valid object pointer of type T */ T* Allocate() { T* object = NULL; Slab* slab = &FirstPool; //Begin with first pool while(object == NULL) { //Any objects left in this pool? object = slab->Allocate(); if(object == NULL) { //Doh, try next pool Slab* next = slab->GetNextPool(); //Unless there is no next pool... if(next == NULL) { //Try to allocate another one Slab* newPool = znew Slab; if(newPool == NULL) return NULL; //Doh, out of memory. :( else NrSlabs++; //Save this pool slab->SetNextPool(newPool); next = newPool; } //Try next pool slab = next; } else NrAllocated++; } return object; } /* public ZSlabAllocator::Capacity Gets the size of the object pool. Returns the total capacity of the pool. @return (size_t) - the capacity of the object pool */ size_t Capacity() const { return NrSlabs * N; } /* public ZSlabAllocator::Count Gets the number of objects contained by the object pool. @return (size_t) - the number of objects contained by this pool */ size_t Available() const { return NrSlabs * N - NrAllocated; } size_t Allocated() const { return NrAllocated; } /* public ZSlabAllocator::Deallocate Allocates an object from the slab. @param _object - a valid object pointer of type T gained through 'Allocate' @return (void) */ void Deallocate(T *_object) { for(Slab* slab = &FirstPool; slab != NULL; slab = slab->GetNextPool()) { if(slab->IsPoolMember(_object)) { NrAllocated--; slab->Deallocate(_object); return; } } ZASSERT_RUNTIME_FAIL("ZSlabAllocator: Object deallocated that was not allocated here!"); } }; #endif