/*!
 * \file    LVCMem_UserAllocator.cpp
 * \author  IvanS
 * \brief   LiveCache user allocator (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_UserAllocator.hpp"
#include "SAPDBCommon/SAPDB_UTF8Basis.hpp"
#include "hsp77.h"
#include "hak34.h"
#include "hgg01_3.h"
#include "hgg01_1.h"
#include "gsp03_1.h"

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

static char *decodeType(OmsCallbackInterface::BlockType tp)
{
  switch (tp) {
  case OmsCallbackInterface::BlockCOMRoutine:
    return "COM";
  case OmsCallbackInterface::BlockSession:
    return "session";
  case OmsCallbackInterface::BlockGlobal:
    return "global";
  case OmsCallbackInterface::BlockTransaction:
    return "TX";
  case OmsCallbackInterface::BlockInvalid:
    return "invalid";
  case OmsCallbackInterface::BlockFreed:
    return "free";
  default:
    return "???";
  }
}

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

static void dumpMessage(bool        isOpError, 
                        SAPDB_UTF8* pMsg,
                        int         msgLen)
{
  tsp00_C40 c40;
  unsigned char* pC40    = &c40[0];
  int            restLen = sizeof(c40) ;
  memset (c40, ' ', sizeof(c40));
  while (true)
  {
    int characterSize = (int) SAPDB_UTF8Basis::CharacterSize(pMsg);

    if ((0 == characterSize) || (restLen < characterSize) || (msgLen <= 0))
    {
      g01optextmsg (sp3p_console, (isOpError) ? sp3m_error : sp3m_info, csp3_ak_msg, csp3_n_obj, c40);
      if (msgLen <= 0)
      {
        return;
      }
      memset (c40, ' ', sizeof(c40));
      pC40    = &c40[0];
      restLen = sizeof(c40);
      if (0 == characterSize)
      {
        // we are out of step
        sp77sprintf ( c40, sizeof(c40), "invalid UTF8 string, truncated");
        g01optextmsg (sp3p_console, sp3m_error, csp3_ak_msg, csp3_n_obj, c40);
        return;
      }
    }
    for (int ix = 0; ix < characterSize; ++ix)
    {
      *pC40 = *pMsg;
      ++pC40;
      ++pMsg;
    }
    restLen -= characterSize;
    msgLen  -= characterSize;
  }
}

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

LVCMem_UserAllocator::LVCMem_UserAllocator(
  const SAPDB_UTF8       *   Identifier,
  SAPDBMem_IRawAllocator &   RawAllocator,
  SAPDB_ULong                FirstBlockSize,
  SAPDB_ULong                SupplementBlockSize,
  FreeRawExtendsEnum         FreeRawExtends,
  SAPDB_ULong                MaxSize)
  : SAPDBMem_RawAllocator(Identifier, RawAllocator, FirstBlockSize, 
    SupplementBlockSize, FreeRawExtends, MaxSize)
{
}

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

void *LVCMem_UserAllocator::omsAllocate(size_t size) // allocation
{
  void *p = Allocate(size);
  SetChunkProperties(p, OmsCallbackInterface::BlockGlobal);
  return p;
}

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

void LVCMem_UserAllocator::omsDeallocate(void *p)    // deallocation
{
  if (GetAllocator(p) != this) {
    SAPDB_ULong size, callstack;
    bool inUse;
    GetChunkInfo(p, inUse, size, callstack);
    if (inUse) {
      SetChunkProperties(p, 0);
    }
    // TODO: use memory debugger's allocator callback instead to report the
    // block to the user (when memory debugger integrated)
    memInvalidFree(p, callstack, GetChunkSize(p), OmsCallbackInterface::BlockGlobal, 
      inUse ? OmsCallbackInterface::BlockInvalid : OmsCallbackInterface::BlockFreed);
    return;
  }
  int code = GetChunkProperties(p);
  if (code == 1) {
    Deallocate(p);
  } else {
    SAPDB_ULong size, callstack;
    bool inUse;
    GetChunkInfo(p, inUse, size, callstack);
    if (inUse) {
      SetChunkProperties(p, 0);
    }
    // TODO: use memory debugger's allocator callback instead to report the
    // block to the user (when memory debugger integrated)
    memInvalidFree(p, callstack, size,
      OmsCallbackInterface::BlockGlobal, (OmsCallbackInterface::BlockType) code);
  }
}

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

bool LVCMem_UserAllocator::omsCheckAllocatorEmpty()  // check for emptyness
{
  // make emptyness check? doesn't make sense...
  return true;
}

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

int LVCMem_UserAllocator::omsGetAllocatorType() const
{
  return 1;
}

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

void LVCMem_UserAllocator::memLeftOver(const void *blk, SAPDB_ULong stack, size_t size,
                                              OmsCallbackInterface::BlockType blockType)
{
  char buf[256];
  int bufsize = sp77sprintf(buf, sizeof(buf), "LEAK: %p, size %d, type %s",
    blk, size, decodeType(blockType));
  dumpMessage(false, reinterpret_cast<SAPDB_UTF8*>(buf), bufsize);
  // TODO: dump stack traces
  if (callback) {
    callback->omsMemLeftOver(blk, size, blockType);
  }
}

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

void LVCMem_UserAllocator::memInvalidFree(const void *blk, SAPDB_ULong stack, size_t size,
                                                 OmsCallbackInterface::BlockType blockType, OmsCallbackInterface::BlockType freeType)
{
  char buf[256];
  int bufsize = sp77sprintf(buf, sizeof(buf), "INVALID FREE: %p, size %d, type %s, alloc %s",
    blk, size, decodeType(freeType), decodeType(blockType));
  dumpMessage(false, reinterpret_cast<SAPDB_UTF8*>(buf), bufsize);
  // TODO: dump stack traces
  if (callback) {
    callback->omsMemInvalidFree(blk, size, blockType, freeType);
  }
}

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