/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *        DYNAMIC MEMORY MANAGEMENT MODULE                * */
/* *                                                        * */
/* *  $Module:   MEMORY                                     * */ 
/* *                                                        * */
/* *  Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001      * */
/* *  MPI fuer Informatik                                   * */
/* *                                                        * */
/* *  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         * */
/* *                                                        * */
/* *                                                        * */
/* $Revision$                                        * */
/* $State$                                            * */
/* $Date$                             * */
/* $Author$                                       * */
/* *                                                        * */
/* *             Contact:                                   * */
/* *             Christoph Weidenbach                       * */
/* *             MPI fuer Informatik                        * */
/* *             Stuhlsatzenhausweg 85                      * */
/* *             66123 Saarbruecken                         * */
/* *             Email: weidenb@mpi-sb.mpg.de               * */
/* *             Germany                                    * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


/* $RCSfile$ */


#ifndef _MEMORY_
#define _MEMORY_

/**************************************************************/
/* Includes                                                   */
/**************************************************************/

#include "misc.h"

/**************************************************************/
/* Data structures and constants                              */
/**************************************************************/

#ifndef memory__DYNMAXSIZE
#define memory__DYNMAXSIZE   1024 /* At most blocks of size memory__DYNMAXSIZE
				     bytes are administrated by the
				     module, larger requests are
				     directly mapped to system calls */
#endif

#ifndef memory__SHAREDPAGES
#define memory__SHAREDPAGES    1  /* Number of block sizes sharing an allocated 
				     page. By setting memory__SHAREDPAGES to 4, 
				     the module would administrate requests 
				     for 1, 2, 3 and 4 bytes on the same set of 
				     pages, requests for 5, 6, 7 and 8 on another 
				     one, etc. By default every block size has 
				     its own set of pages */
#endif

#ifndef memory__FREESHREDDER
#define memory__FREESHREDDER  'S' /*  The decimal number 83, which can also be 
				      read as 53 hexadecimal, or the character 
				      'S' in ASCII code */
#endif

#define memory__KILOBYTE        1024

#ifndef memory__DEFAULTPAGESIZE
#define memory__DEFAULTPAGESIZE (8 * memory__KILOBYTE)
                                  /* Used to set the default size of a page. 
				     If the default page size is too small to 
				     contain two objects of size memory__DYNMAXSIZE
				     the default page size is automatically 
				     increased by the function memory_Init */
#endif

#ifndef memory__UNLIMITED
#define memory__UNLIMITED       (-1)
                                  /* Used to set the maximal amount of memory
				     available for the memory module to
				     "unlimited" when calling memory_Init. */
#endif

typedef struct MEMORY_RESOURCEHELP {
  POINTER free;                    /* pointer to the next free block in list  */
  POINTER next;                    /* pointer to the next fresh block         */
  POINTER page;                    /* pointer to head of page list            */
  POINTER end_of_page;             /* pointer to the end of current page      */
  int     total_size;              /* total block size inc. debug marks       */
  int     aligned_size;            /* block size without debug marks          */
  int     offset;                  /* offset of last usable block on page     */
} MEMORY_RESOURCE;

extern MEMORY_RESOURCE * memory_ARRAY[];

#if defined(CHECK)
typedef struct MEMORY_INFOHELP {
  const char * mallocInFile;       /* origin of allocation request:   file    */
  const char * freeInFile;         /* origin of deallocation request: file    */
  unsigned short int mallocAtLine; /* origin of allocation request:   line    */
  unsigned short int freeAtLine;   /* origin of deallocation request: line    */
} MEMORY_INFONODE, * MEMORY_INFO;

#endif

typedef struct MEMORY_BIGBLOCKHEADERHELP {
  struct MEMORY_BIGBLOCKHEADERHELP * previous, * next;
} MEMORY_BIGBLOCKHEADERNODE, * MEMORY_BIGBLOCKHEADER;

extern long                  memory_MAXMEM;

extern unsigned long         memory_NEWBYTES;
extern unsigned long         memory_FREEDBYTES;

extern const unsigned int    memory_MAGICMALLOC;
extern const unsigned int    memory_MAGICFREE;

extern const unsigned int    memory_ALIGN;

extern MEMORY_BIGBLOCKHEADER memory_BIGBLOCKS;

/**************************************************************/
/* Debug Information                                          */
/**************************************************************/

extern unsigned int memory_MARKSIZE;
extern unsigned int memory_OFFSET;

/**************************************************************/
/* Check Functions                                            */
/**************************************************************/

#ifdef __cplusplus
extern "C" {
#endif


#ifdef CHECK
void    memory_CheckFree(POINTER Freepointer, unsigned int Size, unsigned int RealBlockSize, const char * File, unsigned short int Line);
#endif /* CHECK */

#ifdef __cplusplus
}
#endif

/**************************************************************/
/* Inline Functions                                           */
/**************************************************************/


static __inline__ unsigned int memory_CalculateRealBlockSize(unsigned int 
							     BlockSize)
/**********************************************************
  INPUT  : Size of a block of memory.
  RETURNS: its real size.
  SUMMARY: Calculates the size of a memory block, 
           including padding due to memory alignment and
           page sharing
**********************************************************/
{  
  unsigned int RealSize;

  RealSize = BlockSize;

  if (RealSize % memory__SHAREDPAGES) {
    RealSize += memory__SHAREDPAGES - (RealSize % memory__SHAREDPAGES);
  }

  if (RealSize % memory_ALIGN) {
    RealSize += memory_ALIGN - (RealSize % memory_ALIGN);
  }

  return RealSize;

}

static __inline__ unsigned int memory_LookupRealBlockSize(unsigned int 
							  BlockSize)
/**********************************************************
  INPUT  : Size of a block of memory.
  RETURNS: its real size.
  SUMMARY: Returns the size of a memory block, 
           including padding due to memory alignment and
           page sharing.
**********************************************************/
{  

  unsigned int RealSize;

  if (BlockSize < memory__DYNMAXSIZE) {
    RealSize = memory_ARRAY[BlockSize]->aligned_size;
  }
  else {
    RealSize = memory_CalculateRealBlockSize(BlockSize);
  }

  return RealSize;

}

#ifdef CHECK
static __inline__ void memory_SetBlockStatusAndSize(POINTER Mem, 
						    unsigned int Status,
						    unsigned int Size)
/**********************************************************
  INPUT  : a pointer to a block of memory, its status
           (memory_MAGICMALLOC or memory_MAGICFREE), and
           size.
  RETURNS: None.
  SUMMARY: Sets a status flag (memory_MAGICMALLOC or
           memory_MAGICFREE) and block size.
**********************************************************/
{
  *((int *)Mem - 1) = Size; 
  *((int *)((char *)Mem + memory_LookupRealBlockSize(Size))) = Status;
}

static __inline__ unsigned int memory_GetBlockSize(POINTER Mem)
/**********************************************************
  INPUT  : A pointer to a block of memory.
  RETURNS: its size.
  SUMMARY: Returns the size of a memory block.
**********************************************************/
{
  return *((int *)Mem - 1);
}

static __inline__ unsigned int memory_GetRealBlockSize(POINTER Mem)
/**********************************************************
  INPUT  : A pointer to a block of memory.
  RETURNS: its real size.
  SUMMARY: Returns the real size of a memory block,
           including padding bytes.
**********************************************************/
{
  return memory_LookupRealBlockSize(memory_GetBlockSize(Mem));
}

static __inline__ unsigned int memory_GetBlockStatus(POINTER Mem) 
/**********************************************************
  INPUT  : A pointer to a block of memory.
  RETURNS: its status.
  SUMMARY: Returns the status of a memory block.
**********************************************************/
{
  unsigned int Size;

  Size = memory_GetBlockSize(Mem); 

  return  *((int *)((char *)Mem + memory_LookupRealBlockSize(Size)));
}

static __inline__ void memory_SetInfo(MEMORY_INFO Info,
				      const char * MallocInFile,
				      unsigned short int MallocAtLine,
				      const char * FreeInFile,
				      unsigned short int FreeAtLine)
/**********************************************************
  INPUT  : a memory info structure, strings for files where
           the block was allocated and freed, and short 
	   integers for the corresponding lines.
  RETURNS: None.
  SUMMARY: Sets the debugging information for a memory block
**********************************************************/
{
  if (!Info) {
    misc_StartErrorReport();
    misc_ErrorReport("\n In memory_SetInfo:");
    misc_ErrorReport("\n Memory Error. Info is a NULL pointer.\n");
    misc_FinishErrorReport();
  }
 
  Info->mallocAtLine   = MallocAtLine;
  Info->mallocInFile   = MallocInFile;
  Info->freeAtLine     = FreeAtLine;
  Info->freeInFile     = FreeInFile;
}
#endif

static __inline__ unsigned long memory_DemandedBytes(void)
/**********************************************************
  INPUT  : Nothing.
  RETURNS: Maximum number of bytes allocated at the same 
           time by the module.
  SUMMARY: Returns maximum number of allocated bytes at the
           same time.
**********************************************************/
{
  return memory_NEWBYTES;
}


static __inline__ unsigned long memory_UsedBytes(void)
/**********************************************************
  INPUT  : Nothing.
  RETURNS: Number of bytes currently in use by your program.
  SUMMARY: Returns number of bytes currently allocated.
**********************************************************/
{
  return memory_NEWBYTES-memory_FREEDBYTES;
}

#ifdef NO_MEMORY_MANAGEMENT

static __inline__ void memory_Free(POINTER Freepointer, unsigned int Size)
{
  free((char*)Freepointer);
}

#else

#ifdef CHECK
static __inline__ void memory_FreeIntern(POINTER  Freepointer, 
					 unsigned int Size, 
					 const char * File, 
					 unsigned short int Line)
#else
static __inline__ void memory_Free(POINTER Freepointer, unsigned int Size)
#endif
/**********************************************************
  INPUT  : Pointer to the block of memory to be freed, 
           and the block's size
  RETURNS: Nothing.
  SUMMARY: Frees a block of memory for reallocation.
           This function performs correctness checks
	   in debugging mode.
**********************************************************/
{ 
  unsigned int RealBlockSize; /* blockīs alignment correct size */

  RealBlockSize = memory_LookupRealBlockSize(Size);

#ifdef CHECK
  memory_CheckFree(Freepointer, Size, RealBlockSize, File, Line);
#endif


  /* Check if current block is a big block */ 
  if (Size >= memory__DYNMAXSIZE) {
    /* if thatīs true, remove it from the double linked big block list */
    MEMORY_BIGBLOCKHEADER BigBlockHeader;

    BigBlockHeader = 
      (MEMORY_BIGBLOCKHEADER) ((char *) Freepointer - memory_OFFSET
			       - sizeof(MEMORY_BIGBLOCKHEADERNODE));

    /* Check if current big block is the first block in the list */
    if (BigBlockHeader->previous != NULL) {
      /* if thatīs not true, set previous blockīs successor
	 to current blockīs successor
      */
      BigBlockHeader->previous->next = BigBlockHeader->next;
    }
    else {
      /* otherwise set the first block in the big blocks list
	 to current blockīs successor
      */
      memory_BIGBLOCKS = BigBlockHeader->next;
    }

    /* Check if current block is the last block in the list */
    if (BigBlockHeader->next != NULL) {
      /* if thatīs not true, set next blockī predecessor to
	 current blockīs predecessor 
      */
      BigBlockHeader->next->previous = BigBlockHeader->previous;
    }

    /* Adapt total number of freed bytes 
       and number of bytes available for allocation
       accordingly
    */
    memory_FREEDBYTES       += RealBlockSize + memory_MARKSIZE + 
      sizeof(MEMORY_BIGBLOCKHEADERNODE);

    if (memory_MAXMEM >= 0) {
      memory_MAXMEM           += RealBlockSize + memory_MARKSIZE + 
	sizeof(MEMORY_BIGBLOCKHEADERNODE);
    }

    /* pass the call though to free() with correct pointer value */
#ifdef CHECK
    free((char *)Freepointer - memory_OFFSET 
	 - sizeof(MEMORY_BIGBLOCKHEADERNODE));
#else
    free((char*) Freepointer - sizeof(MEMORY_BIGBLOCKHEADERNODE));
#endif
  }
  else {
    /* current block is not a big block */
    /* Adapt number of allocated bytes 
       and the freed blocks list accordingly 
    */
    memory_FREEDBYTES       += memory_ARRAY[Size]->total_size;
    *(POINTER *)Freepointer  = memory_ARRAY[Size]->free;
    memory_ARRAY[Size]->free = Freepointer;
  }

}

#endif

/**************************************************************/
/* Functions                                                  */
/**************************************************************/

#ifdef __cplusplus
extern "C" {
#endif


void    memory_Init(long);
void    memory_Restrict(long);

void    memory_Print(void);
void    memory_FPrint(FILE*);
  
void    memory_PrintLeaks(void);
void    memory_PrintDetailed(void);
void    memory_PrintAllocatedBlocks(unsigned int Size);
void    memory_PrintFreedBlocks(unsigned int Size);
void    memory_PrintAllocatedBigBlocks(void);
  
void    memory_FreeAllMem(void);

#ifdef __cplusplus
}
#endif

#if defined(CHECK) && !defined(NO_MEMORY_MANAGEMENT)
/* declare drop-in debug versions memory functions */
#ifdef __cplusplus
extern "C" {
#endif
POINTER memory_MallocIntern(unsigned int, const char *, unsigned short int);
POINTER memory_CallocIntern(unsigned int,
			    unsigned int, 
			    const char *, 
			    unsigned short int);
#ifdef __cplusplus
}
#endif
#define memory_Malloc(Size) memory_MallocIntern((Size), __FILE__, __LINE__)
#define memory_Calloc(Elements, Size) \
memory_CallocIntern((Elements), (Size), __FILE__, __LINE__)
#define memory_Free(Pointer, Size) \
memory_FreeIntern((Pointer), (Size), __FILE__, __LINE__)
#else
#ifdef __cplusplus
extern "C" {
#endif
POINTER memory_Malloc(unsigned int);
POINTER memory_Calloc(unsigned int, unsigned int);
#ifdef __cplusplus
}
#endif
#endif

#endif
