//-*-C++-*-

/*  src/new-operator.ipp  */


/*
 * Author: Philogelos A. <Philogelos@yahoo.com>
 * Maintainer: Philogelos A.
 * Keywords: C++, library, containers
 *
 * Copyright (C) 1998 Philogelos A.
 *
 * This file is part of Quercus Robusta.
 *
 * Quercus Robusta is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This software 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 Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this software; see the file COPYING.LIB.  If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */



/* $Id: new-operator.ipp,v 1.3 1999/05/22 13:00:32 philogelos Exp $ */

#if !defined(__NEW_OPERATOR_IPP__)
#define __NEW_OPERATOR_IPP__

#include "new-operator.hpp"
#include "MemoryMonitor.hpp"
#include "Top.hpp"
#include "Debug.hpp"
#include "defines.h"
#include "platform/Platform.hpp"

long magicStart   = 0xa1efc0fe;
long magicEnd     = 0xc001deaf;
long magicDeleted = 0xb100ded7;

static const size_t shift = sizeof magicStart;
static const size_t doubleShift = 2 * shift;

#if defined( USE_ALLOC_CHAIN )
static  long *firstInAllocatedChain = ( long * ) NULL;
static  long *lastInAllocatedChain = ( long * ) NULL;

void dumpAllocatedByNew();
/* USE_ALLOC_CHAIN */
#endif

INLINE void *operator new( size_t aSize )
{
#if defined( USE_ALLOC_CHAIN )
  aSize += doubleShift;
#endif
  const size_t rSize = ( ( aSize - 1 ) / shift + 1 );
  char *arena;
  Platform *platform;

  platform = Platform::getInstanceNow();
  if( platform != ( Platform * ) NIL )
	{
	  arena = ( char * ) platform -> allocateMemory( ( rSize  + 3 ) * shift );
	}
  else
	{
	  arena = ( char * ) platform -> allocateMemoryBootstrap( ( rSize  + 3 ) * shift );
	}

#if defined( PROFILE_MEM_ALLOC )
  MemoryMonitor::inc( aSize, arena + doubleShift );
#endif

  if( arena == NIL )
	{
	  MemoryMonitor::release();
	  Debug::getLogger() -> log("Failed to allocate %i bytes. "
								"(Totally allocated: %lu.) Cleaning up",
								aSize, MemoryMonitor::amount() );
	}
  ( ( long * ) arena )[ 0 ] = ( long ) aSize;
  ( ( long * ) arena )[ 1 ] = magicStart;
  ( ( long * ) arena )[ rSize + 2 ] = magicEnd;
#if defined( USE_ALLOC_CHAIN )

  ( ( long * ) arena )[ 2 ] = ( long ) lastInAllocatedChain;
  ( ( long * ) arena )[ 3 ] = ( long ) NULL;
  if( ( ( long * ) arena )[ 2 ] != ( long ) NULL )
	{
	  ( ( long * )( ( ( long * ) arena )[ 2 ] ) )[ 1 ] = ( long ) &( ( long * ) arena )[ 2 ];
	}
  if( firstInAllocatedChain == ( long * ) NULL )
	{
	  firstInAllocatedChain = &( ( long * ) arena )[ 2 ];
	}
  lastInAllocatedChain = &( ( long * ) arena )[ 2 ];
  return ( void * )( arena + doubleShift + doubleShift );

  /*                                                            */
  /*                                  +-- returned              */
  /*                                  |                         */
  /*    aSize a1efc0fe   o        o   V  data      c001deaf     */
  /*  +-------+-------+--|----+---|---+----....----+-------+    */
  /*  ^                  |        |                             */
  /*  |     prev <--...--+        +--...--> next                */
  /*  +-- arena                                                 */
  /*                                                            */

#endif
  //  Platform::getInstance() -> memset( arena + doubleShift, 0, aSize );
  //  fprintf( stderr, "\nnew at %lx", ( long unsigned int ) arena + doubleShift );
  return ( void * )( arena + doubleShift );
}

INLINE boolean checkForDelete( const void *aPointer )
{
  if( aPointer == null )
	{
	  Platform::getInstance() -> rawErrorPrintf( "delete null" );
	  return false;
	}
  else
	{
	  long *arena = ( long * ) aPointer;
	  if( arena[ -1 ] != magicStart )
		{
		  if( arena[ -1 ] == magicDeleted )
			{
			  Platform::getInstance() -> rawErrorPrintf
				( "double delete at %lx", ( long unsigned int ) aPointer );
			}
		  else
			{
			  Platform::getInstance() -> 
				rawErrorPrintf( "wrong delete at %lx", ( long unsigned int ) aPointer );
			}
		  return false;
		}
	  else
		{
		  size_t len = ( size_t ) arena[ -2 ];
		  size_t rLen = ( len - 1 ) / shift + 1;
		  if( arena[ rLen ] != magicEnd )
			{
			  Platform::getInstance() -> 
				rawErrorPrintf( "corrupted heap at %lx", 
								( long unsigned int ) aPointer );
			  return false;
			}
		}
	}
  return true;
}

#if !defined(__SUNPRO_CC)
INLINE void operator delete( void *aPointer ) throw()
#else
INLINE void operator delete( void *aPointer )
#endif
{
  long *arena = ( long * ) aPointer;
#if defined( USE_ALLOC_CHAIN )
  arena -= 2;
  if( arena[ 1 ] != ( long ) NULL )
	{
	  ( ( long * )arena[ 1 ] )[ 0 ] = arena[ 0 ];
	}
  else
	{
	  lastInAllocatedChain = ( long * ) arena[ 0 ];
	}
  
  if( arena[ 0 ] != ( long ) NULL )
	{
	  ( ( long * )arena[ 0 ] )[ 1 ] = arena[ 1 ];
	}
  else
	{
	  firstInAllocatedChain = ( long * ) arena[ 1 ];
	}
  aPointer = ( void * ) arena;
#endif
  
#if defined( CHECK_DELETES )
  if( checkForDelete( aPointer ) )
	{
#endif
	  Platform *platform;

#if defined( PROFILE_MEM_ALLOC )
	  MemoryMonitor::dec( ( size_t ) arena[ -2 ], arena );
#endif
	  arena[ -1 ] = magicDeleted;
	  platform = Platform::getInstanceNow();
	  if( platform != ( Platform * ) NIL )
		{
		  platform -> deallocateMemory( arena - 2 );
		}
	  else
		{
		  platform -> deallocateMemoryBootstrap( arena - 2 );
		}

	  //	  free( arena - 2 );
	  //	  fprintf( stderr, "\ndel at %lx", ( long unsigned int ) aPointer );
#if defined( CHECK_DELETES )
	}
#endif
}

#if defined( USE_ALLOC_CHAIN )
void dumpAllocatedByNew()
{
  long *end;
  long *point;
  Index count;
  
  end = lastInAllocatedChain;
  point = firstInAllocatedChain;

  for( count = 0 ; ( point != ( long * ) NULL ) && ( point != end ) ; 
	   point = ( long * ) ( point[ 1 ] ), ++count )
	{
	  Debug::getLogger() -> log( "Allocated: %lx", point );
	}
  Debug::getLogger() -> log( "*** Totally allocated: %li, in %li chunks", 
							 MemoryMonitor::amount(), count );
}

#endif


#if defined(_INLINE)
#include "../src/Debug.ipp"
#endif


#if defined(_INLINE)
#include "../src/Top.ipp"
#endif

/* __NEW_OPERATOR_IPP__ */
#endif

/* $Log: new-operator.ipp,v $
 * Revision 1.3  1999/05/22 13:00:32  philogelos
 * Merging sources back from SPARC
 *
 * Revision 1.2  1999/03/03 19:09:31  philogelos
 * Put sources under GNU Library License
 *
 * Revision 1.1  1999/02/28 16:01:18  philogelos
 * Created from .cpp
 *
 * Revision 1.1.1.1  1998/11/25 20:11:01  philogelos
 * Quercus Robusta
 *
 * Revision 1.1  1998/07/09 09:30:38  philogelos
 * new files added to the repository
 * */