/*

    ========== licence begin  GPL
    Copyright (c) 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 "Oms/OMS_Defines.h"
#include "Oms/OMS_NewObjCache.hpp"
#include "Oms/OMS_Globals.hpp"
#include "Oms/OMS_DbpError.hpp"
#include "Oms/OMS_ObjectContainer.hpp"
#include "Oms/OMS_Session.hpp"

#define NEW_CHAIN(obj)  ((OmsObjectContainer**)           \
  (((char*) (obj)) + (((char*) chainPtr) - ((char*) p))))

#ifdef USE_SYSTEM_ALLOC_CO13
static double LVC_SystemAllocator_Space[4];

class LVC_SystemAllocator : public SAPDBMem_IRawAllocator
{
public:
	LVC_SystemAllocator() : m_alloccnt(0), m_dealloccnt(0)
	{
	}

	static LVC_SystemAllocator &Instance()
	{
		if (!m_instance) m_instance = new(&LVC_SystemAllocator_Space)
            LVC_SystemAllocator;
		return *m_instance;
	}

	virtual void* Allocate(SAPDB_ULong ByteCount)
	{
		m_alloccnt++;
		return new char[ByteCount];
	}

	virtual void* Allocate(SAPDB_ULong ByteCount, const void *hint)
	{
		m_alloccnt++;
		return new char[ByteCount];
	}

	virtual void Deallocate(void* p)
	{
		m_dealloccnt++;
		delete[] (char*) p;
	}

	virtual void GetBaseAllocatorCallStatistics(SAPDB_ULong &CountAlloc,
		SAPDB_ULong &CountDealloc) const
	{
		GetCallStatistics(CountAlloc, CountDealloc);
	}

	virtual void GetCallStatistics(SAPDB_ULong &CountAlloc,
		SAPDB_ULong &CountDealloc) const
	{
		CountAlloc = m_alloccnt;
		CountDealloc = m_dealloccnt;
	}

  virtual int GetErrorCount(void) const
  {
    return 0;
  }

	SAPDB_ULong	m_alloccnt;
	SAPDB_ULong	m_dealloccnt;

private:
	static LVC_SystemAllocator	*m_instance;
};

LVC_SystemAllocator *LVC_SystemAllocator::m_instance = NULL;

OMS_NewObjCache::OMS_NewObjCache() : m_Containers(LVC_SystemAllocator::Instance())
{
}
#endif


class OMS_NewObjList
{
public:
  OmsObjectContainer  *head;

  OMS_NewObjList() : head(NULL)
  {
  }

  ~OMS_NewObjList()
  {
  }

  void insert(OmsObjectContainer *p, OmsObjectContainer **chainPtr)
  {
    chainPtr[0] = NULL;
    chainPtr[1] = head;
    if (head) {
      NEW_CHAIN(head)[0] = p;
    }
    head = p;
  }

  void remove(OmsObjectContainer *p, OmsObjectContainer **chainPtr)
  {
    if (chainPtr[0]) {
      NEW_CHAIN(chainPtr[0])[1] = chainPtr[1];
    } else if (head == p) {
      head = chainPtr[1];
    } else {
      // error - this cannot be, head is incorrect
      // TODO: proper exception code
      OMS_Globals::Throw(DbpError(DbpError::DB_ERROR, e_unknown_error, __MY_FILE__, __LINE__));
    }
    if (chainPtr[1]) {
      NEW_CHAIN(chainPtr[1])[0] = chainPtr[0];
    }
    chainPtr[0] = chainPtr[1] = NULL;
  }
};


OMS_NewObjCache::~OMS_NewObjCache()
{
  // remove OID lists
  OMS_NewObjHash::Iterator iter = m_Containers.Begin();
  while (iter.IsValid())
  {
#ifdef USE_SYSTEM_ALLOC_CO13
	delete iter->value;
#else
    iter->value->~OMS_NewObjList();
    m_Allocator.Deallocate(iter->value);
#endif
    ++iter;
  }
}

void OMS_NewObjCache::registerObject(tsp00_Uint4 cont, OmsObjectContainer *p, OmsObjectContainer **chainPtr)
{
  //tsp00_Uint4 cont = p->GetContainerHandle();
  OMS_NewObjHash::Iterator iter = m_Containers.Find(cont);
  if (!iter.IsValid()) 
  {
    // register new container
#ifdef USE_SYSTEM_ALLOC_CO13
    OMS_NewObjList *list = new OMS_NewObjList();
#else
    OMS_NewObjList *list = new(m_Allocator.Allocate(sizeof(OMS_NewObjList))) 
      OMS_NewObjList();
#endif
    iter = m_Containers.Insert(cont, list);
    if (!iter.IsValid())
    {
      // error - shouldn't happen
#ifdef USE_SYSTEM_ALLOC_CO13
      delete list;
#else
      list->~OMS_NewObjList();
      m_Allocator.Deallocate(list);
#endif
      OMS_Globals::Throw(DbpError(DbpError::DB_ERROR, e_hash_memory_exceeded, __MY_FILE__, __LINE__));
    }
  }
  iter->value->insert(p, chainPtr);
}

void OMS_NewObjCache::removeObject(OmsObjectContainer *p, OMS_Context* context)
{
  tsp00_Uint4 cont = p->GetContainerHandle();
  OMS_NewObjHash::Iterator iter = m_Containers.Find(cont);
  if (!iter.IsValid()) return;  // no such container

  // remove from list
  OmsObjectContainer **chainPtr = (OmsObjectContainer**) (((char*) p) + 
    ((context->GetContainerInfo(cont)->GetObjectSize() + 
    sizeof(void *) - 1) & ~(sizeof(void *) - 1)));
  iter->value->remove(p, chainPtr);
}

const OmsObjectContainer *OMS_NewObjCache::nextObject(
      tsp00_Uint4               contid, 
      tsp00_Int4                objsize,
      const OmsObjectContainer  *last)
{
  if (last) {
    // iterate to next in linked list
    return ((OmsObjectContainer **) (((char*)last) +
      ((objsize + sizeof(void *) - 1) & ~(sizeof(void*) - 1))))[1];
  }

  OMS_NewObjHash::Iterator iter = m_Containers.Find(contid);
  if (!iter.IsValid()) return NULL;

  return iter->value->head;
}

void OMS_NewObjCache::SetEmpty()
{
  // remove OID lists
  OMS_NewObjHash::Iterator iter = m_Containers.Begin();
  while (iter.IsValid())
  {
#ifdef USE_SYSTEM_ALLOC_CO13
    delete iter->value;
#else
    iter->value->~OMS_NewObjList();
    m_Allocator.Deallocate(iter->value);
#endif
    ++iter;
  }
  m_Containers.Delete();
}
