/*
 $Id: varalloc.cc,v 1.2 1996/10/11 09:10:26 roitzsch Exp $
 (C)opyright 1996 by Konrad-Zuse-Center, Berlin
 All rights reserved.
 Part of the Kaskade distribution
*/

#include <iostream.h>
#include <stdlib.h>

#include "varalloc.h"
#include "utils.h"

//-------------------------------------------------------------------------

int VarSizeAllocator:: elemBaseSize = sizeof(int*);		// for address alignment

//-------------------------------------------------------------------------


VarSizeAllocator:: VarSizeAllocator(int blockSize0)  : blockSize(blockSize0)
{ 
    if (blockSize <= 0)
    {
	cout << "\n*** class VarSizeAllocator: blockSize must be > 0"
	     << " (read: " << blockSize << ")\n";
	cout.flush(); abort();
    }

    while (blockSize % elemBaseSize != 0) ++blockSize; 

    lastBlock = 0;
    noOfBlocks = 0;
    noOfBlocksSinceIncrement = 0;

    freeSpace  = 0;
    topOfBlock = 0;

    memSpace = 0;
}
//-------------------------------------------------------------------------

VarSizeAllocator:: ~VarSizeAllocator()
{ 
    MemoryBlock* toDel;

    while(lastBlock)
    {
	toDel = lastBlock;
	lastBlock = lastBlock->next;
	::delete toDel->space;
	::delete toDel;
    }
}
//-------------------------------------------------------------------------

void VarSizeAllocator:: Info() const
{ 
    cout << "\n\tVarSizeAllocator: " << noOfBlocks << " blocks (" 
	    	<< Form("%1.6f MB)\n", float(MemSpace())/1e6);
}
//-------------------------------------------------------------------------

void* VarSizeAllocator:: Get(int size)
{
    char* returnSpace;

    if (size > 1)  while (size % elemBaseSize != 0) ++size;

    while (size > blockSize) blockSize *= 2;


    if (freeSpace+size > topOfBlock)
    {
	if (noOfBlocksSinceIncrement > 100) 	// dynamic size extension
	{
	    blockSize *= 2;
	    noOfBlocksSinceIncrement = 0;
	}

	MemoryBlock* newBlock = new MemoryBlock;
	if (newBlock == 0) allocationError();

	newBlock->space = new char[blockSize];  
	if (newBlock->space == 0) allocationError();

	newBlock->next = lastBlock;
	lastBlock = newBlock;

	++noOfBlocks;
	++noOfBlocksSinceIncrement;
	memSpace += blockSize + sizeof(MemoryBlock);

	freeSpace = newBlock->space;
	topOfBlock = freeSpace + blockSize;
    }

    returnSpace = freeSpace;
    freeSpace += size;

    return returnSpace;
}
//-------------------------------------------------------------------------

void VarSizeAllocator:: allocationError()
{
    cout << "\n*** class VarSizeAllocator: could not allocate MemoryBlock\n";
    cout.flush(); abort();
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------


FixedSizeAllocator:: FixedSizeAllocator(int elementSize0, 
					int maxElementsInBlock0)

	: elementSize(elementSize0), returnedElements(1,10)
{ 
    if (maxElementsInBlock0 < 1)
    {
	cout << "\n*** Allocator: maxElementsInBlock must be > 1\n"
	     << "\t (read: " << maxElementsInBlock << ")\n";
	cout.flush(); abort();
    }

    maxElementsInBlock  = maxElementsInBlock0; 
    noOfElementsInBlock = maxElementsInBlock0;
    lastBlock = 0;

    noOfBlocks  = 0;
    freeElement = 0;
    noOfBlocksSinceIncrement = 0;
    memSpace = 0;
}
//-------------------------------------------------------------------------

int FixedSizeAllocator:: MemSpace()  const
{ 
    return memSpace + returnedElements.memSpace();
}
//-------------------------------------------------------------------------

 void FixedSizeAllocator:: Info() const
{ 
    cout << "\n\tFixedSizeAllocator: " << noOfBlocks << " blocks (" 
	    	<< Form("%1.6f MB)", float(MemSpace())/1e6)
		<< "  on stack: " << returnedElements.high() << "\n";
}
//-------------------------------------------------------------------------


void* FixedSizeAllocator:: getFromBlock()
{
    ++noOfElementsInBlock;
    freeElement += elementSize;

    if (noOfElementsInBlock > maxElementsInBlock) 
    {
	if (noOfBlocksSinceIncrement > 100) 	// dynamic size extension
	{
	    maxElementsInBlock *= 2;
	    noOfBlocksSinceIncrement = 0;
	}

	MemoryBlock* newBlock = ::new MemoryBlock;
	if (newBlock == 0) allocationError();

	newBlock->space = ::new char[elementSize*maxElementsInBlock];  
	if (newBlock->space == 0) allocationError();

	newBlock->next = lastBlock;
	lastBlock = newBlock;

	++noOfBlocks;
	++noOfBlocksSinceIncrement;
	memSpace += maxElementsInBlock*elementSize + sizeof(MemoryBlock);

	noOfElementsInBlock = 1;
	freeElement = newBlock->space;

    }
    return freeElement;
}
//-------------------------------------------------------------------------

void FixedSizeAllocator:: allocationError()
{
    cout << "\n*** Allocator: could not allocate MemoryBlock\n";
    cout.flush(); abort();
}
//-------------------------------------------------------------------------


FixedSizeAllocator:: ~FixedSizeAllocator()
{ 
    MemoryBlock* toDel;

    while(lastBlock)
    {
	toDel = lastBlock;
	lastBlock = lastBlock->next;
	::delete [] toDel->space;
	::delete toDel;
    }

    int i;
    FORALL(returnedElements,i) returnedElements[i] = 0;
}
//-------------------------------------------------------------------------


void* FixedSizeAllocator:: Get()
{
    void* elem;

    if (returnedElements.h == 0) elem = getFromBlock();
    else 			 elem = returnedElements.pop(); 
    return elem;
}
