/**********************************************************************
 * Copyright (c) 2005, 2007 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: ossmemory.cpp,v 1.5 2007/03/19 03:14:37 rsmith Exp $
 * 
 * Contributors: 
 * IBM - Initial API and implementation
 **********************************************************************/

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

   Source File Name = ossmemory.C

   Descriptive Name = OSSe memory sub-allocation

   Function:
      Defines:

   Dependencies:
      None

   Restrictions:
      None

   Change Activity:
   Defect Date        Who Description
   ====== =========== === ==============================================
   xxxxxx mmm/dd/yyyy aha Initial Code Drop
   161881 Nov/30/2000 pj  Various changes.
   164413 Jan/17/2001 jpk Various changes.
   186134 Jan/25/2002 dns replaced OSSExs with OSSLatch

        Last Changed =    02/08/27  12:38:38

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

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#include "osslatch.h"      /* 186134 */
#include "ossmemory.h"

/* Memory Anchor (offset based) */
struct OSSMemCB
{
   OSSLatch exs ;         /* Exclusive section - 186134 */
   size_t curSize ;     /* Current used memory size */
   size_t maxSize ;     /* Maximum allocated size */
   size_t freeNode ;    /* Free memory node linked list */
} ;

/* Free Memory Node Entry (offset based) */
struct OSSMemNode
{
   size_t next ;        /* Offset of next free node */
   size_t prev ;        /* Offset of previous free node */
   size_t size ;        /* Node size in bytes */
} ;


/* Resolve memory node address given its offset */
#define memNode( pMem, offset ) \
    ((OSSMemNode *)((0 == (offset)) ? 0 : (((char *)pMem) + (offset))))


/* Get memory node size */
#define memSize( pMemNode ) \
    ((NULL == (pMemNode)) ? 0 : (pMemNode)->size)


/* Upsize based on memory node size limitation */
#define memUpsize( size ) \
    ((((size) + sizeof( OSSMemNode ) - 1) / sizeof( OSSMemNode )) * sizeof( OSSMemNode ))


/* Downsize based on memory node size limitation */
#define memDownsize( size ) \
    (((size) / sizeof( OSSMemNode )) * sizeof( OSSMemNode ))


/* Get memory object offset given its address in this process */
#define memOffset( pMem, pMemNode ) \
    ((size_t)((NULL == (pMemNode)) ? 0 : ((char *)(pMemNode)) - ((char *)(pMem))))


/******************************************************************************
   Insert a new free memory node into Size ordered linked list
 *****************************************************************************/
static void memAddNode(
      OSSMemCB   * pMemCB,                   /* I: Free memory CB */
      OSSMemNode * pNode )                   /* I: Memory node to insert */
{
   OSSMemNode * pParNode = NULL ;
   OSSMemNode * pCurNode = memNode( pMemCB, pMemCB->freeNode ) ;
   size_t       offset   = memOffset( pMemCB, pNode ) ;

   /* Insert node in free nodes chain (sorted by node size) */
   while ( memSize( pCurNode ) > pNode->size )
   {
      pParNode = pCurNode ;
      pCurNode = memNode( pMemCB, pParNode->next ) ;
   }

   if ( NULL == pParNode )
      pMemCB->freeNode = offset ;
   else
      pParNode->next = offset ;

   if ( pCurNode )
      pCurNode->prev = offset ;

   pNode->prev = memOffset( pMemCB, pParNode ) ;
   pNode->next = memOffset( pMemCB, pCurNode ) ;

   return ;
}


/******************************************************************************
   Delete a free memory node from size ordered linked list
 *****************************************************************************/
static void memRemoveNode(
      OSSMemCB   * pMemCB,                   /* I: Free memory CB */
      OSSMemNode * pNode )                   /* I: Memory node to delete */
{
   OSSMemNode * pMemNode = NULL ;

   pMemNode = memNode( pMemCB, pNode->prev ) ;
   if ( NULL != pMemNode )
      pMemNode->next = pNode->next ;
   else
      pMemCB->freeNode = pNode->next ;

   pMemNode = memNode( pMemCB, pNode->next ) ;
   if ( pMemNode != NULL )
      pMemNode->prev = pNode->prev ;

   return ;
}


/******************************************************************************
   Initialize OSS Memory for Sub-Allocation
 *****************************************************************************/
void OSS_API ossMemInitialize(
      void   * pMemArea,                     /* I: Free memory anchor */
      size_t   memSize )                     /* I: Free memory size */
{
   OSSMemCB   * pMemCB   = (OSSMemCB *)pMemArea ;
   OSSMemNode * pMemNode = NULL ;

   memSize -= sizeof( OSSMemCB ) ;
   memSize  = memDownsize( memSize ) ;

   pMemCB->curSize  = 0 ;
   pMemCB->maxSize  = memSize ;
   pMemCB->freeNode = sizeof( OSSMemCB ) ;
   ossLatchInit( &(pMemCB->exs) ) ;      /* 186134 */

   pMemNode = memNode( pMemCB, pMemCB->freeNode ) ;
   pMemNode->next = 0 ;
   pMemNode->prev = 0 ;
   pMemNode->size = memSize ;

   return ;
}


/******************************************************************************
   Sub-Allocate a Block of Memory
 *****************************************************************************/
void * OSS_API ossMemAlloc(
      void   * pMemArea,                     /* I: Free memory anchor */
      size_t   size )                        /* I: Free memory size */
{
   void       * pBlk     = NULL ;
   OSSMemCB   * pMemCB   = (OSSMemCB *)pMemArea ;
   OSSMemNode * pMemNode = NULL ;
   OSSMemNode * pBlkNode = NULL ;

   /* Private memory allocation */
   if ( NULL == pMemArea )
   {
      pBlk = (void *)malloc( size ) ;
      goto exit ;
   }

   /* Memory sub-allocation from pre-allocated memory area */
   size = memUpsize( size ) ;

   ossLatchGet( &(pMemCB->exs) ) ;      /* 186134 */

   pMemNode = memNode( pMemCB, pMemCB->freeNode ) ;
   while ( memSize( pMemNode ) >= size )
   {
      pBlkNode = pMemNode ;
      pMemNode = memNode( pMemCB, pMemNode->next ) ;
   }

   if ( NULL != pBlkNode )
   {
      memRemoveNode( pMemCB, pBlkNode ) ;
      pBlk = (void *)pBlkNode ;

      size_t  memSize ;
      memSize = pBlkNode->size - size ;
      if ( memSize > 0 )
      {
         pMemNode = memNode( pBlkNode, size ) ;
         pMemNode->size = memSize;
         pMemNode->next = 0 ;
         pMemNode->prev = 0 ;
         memAddNode( pMemCB, pMemNode ) ;
      }
   }

   ossLatchRelease( &(pMemCB->exs) ) ;      /* 186134 */

exit :
   return pBlk ;
}


/******************************************************************************
   Free a Sub-Allocated Block of Memory
 *****************************************************************************/
void OSS_API ossMemFree(
      void   * pMemArea,                     /* I: Free memory anchor */
      void   * pBlk,                         /* I: Block to be freed */
      size_t   size )                        /* I: Block size */
{
   OSSMemCB   * pMemCB   = (OSSMemCB *)pMemArea ;
   OSSMemNode * pMemNode = NULL ;
   OSSMemNode * pCurNode = NULL ;
   OSSMemNode * pBlkNode = NULL ;

   /* Private memory release */
   if ( NULL == pMemArea )
   {
      free( pBlk ) ;
      goto exit ;
   }

   /* Memory sub-alloc release to pre-allocated memory area */
   pBlkNode = (OSSMemNode *)pBlk ;
   pBlkNode->size = memUpsize( size ) ;
   pBlkNode->next = 0 ;
   pBlkNode->prev = 0 ;

   ossLatchGet( &(pMemCB->exs) ) ;      /* 186134 */

   pMemNode = memNode( pMemCB, pMemCB->freeNode ) ;

   /* Combine with existing nodes where possible */
   while ( NULL != (pCurNode = pMemNode) )
   {
      pMemNode = memNode( pMemCB, pCurNode->next ) ;

      if ( (((char*)pBlkNode) + pBlkNode->size) == ((char*)pCurNode) )
      {
         pBlkNode->size += pCurNode->size ;
         memRemoveNode( pMemCB, pCurNode ) ;
      }
      else if ( (((char*)pCurNode) + pCurNode->size) == ((char*)pBlkNode) )
      {
         pCurNode->size += pBlkNode->size ;
         memRemoveNode( pMemCB, pCurNode ) ;
         pBlkNode = pCurNode ;
      }
   }

   /* Insert new combined node in nodes chain */
   memAddNode( pMemCB, pBlkNode ) ;

  ossLatchRelease( &(pMemCB->exs) ) ;      /* 186134 */

exit :
  return ;
}

#if defined SQLO_INTEL_IA64
void __xchg_called_with_bad_pointer(void)
{
}
#endif
