#ifndef SDI2_PAGER_SLABALLOCATOR_H
#define SDI2_PAGER_SLABALLOCATOR_H
#include <new>
#include <assert.h>
template <typename Object, unsigned int _pagesize = 4096>
class SlabAllocator
{
public:
class Pool;
typedef Object Data;
typedef Data* DataPtr;
static const unsigned int pagesize = _pagesize;
static const unsigned int pageheadersize = 2 * sizeof(class Page*)
+ sizeof(unsigned int)
+ sizeof(Data*);
static const unsigned int datarestsize = pagesize - pageheadersize;
static const unsigned int objperpage = datarestsize / sizeof(Data);
static const unsigned int remainingbytes = datarestsize
- objperpage * sizeof(Data);
class Page
{
protected:
class Page *prev, *next;
friend class Pool;
unsigned int usednum;
static const unsigned int objnum = objperpage;
Data* firstfree;
Data body[objperpage];
inline Data** bodyfreeptr(unsigned int i)
{
return reinterpret_cast<Data**>(&body[i]);
}
static inline Data* freeptr(Data *data)
{
return *reinterpret_cast<Data**>(data);
}
public:
inline Page()
: prev(NULL), next(NULL),
usednum(0), firstfree(NULL)
{
firstfree = &body[0];
for(unsigned int i = 0; i < objnum-1; i++)
{
*bodyfreeptr(i) = &body[i+1];
}
*bodyfreeptr(objnum-1) = NULL;
}
inline unsigned int used() const
{ return usednum; }
inline unsigned int available() const
{ return objnum - usednum; }
inline bool contains(Data *obj) const
{
return (&body[0] <= obj && obj <= &body[objnum-1]);
}
inline Data* allocate()
{
if (firstfree == NULL) { assert(0); return NULL; }
Data* obj = firstfree;
firstfree = freeptr(firstfree);
usednum++;
new (obj) Data();
return obj;
}
inline void freeobj(Data *obj)
{
assert(contains(obj));
assert(usednum > 0);
assert(!isFree(obj));
obj->~Data();
*reinterpret_cast<Data**>(obj) = firstfree;
firstfree = obj;
usednum--;
}
inline bool isFree(Data* obj) const
{
Data* iter = firstfree;
while(iter != NULL)
{
if (obj == iter) return true;
iter = freeptr(iter);
}
return false;
}
inline void test() const
{
unsigned int fonum = 0;
Data* fo = firstfree;
while(fo != NULL) {
fonum++;
fo = freeptr(fo);
}
assert(fonum == available());
}
};
class Pool
{
private:
class Page *head;
typedef void* (*alloccallback_t)(unsigned int pagesize);
alloccallback_t alloccallback;
bool requestMoreMemory()
{
void *newmem = alloccallback(pagesize);
if (!newmem) return false;
class Page* newpage = static_cast<class Page*>(newmem);
new (newpage) Page();
addPage(newpage);
return true;
}
public:
Pool()
: head(NULL), alloccallback(NULL)
{
assert(sizeof(Page) <= _pagesize);
}
Pool(class Page* pfirst, unsigned int pnum=1)
: head(NULL), alloccallback(NULL)
{
assert(sizeof(Page) <= _pagesize);
for(unsigned int pi = 0; pi < pnum; pi++) {
addPage(pfirst+pi);
}
}
void setAllocCallback(alloccallback_t acb)
{
alloccallback = acb;
}
void addPage(class Page* p)
{
if (head) {
p->next = head;
head->prev = p;
}
else {
p->next = p;
}
p->prev = p;
head = p;
}
void test() const
{
if (!head) return;
class Page* i = head;
assert(i->prev == i);
while(1)
{
assert(i->next->prev = i);
i->test();
if (i->next == i) break;
i = i->next;
}
}
Data* allocate()
{
class Page* iter = head;
if (!iter) {
panic("pager: out of slab memory - no pages");
return NULL;
}
while(iter->available() == 0)
{
if (iter->next == iter) {
if (alloccallback)
{
if (!requestMoreMemory()) return NULL;
return allocate();
}
panic("pager: out of slab memory");
return NULL;
}
iter = iter->next;
}
return iter->allocate();
}
bool freeobj(Data* obj)
{
class Page* iter = head;
if (!iter) return false;
while(!iter->contains(obj))
{
if (iter->next == iter) return false;
iter = iter->next;
}
iter->freeobj(obj);
return true;
}
};
};
#endif