238 lines
4.6 KiB
C++
238 lines
4.6 KiB
C++
/*
|
|
ZSlabAllocator.hpp
|
|
Author: James Russell <jcrussell@762studios.com>
|
|
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 <ZUtil/ZUtilBuild.hpp>
|
|
#include <ZUtil/ZAlloc.hpp>
|
|
#include <ZUtil/ZAssert.hpp>
|
|
#include <ZUtil/ZLog.hpp>
|
|
|
|
/*
|
|
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 <typename T, size_t N>
|
|
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<T,N>::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<T,N>::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<T,N>::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<T,N>::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
|