
#include <classes/Exec/Memory.h>
#include <exec/alerts.h>

#include <pragma/exec_lib.h>

#define PHeapSize     65536

PoolListC::PoolListC()
	: ListC()
{
}

PoolListC::~PoolListC()
{
	struct MemHeader *mh;
	ULONG b;
	while (mh = (struct MemHeader *) remHead()) {
		b = ((ULONG) mh->mh_Upper) - ((ULONG) mh->mh_Lower);
		if (b > 0)
			FreeMem(mh->mh_Lower,b);
		FreeMem(mh,sizeof(struct MemHeader));
	};
}

// *************************************************************

void *PublicHeapC::operator new(size_t bytes)
{
	void *mem;
	ListCursorC lc(pool);
	while (!lc.isDone()) {
		if (mem = Allocate((struct MemHeader *) lc.item(),bytes))
			return mem;
		lc.next();
	};
	struct MemHeader *mh = (struct MemHeader *) AllocMem(
		sizeof(struct MemHeader),MEMF_PUBLIC|MEMF_CLEAR);
	if (!mh)
		return NULL;
	struct MemChunk *mc;
	ULONG b = bytes > PHeapSize ? bytes : PHeapSize;
	if (!(mc = (struct MemChunk *) AllocMem(b,MEMF_PUBLIC)))
	{
		FreeMem(mh,sizeof(struct MemHeader));
		return NULL;
	};
	mh->mh_Node.ln_Type = NT_MEMORY;
	mh->mh_First = mc;
	mh->mh_Lower = (APTR) mc;
	mh->mh_Upper = (APTR) (((ULONG) mc) + b);
	mh->mh_Free = b;
	mc->mc_Next = NULL;
	mc->mc_Bytes = b;
	pool.addHead((NodeC &) *mh);
	return Allocate(mh,bytes);
}

void PublicHeapC::operator delete(void *object, size_t bytes)
{
	if (!object)
		return;
	ListCursorC lc(pool);
	struct MemHeader *mh;
	while (!lc.isDone()) {
		mh = (struct MemHeader *) lc.item();
		if (((ULONG) object >= (ULONG) mh->mh_Lower)
			&& ((ULONG) object < (ULONG) mh->mh_Upper))
		{
			Deallocate(mh,object,bytes);
			if (((ULONG) mh->mh_Upper) - ((ULONG) mh->mh_Lower) <= mh->mh_Free)
			{
				Remove((struct Node *) mh);
				FreeMem(mh->mh_Lower,mh->mh_Free);
				FreeMem(mh,sizeof(struct MemHeader));
			};
			return;
		};
		lc.next();
	};
	Alert(AN_BadFreeAddr);
}

PoolListC PublicHeapC::pool;

// ******************************************************************

MemoryC::MemoryC()
	: ShareManualC()
{
	mMemory = NULL;
	mSize = 0;
}

MemoryC::MemoryC(const MemoryC &s)
{
	mMemory = s.mMemory;
	mSize = s.mSize;
	if (mMemory)
		reference();
}

MemoryC::~MemoryC()
{
	if (mMemory)
	{
		dereference();
		if (only())
			FreeMem(mMemory,mSize);
	};
}

MemoryC &MemoryC::operator = (const MemoryC &s)
{
	if (this != &s)
	{
		if (mMemory)
		{
			if (only())
				FreeMem(mMemory,mSize);
		}
		else
			dereference();
		ShareManualC::operator= (s);
		mMemory = s.mMemory;
		mSize = s.mSize;
		if (mMemory)
			reference();
	};
	return *this;
}

APTR MemoryC::allocate(ULONG bytes, ULONG flags)
{
	if (mMemory)
		throw MemoryX(bytes,flags);
	mSize = bytes;
	mMemory = AllocMem(bytes,flags);
	if (!mMemory)
		throw MemoryX(bytes,flags);
	reference();
	return mMemory;
}

VOID MemoryC::free()
{
	if (mMemory)
	{
		dereference();
		if (only())
			FreeMem(mMemory,mSize);
		mMemory = NULL;
		mSize = 0;
	};
}

ULONG MemoryC::available(ULONG flags)
{
	return AvailMem(flags);
}

// *************************************************************

MemoryPoolC::MemoryPoolC(ULONG flags, ULONG poolsize)
	: ListC()
{
	mflags = flags;
	psize = poolsize;
}

MemoryPoolC::~MemoryPoolC()
{
	struct MemHeader *mh;
	ULONG b;
	while (mh = (struct MemHeader *) remHead()) {
		b = ((ULONG) mh->mh_Upper) - ((ULONG) mh->mh_Lower);
		if (b > 0)
			FreeMem(mh->mh_Lower,b);
		FreeMem(mh,sizeof(struct MemHeader));
	};
}

VOID MemoryPoolC::changePoolsize(ULONG poolsize)
{
	psize = poolsize;
}

APTR MemoryPoolC::allocate(ULONG bytes)
{
	void *mem;
	ListCursorC lc(*this);
	while (!lc.isDone()) {
		if (mem = Allocate((struct MemHeader *) lc.item(),bytes))
			return mem;
		lc.next();
	};
	struct MemHeader *mh = (struct MemHeader *) AllocMem(
		sizeof(struct MemHeader),(mflags & MEMF_PUBLIC) | MEMF_CLEAR);
	if (!mh)
		throw MemoryX(bytes,mflags & MEMF_PUBLIC);
	struct MemChunk *mc;
	ULONG b = bytes > psize ? bytes : psize;
	if (!(mc = (struct MemChunk *) AllocMem(b,mflags & MEMF_PUBLIC)))
	{
		FreeMem(mh,sizeof(struct MemHeader));
		throw MemoryX(bytes,mflags & MEMF_PUBLIC);
	};
	mh->mh_Node.ln_Type = NT_MEMORY;
	mh->mh_First = mc;
	mh->mh_Lower = (APTR) mc;
	mh->mh_Upper = (APTR) (((ULONG) mc) + b);
	mh->mh_Free = b;
	mc->mc_Next = NULL;
	mc->mc_Bytes = b;
	addHead((NodeC &) *mh);
	return Allocate(mh,bytes);
}

VOID MemoryPoolC::free(APTR address, ULONG bytes)
{
	if (!address)
		return;
	ListCursorC lc(*this);
	struct MemHeader *mh;
	while (!lc.isDone()) {
		mh = (struct MemHeader *) lc.item();
		if (((ULONG) address >= (ULONG) mh->mh_Lower)
			&& ((ULONG) address < (ULONG) mh->mh_Upper))
		{
			Deallocate(mh,address,bytes);
			if (((ULONG) mh->mh_Upper) - ((ULONG) mh->mh_Lower) <= mh->mh_Free)
			{
				Remove((struct Node *) mh);
				FreeMem(mh->mh_Lower,mh->mh_Free);
				FreeMem(mh,sizeof(struct MemHeader));
			};
			return;
		};
		lc.next();
	};
	Alert(AN_BadFreeAddr);
}

// *************************************************************

SmallMemoryPoolC::SmallMemoryPoolC(ULONG flags, ULONG poolsize)
	: pools(sizeof(SmallMemoryBlock),10)
{
	mflags = flags;
	psize = poolsize;
	pool = NULL;
}

SmallMemoryPoolC::~SmallMemoryPoolC()
{
	flush();
}

APTR SmallMemoryPoolC::allocate(ULONG bytes)
{
	if (!pool)
	{
		APTR mem = AllocMem(psize,mflags);
		if (!mem)
			throw MemoryX(psize,mflags);
		pool = &(struct SmallMemoryBlock &) pools.addTail();
		pool->memory = mem;
		pool->size = psize;
		pool->full = 0;
	};
	if (pool->full + bytes > pool->size)
	{
		APTR mem = AllocMem(psize,mflags);
		if (!mem)
			throw MemoryX(psize,mflags);
		pool = &(struct SmallMemoryBlock &) pools.addTail();
		pool->memory = mem;
		pool->size = psize;
		pool->full = 0;
	};
	APTR retval = (APTR) (((ULONG) pool->memory) + pool->full);
	pool->full += bytes;
	return retval;
}

VOID SmallMemoryPoolC::free(APTR address, ULONG size)
{
}

VOID SmallMemoryPoolC::flush()
{
	gen_arraycursor gac(pools);
	while (!gac.isDone()) {
		struct SmallMemoryBlock *i = &(struct SmallMemoryBlock &) gac.item();
		if (i->memory)
			FreeMem(i->memory,i->size);
		gac.next();
	};
}
