/*!***************************************************************************

  module      : LVCSim_OIDAllocator.cpp

  -------------------------------------------------------------------------

  responsible : IvanS

  special area: liveCache Simulator
  description : allocator for memory and OIDs

  -------------------------------------------------------------------------





    ========== licence begin  GPL
    Copyright (c) 2000-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end




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


#include "LVCSimulator/LVCSim_Internal.hpp"
#include "LVCSimulator/LVCSim_OIDAllocator.hpp"
#include "LVCSimulator/LVCSim_ObjectHandle.hpp"


#define	ALIGN				sizeof(int)
#define	OBJ_SIZE_BITS		(MAX_OBJ_SIZE_BITS - MIN_OBJ_SIZE_BITS)

#define	BYTES_PER_RANGE		(64*1024)	// allocate 64kB blocks


void LVCSim_OIDObject::getOid(OmsTypeOid *lpoid) const
{
	lpoid->pno = pagenr;
	lpoid->pagePos = pagepos;
	lpoid->generation = generation;
}

void LVCSim_OIDObject::getRef(tgg91_PageRef *lpref) const
{
	lpref->gg91BuildRef(pagenr, pagepos);
}


LVCSim_OIDAllocator::LVCSim_OIDAllocator(unsigned int _idx, size_t _size)
	: idx(_idx), size(((_size + ALIGN - 1) / ALIGN) * ALIGN), freeptr(-1),
	  endptr(0), mapcnt(0)
{
	objpage = BYTES_PER_RANGE / size;
}

LVCSim_OIDAllocator::~LVCSim_OIDAllocator()
{
	// delete all page maps
	// TODO: use opposite to allocate map
    for (size_t i = 0; i < mapcnt; ++i) {
        delete[] maps[i];
    }
}



void *LVCSim_OIDAllocator::allocate()
{
	RTESync_LockedScope l(lock);

	if (freeptr >= 0) {
		// reuse address
		LVCSim_OIDObject *addr = (LVCSim_OIDObject*)
			(maps[freeptr / objpage] + (freeptr % objpage) * size);
		int newfreeptr = *((int*) addr);
		addr->setOid((unsigned int) idx, freeptr + 1, addr->getGeneration());
		memset(addr + 1, 0, size - sizeof(LVCSim_OIDObject));
		freeptr = newfreeptr;
		return addr;
	}

	// must create new object
	if (endptr == mapcnt * objpage) {
		// no space in last block, get new block
		if (mapcnt == LVCSIM_ALLOC_MAX_MAP) {
			// TODO: throw exception?
			return NULL;
		}
		// TODO: do not use new, do mmap()
		maps[mapcnt++] = new char[BYTES_PER_RANGE];
	}

	LVCSim_OIDObject *addr = (LVCSim_OIDObject*)
		(maps[mapcnt-1] + (endptr++ % objpage) * size);
	addr->setOid((unsigned int) idx, (unsigned int) endptr, 1);
	memset(addr + 1, 0, size - sizeof(LVCSim_OIDObject));
	return addr;
}

void LVCSim_OIDAllocator::deallocate(unsigned int oid)
{
	--oid;
	size_t page = oid / objpage;
    LVCSIM_ASSERT((page < mapcnt));

	RTESync_LockedScope l(lock);

	LVCSim_OIDObject *addr = (LVCSim_OIDObject*)
		(maps[page] + (oid % objpage) * size);
	LVCSIM_ASSERT(addr->isValid());
	addr->nextGeneration();
	*((int*) addr) = freeptr;
	freeptr = oid;
}

void *LVCSim_OIDAllocator::map(unsigned int oid) const
{
	--oid;
	size_t page = oid / objpage;
	if (page >= mapcnt) return NULL;
	return (maps[page] + (oid % objpage) * size);
}

unsigned int LVCSim_OIDAllocator::getNextOid(unsigned int oid) const
{
	// since external OIDs are internal + 1, it's already incremented
	size_t page = oid / objpage;
	if (page >= mapcnt) return 0;

	LVCSim_OIDObject *addr = (LVCSim_OIDObject*)
		(maps[page] + (oid % objpage) * size);

	while (oid < endptr) {
		++oid;
		if (addr->isValid()) {
			// OK, this is not deleted, return it
			return oid;
		}
		if (oid % objpage) {
			// same page - seek to next object
			addr = (LVCSim_OIDObject*) (((char*)addr) + size);
		} else {
			// new page
			addr = (LVCSim_OIDObject*) maps[++page];
		}
	}
	return 0;	// no more objects
}


LVCSim_OIDAllocator::LVCSim_OIDAllocator(LVCSim_CheckpointReader &inp, size_t objsize)
{
	inp.read(this, ((char*) &maps) - ((char*) this));

	for (unsigned int i = 0; i < mapcnt; ++i)
	{
		maps[i] = new char[BYTES_PER_RANGE];
	}

	// read objects
	unsigned int oid;
	inp.read(&oid, sizeof(oid));
	while (oid) {
		// read object
		new(this, oid) LVCSim_ObjectHandle(inp, objsize);
		inp.read(&oid, sizeof(oid));
	}

	// read & reconnect freelist
	inp.read(&freeptr, sizeof(freeptr));
	int fptr = freeptr;
	while (fptr != -1)
	{
		int ofptr = fptr;
		inp.read(&fptr, sizeof(fptr));
		LVCSim_ObjectHandle *h = reinterpret_cast<LVCSim_ObjectHandle*>
			(maps[ofptr / objpage] + (ofptr % objpage) * size);
		int *addr = reinterpret_cast<int*>(h);
		h->deactivateObject();
		*addr = fptr;
	}
}

void LVCSim_OIDAllocator::writeToStream(LVCSim_CheckpointWriter &o, size_t objsize) const
{
	o.write(this, ((char*) &maps) - ((char*) this));

	// dump objects as such
	unsigned int oid = 0;
	while (oid = getNextOid(oid)) {
		o.write(&oid, sizeof(oid));
		// dump an object
		const LVCSim_ObjectHandle *obj = (const LVCSim_ObjectHandle *) map(oid);
		obj->writeToStream(o, objsize);
	}
	o.write(&oid, sizeof(oid));

	// dump freelist
	int fptr = freeptr;
	while (fptr != -1) 
	{
		o.write(&fptr, sizeof(fptr));
		int *addr = (int*) (maps[fptr / objpage] + (fptr % objpage) * size);
		fptr = *addr;
	}
	o.write(&fptr, sizeof(fptr));
}

