#ifndef _SIMPLE_ALLOCATOR_H
#define _SIMPLE_ALLOCATOR_H
#include <SupportDefs.h>
#include <util/SplayTree.h>
template<uint32 Alignment = 8>
class SimpleAllocator {
class Chunk {
public:
size_t CompleteSize() const
{
return fSize;
}
protected:
union {
uint32 fSize;
char fAlignment[Alignment];
};
};
class FreeChunk;
struct FreeChunkData : SplayTreeLink<FreeChunk> {
FreeChunk* Next() const
{
return fNext;
}
FreeChunk** NextLink()
{
return &fNext;
}
protected:
FreeChunk* fNext;
};
class FreeChunk : public Chunk, public FreeChunkData {
public:
void SetTo(size_t size)
{
Chunk::fSize = size;
FreeChunkData::fNext = NULL;
}
size_t Size() const
{
return (addr_t)this + Chunk::fSize - (addr_t)AllocatedAddress();
}
FreeChunk* Split(size_t splitSize)
{
splitSize = Align(splitSize);
FreeChunk* chunk = (FreeChunk*)((addr_t)AllocatedAddress() + splitSize);
size_t newSize = (addr_t)chunk - (addr_t)this;
chunk->fSize = Chunk::fSize - newSize;
chunk->fNext = NULL;
Chunk::fSize = newSize;
return chunk;
}
bool IsTouching(FreeChunk* chunk)
{
return chunk
&& (((uint8*)this + Chunk::fSize == (uint8*)chunk)
|| (uint8*)chunk + chunk->fSize == (uint8*)this);
}
FreeChunk* Join(FreeChunk* chunk)
{
if (chunk < this) {
chunk->fSize += Chunk::fSize;
chunk->fNext = FreeChunkData::fNext;
return chunk;
}
Chunk::fSize += chunk->fSize;
FreeChunkData::fNext = chunk->fNext;
return this;
}
void* AllocatedAddress() const
{
return (void*)static_cast<const FreeChunkData*>(this);
}
static FreeChunk* SetToAllocated(void* allocated)
{
return static_cast<FreeChunk*>((FreeChunkData*)allocated);
}
};
struct FreeChunkKey {
FreeChunkKey(size_t size)
:
fSize(size),
fChunk(NULL)
{
}
FreeChunkKey(const FreeChunk* chunk)
:
fSize(chunk->Size()),
fChunk(chunk)
{
}
int Compare(const FreeChunk* chunk) const
{
size_t chunkSize = chunk->Size();
if (chunkSize != fSize)
return fSize < chunkSize ? -1 : 1;
if (fChunk == chunk)
return 0;
return fChunk < chunk ? -1 : 1;
}
private:
size_t fSize;
const FreeChunk* fChunk;
};
struct FreeChunkTreeDefinition {
typedef FreeChunkKey KeyType;
typedef FreeChunk NodeType;
static FreeChunkKey GetKey(const FreeChunk* node)
{
return FreeChunkKey(node);
}
static SplayTreeLink<FreeChunk>* GetLink(FreeChunk* node)
{
return node;
}
static int Compare(const FreeChunkKey& key, const FreeChunk* node)
{
return key.Compare(node);
}
static FreeChunk** GetListLink(FreeChunk* node)
{
return node->NextLink();
}
};
typedef IteratableSplayTree<FreeChunkTreeDefinition> FreeChunkTree;
public:
static inline size_t Align(size_t size, size_t alignment = Alignment)
{
return (size + alignment - 1) & ~(alignment - 1);
}
public:
SimpleAllocator()
:
fAvailable(0)
{
#ifdef DEBUG_MAX_HEAP_USAGE
fMaxHeapSize = fMaxHeapUsage = 0;
#endif
}
~SimpleAllocator()
{
}
void AddChunk(void* base, uint32 size)
{
FreeChunk* chunk = (FreeChunk*)base;
chunk->SetTo(size);
#ifdef DEBUG_MAX_HEAP_USAGE
fMaxHeapSize += chunk->Size();
#endif
_InsertChunk(chunk);
}
uint32 Available() const { return fAvailable; }
void* Allocate(uint32 size)
{
if (size == 0)
return NULL;
if (size < sizeof(FreeChunkData))
size = sizeof(FreeChunkData);
size = Align(size);
if (size > fAvailable)
return NULL;
FreeChunk* chunk = fFreeChunkTree.FindClosest(FreeChunkKey(size), true, true);
if (chunk == NULL) {
return NULL;
}
fFreeChunkTree.Remove(chunk);
fAvailable -= chunk->Size();
void* allocated = chunk->AllocatedAddress();
if (chunk->Size() >= (size + Align(sizeof(FreeChunk)))) {
FreeChunk* freeChunk = chunk->Split(size);
fFreeChunkTree.Insert(freeChunk);
fAvailable += freeChunk->Size();
}
#ifdef DEBUG_MAX_HEAP_USAGE
fMaxHeapUsage = std::max(fMaxHeapUsage, fMaxHeapSize - fAvailable);
#endif
#ifdef DEBUG_ALLOCATIONS
memset(allocated, 0xcc, chunk->Size());
#endif
return allocated;
}
uint32 UsableSize(void* allocated)
{
FreeChunk* chunk = FreeChunk::SetToAllocated(allocated);
return chunk->Size();
}
void* Reallocate(void* oldBuffer, uint32 newSize)
{
size_t oldSize = 0;
if (oldBuffer != NULL) {
oldSize = UsableSize(oldBuffer);
if (oldSize >= newSize && (oldSize < 128 || newSize > (oldSize / 3)))
return oldBuffer;
}
void* newBuffer = Allocate(newSize);
if (newBuffer == NULL)
return NULL;
if (oldBuffer != NULL) {
memcpy(newBuffer, oldBuffer, (oldSize < newSize) ? oldSize : newSize);
Free(oldBuffer);
}
return newBuffer;
}
void Free(void* allocated)
{
if (allocated == NULL)
return;
FreeChunk* freedChunk = FreeChunk::SetToAllocated(allocated);
#ifdef DEBUG_VALIDATE_HEAP_ON_FREE
if (freedChunk->Size() > (fMaxHeapSize - fAvailable)) {
panic("freed chunk %p clobbered (%#zx)!\n", freedChunk,
freedChunk->Size());
}
{
FreeChunk* chunk = fFreeChunkTree.FindMin();
while (chunk) {
if (chunk->Size() > fAvailable || freedChunk == chunk)
panic("invalid chunk in free list (%p (%zu)), or double free\n",
chunk, chunk->Size());
chunk = chunk->Next();
}
}
#endif
#ifdef DEBUG_ALLOCATIONS
for (uint32 i = 0; i < (freedChunk->Size() / 4); i++)
((uint32*)allocated)[i] = 0xdeadbeef;
#endif
_InsertChunk(freedChunk);
}
#ifdef DEBUG_MAX_HEAP_USAGE
uint32 MaxHeapSize() const { return fMaxHeapSize; }
uint32 MaxHeapUsage() const { return fMaxHeapUsage; }
#endif
void DumpChunks()
{
FreeChunk* chunk = fFreeChunkTree.FindMin();
while (chunk != NULL) {
printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk,
chunk->Size(), (uint8*)chunk + chunk->CompleteSize(),
chunk->Next());
chunk = chunk->Next();
}
}
private:
void _InsertChunk(FreeChunk* freedChunk)
{
FreeChunk* chunk = fFreeChunkTree.FindMin();
int32 joinCount = 0;
while (chunk != NULL) {
FreeChunk* nextChunk = chunk->Next();
if (chunk->IsTouching(freedChunk)) {
fFreeChunkTree.Remove(chunk);
fAvailable -= chunk->Size();
freedChunk = chunk->Join(freedChunk);
if (++joinCount == 2)
break;
}
chunk = nextChunk;
}
fFreeChunkTree.Insert(freedChunk);
fAvailable += freedChunk->Size();
#ifdef DEBUG_MAX_HEAP_USAGE
fMaxHeapUsage = std::max(fMaxHeapUsage, fMaxHeapSize - fAvailable);
#endif
}
private:
FreeChunkTree fFreeChunkTree;
uint32 fAvailable;
#ifdef DEBUG_MAX_HEAP_USAGE
uint32 fMaxHeapSize, fMaxHeapUsage;
#endif
};
#endif