/*!
 * \file    LVCMem_CheckedUserAllocator.cpp
 * \author  IvanS
 * \brief   LiveCache user allocator checked wrapper (for session-specific memory).
 */
/*

    ========== licence begin  GPL
    Copyright (c) 2002-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 "liveCache/MemoryManagement/LVCMem_CheckedUserAllocator.hpp"
#include "SAPDBCommon/MemoryManagement/SAPDBMem_RawAllocator.hpp"
#include "Oms/OMS_CallbackInterface.hpp"

/*---------------------------------------------------------------------------*/

LVCMem_CheckedUserAllocator::LVCMem_CheckedUserAllocator(const SAPDB_UTF8* pName, 
  SAPDBMem_IRawAllocator *parent, int code) :
  SAPDBMem_RawAllocator(pName, *parent, 32768, 32768, SAPDBMem_RawAllocator::FREE_RAW_EXTENDS),
  m_alloc(reinterpret_cast<LVCMem_UserAllocator*>(parent)), m_code(code), m_allocSize(0)
{
}

/*---------------------------------------------------------------------------*/

LVCMem_CheckedUserAllocator::~LVCMem_CheckedUserAllocator()
{
  omsCheckAllocatorEmpty();
}

/*---------------------------------------------------------------------------*/

void *LVCMem_CheckedUserAllocator::omsAllocate(size_t size) // allocation
{
  void *ptr = m_alloc->Allocate(size);
  unsigned long chunkSize = m_alloc->GetChunkSize(ptr);
  m_allocSize += chunkSize;
  IncrementUsed(chunkSize);
  IncrementAllocated(chunkSize);
  IncrementAllocCnt();
  IncrementRawAllocCnt();
  m_alloc->SetChunkProperties(ptr, m_code);
  return ptr;
}

/*---------------------------------------------------------------------------*/

void LVCMem_CheckedUserAllocator::omsDeallocate(void *p)    // deallocation
{
  // decrement alloc size and check code
  int code = m_alloc->GetChunkProperties(p);
  if (m_alloc->GetAllocator(p) != m_alloc) {
    SAPDB_ULong size, callstack;
    bool inUse;
    m_alloc->GetChunkInfo(p, inUse, size, callstack);
    if (inUse) {
      m_alloc->SetChunkProperties(p, 0);
    }
    // TODO: use memory debugger's allocator callback instead to report the
    // block to the user (when memory debugger integrated)
    m_alloc->memInvalidFree(p, callstack, m_alloc->GetChunkSize(p), 
      (OmsCallbackInterface::BlockType) m_code, inUse ?
      (OmsCallbackInterface::BlockType) code : OmsCallbackInterface::BlockFreed);
    return;
  }
  if (code == m_code) {
    unsigned long chunkSize = m_alloc->GetChunkSize(p);
    m_allocSize -= chunkSize;
    DecrementUsed(chunkSize);
    DecrementAllocated(chunkSize);
    IncrementDeallocCnt();
    IncrementRawDeallocCnt();
    m_alloc->Deallocate(p);
  } else {
    SAPDB_ULong size, callstack;
    bool inUse;
    m_alloc->GetChunkInfo(p, inUse, size, callstack);
    if (inUse) {
      m_alloc->SetChunkProperties(p, 0);
    }
    // TODO: use memory debugger's allocator callback instead to report the
    // block to the user (when memory debugger integrated)
    m_alloc->memInvalidFree(p, callstack, size,
      (OmsCallbackInterface::BlockType) m_code,
      inUse ? ((OmsCallbackInterface::BlockType) code) :
      OmsCallbackInterface::BlockFreed);
  }
}

/*---------------------------------------------------------------------------*/

void* LVCMem_CheckedUserAllocator::Allocate(SAPDB_ULong ByteCount)
{
  return omsAllocate(ByteCount);
}

/*---------------------------------------------------------------------------*/

void* LVCMem_CheckedUserAllocator::Allocate(SAPDB_ULong ByteCount, const void*)
{
  return omsAllocate(ByteCount);
}

/*---------------------------------------------------------------------------*/

void LVCMem_CheckedUserAllocator::Deallocate(void* p)
{
  omsDeallocate(p);
}

/*---------------------------------------------------------------------------*/

bool LVCMem_CheckedUserAllocator::omsCheckAllocatorEmpty()  // check for emptyness
{
  if (m_allocSize != 0) {
    bool empty = true;
    SAPDBMem_RawAllocator::HeapIterator iter = m_alloc->Begin();
    while (iter) {
      const void *p = iter();
      bool inUse;
      SAPDB_ULong chunkSize, stackId;
      m_alloc->GetChunkInfo(p, inUse, chunkSize, stackId);
      if (inUse) {
        int typ = m_alloc->GetChunkProperties(p);
        if (typ >= m_code) {
          m_alloc->SetChunkProperties(p, 0);
          // TODO: use memory debugger's allocator callback instead to report the
          // block to the user (when memory debugger integrated)
          m_alloc->memLeftOver(p, stackId, chunkSize,
            (OmsCallbackInterface::BlockType) typ);
          empty = false;
        }
      }
      ++iter;
    }
    m_allocSize = 0;
    return empty;
  }
  return true;
}

/*---------------------------------------------------------------------------*/

void LVCMem_CheckedUserAllocator::omsRegisterCallback(OmsCallbackInterface *cb)
{
  reinterpret_cast<LVCMem_UserAllocator*>(m_alloc)->omsRegisterCallback(cb);
}

/*---------------------------------------------------------------------------*/

OmsCallbackInterface *LVCMem_CheckedUserAllocator::omsGetCallback() const
{
  return reinterpret_cast<LVCMem_UserAllocator*>(m_alloc)->omsGetCallback();
}

/*---------------------------------------------------------------------------*/

int LVCMem_CheckedUserAllocator::omsGetAllocatorType() const
{
  return m_code;
}

/*---------------------------------------------------------------------------*/
