/*!
 * \file    LVCMem_GlobalUserAllocatorRegister.cpp
 * \author  IvanS
 * \brief   LiveCache global user allocator register.
 */
/*

    ========== 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 "SAPDBCommon/MemoryManagement/SAPDBMem_NewDestroy.hpp"
#include "liveCache/MemoryManagement/LVCMem_GlobalUserAllocatorRegister.hpp"
#include "RunTime/Synchronisation/RTESync_Spinlock.hpp"

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

/// Bucket count for allocator lvcGlobalUserAllocHash.
enum {
  BUCKET_CNT  = 127
};

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

/// Hash item for global user allocator list.
class LVCMem_UserAllocatorRegisterItem {
public:
  SAPDB_UTF8                        m_name[40+1]; ///< allocator name
  LVCMem_UserAllocator              *m_alloc;     ///< allocator
  LVCMem_UserAllocatorRegisterItem  *m_next;      ///< next item in lvcGlobalUserAllocHash bucket

  LVCMem_UserAllocatorRegisterItem(LVCMem_UserAllocator *alloc, 
    const SAPDB_UTF8 *name, LVCMem_UserAllocatorRegisterItem *next)
    : m_alloc(alloc), m_next(next)
  {
    strncpy(reinterpret_cast<char*>(&m_name[0]), 
      reinterpret_cast<const char*>(name), 40);
    m_name[40] = 0;
  }
};

typedef LVCMem_UserAllocatorRegisterItem *LVCMem_UserAllocatorRegisterItemPtr;

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

/// Global user allocator hash items.
static LVCMem_UserAllocatorRegisterItemPtr  lvcGlobalUserAllocItems[BUCKET_CNT] = {NULL};

/// Lock for global allocator directory.
static RTESync_Spinlock                     lvcGlobalUserAllocLock;

/// Default instance.
LVCMem_GlobalUserAllocatorRegister          LVCMem_GlobalUserAllocatorRegister::m_instance;

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

static size_t lvcGlobalUserAllocHash(const SAPDB_UTF8 *name)
{
  size_t val = 0;
  while (*name) {
    val = (val << 1) ^ (val >> 31) ^ *(name++);
  }
  return val;
}

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

LVCMem_UserAllocator *LVCMem_GlobalUserAllocatorRegister::get(
  SAPDBMem_IRawAllocator &alloc, SAPDBMem_IRawAllocator &blockalloc,
  const SAPDB_UTF8 *name, IliveCacheSink *sink)
{
  if (name == NULL) {
    name = (const SAPDB_UTF8*) "";
  }
  size_t bucket = lvcGlobalUserAllocHash(name) % BUCKET_CNT;

  RTESync_LockedScope lck(lvcGlobalUserAllocLock);

  LVCMem_UserAllocatorRegisterItem *i = lvcGlobalUserAllocItems[bucket];
  while (i) {
    if (strncmp(reinterpret_cast<const char*>(name),
      reinterpret_cast<const char*>(&i->m_name[0]), 40) == 0) {
        return i->m_alloc;
      }
      i = i->m_next;
  }

  // create new allocator
  if (*name == 0) {
    name = (const SAPDB_UTF8*) "Global User Alloc";
  }
  LVCMem_UserAllocator *a = ::new(alloc) 
    LVCMem_UserAllocator(name, blockalloc, 32 * 1024, 32 * 1024, 
    SAPDBMem_RawAllocator::FREE_RAW_EXTENDS);
  a->SetBadAllocHandler(SAPDBMem_RawAllocator::THROW_STD_BAD_ALLOC);

  lvcGlobalUserAllocItems[bucket] = ::new(alloc) 
    LVCMem_UserAllocatorRegisterItem(a, name, lvcGlobalUserAllocItems[bucket]);
  return a;
}

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