#ifndef HOARD_EMPTYCLASS_H
#define HOARD_EMPTYCLASS_H
#include "check.h"
#include "array.h"
namespace Hoard {
template <class SuperblockType_,
int EmptinessClasses>
class EmptyClass {
enum { SuperblockSize = sizeof(SuperblockType_) };
public:
typedef SuperblockType_ SuperblockType;
EmptyClass()
{
for (auto i = 0; i <= EmptinessClasses + 1; i++) {
_available(i) = 0;
}
}
void dumpStats() {
for (int i = 0; i <= EmptinessClasses + 1; i++) {
auto * s = _available(i);
if (s) {
while (s) {
s->dumpStats();
s = s->getNext();
}
}
}
}
SuperblockType * getEmpty() {
Check<EmptyClass, MyChecker> check (this);
auto * s = _available(0);
if (s &&
(s->getObjectsFree() == s->getTotalObjects())) {
_available(0) = s->getNext();
if (_available(0)) {
_available(0)->setPrev (0);
}
s->setPrev (0);
s->setNext (0);
return s;
}
return 0;
}
SuperblockType * get() {
Check<EmptyClass, MyChecker> check (this);
for (auto n = 0; n < EmptinessClasses + 1; n++) {
auto * s = _available(n);
while (s) {
assert (s->isValidSuperblock());
_available(n) = s->getNext();
if (_available(n)) {
_available(n)->setPrev (0);
}
s->setPrev (0);
s->setNext (0);
#ifndef NDEBUG
for (int z = 0; z < EmptinessClasses + 1; z++) {
auto * p = _available(z);
while (p) {
assert (p != s);
p = p->getNext();
}
}
#endif
auto cl = getFullness (s);
if (cl > n) {
put (s);
SuperblockType * sNew = _available(n);
assert (s != sNew);
s = sNew;
} else {
return s;
}
}
}
return 0;
}
void put (SuperblockType * s) {
Check<EmptyClass, MyChecker> check (this);
#ifndef NDEBUG
for (int n = 0; n <= EmptinessClasses + 1; n++) {
auto * p = _available(n);
while (p) {
if (p == s) {
abort();
}
p = p->getNext();
}
}
#endif
auto cl = getFullness (s);
s->setPrev (0);
s->setNext (_available(cl));
if (_available(cl)) {
_available(cl)->setPrev (s);
}
_available(cl) = s;
}
INLINE MALLOC_FUNCTION void * malloc (size_t sz) {
for (auto i = EmptinessClasses; i >= 0; i--) {
SuperblockType * s = _available(i);
if (s) {
auto oldCl = getFullness (s);
void * ptr = s->malloc (sz);
auto newCl = getFullness (s);
if (ptr) {
if (oldCl != newCl) {
transfer (s, oldCl, newCl);
}
assert ((size_t) ptr % SuperblockType::Alignment == 0);
return ptr;
}
}
}
return nullptr;
}
INLINE void free (void * ptr) {
Check<EmptyClass, MyChecker> check (this);
auto * s = getSuperblock (ptr);
auto oldCl = getFullness (s);
s->free (ptr);
auto newCl = getFullness (s);
if (oldCl != newCl) {
transfer (s, oldCl, newCl);
}
}
static INLINE SuperblockType * getSuperblock (void * ptr) {
return SuperblockType::getSuperblock (ptr);
}
private:
void transfer (SuperblockType * s, int oldCl, int newCl)
{
auto * prev = s->getPrev();
auto * next = s->getNext();
if (prev) { prev->setNext (next); }
if (next) { next->setPrev (prev); }
if (s == _available(oldCl)) {
assert (prev == 0);
_available(oldCl) = next;
}
s->setNext (_available(newCl));
s->setPrev (0);
if (_available(newCl)) { _available(newCl)->setPrev (s); }
_available(newCl) = s;
}
static INLINE int getFullness (SuperblockType * s) {
auto total = s->getTotalObjects();
auto free = s->getObjectsFree();
if (total == free) {
return 0;
} else {
return 1 + (int) ((EmptinessClasses * (total - free)) / total);
}
}
class MyChecker;
friend class MyChecker;
class MyChecker {
public:
#ifndef NDEBUG
static void precondition (EmptyClass * e) {
e->sanityCheckPre();
}
static void postcondition (EmptyClass * e) {
e->sanityCheck();
}
#else
static void precondition (EmptyClass *) {}
static void postcondition (EmptyClass *) {}
#endif
};
void sanityCheckPre() { sanityCheck(); }
void sanityCheck() {
for (int i = 0; i <= EmptinessClasses + 1; i++) {
SuperblockType * s = _available(i);
while (s) {
assert (getFullness(s) == i);
s = s->getNext();
}
}
}
Array<EmptinessClasses + 2, SuperblockType *> _available;
};
}
#endif