//////////////////////////////////////////////////////////////
///                                                        ///
/// xMemMgt.c: memory management functions of liblx        ///
///                                                        ///
//////////////////////////////////////////////////////////////

// $Revision: 1.1 $
// $Date: 2001/06/25 12:29:47 $
// $Author: hakenes $
//
//   (C) 1992-2001 Rolf Hakenes <hakenes@hippomi.de>, under the GNU GPL.
//
// liblx 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, or (at your option)
// any later version.
//
// liblx 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 may have received a copy of the GNU General Public License
// along with liblx; see the file COPYING.  If not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

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

#include "liblx.h"

#ifdef DEBUG
void logPrintf(int, char *, ...);
#endif

static struct MEM_CHUNK *xRememberKey = NULL;

static struct MEM_CHUNK **xRememberPtr = &xRememberKey;

unsigned long xAllocatedMemory = 0;

/*************************************************************************
 *                                                                       *
 *     function  :   xMemAlloc                                           *
 *                                                                       *
 *     parameter :   Size - size of the requested memory area            *
 *                                                                       *
 *                   DataPointer - pointer to data pointer               *
 *                                                                       *
 *     return    :   none                                                *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xMemAlloc() is a clustered, remembering memory management routine.  *
 *   It uses its own tables for free and used memory blocks on private   *
 *   memory area. With xMemFree(), you can free this memory likewise     *
 *   the C free() routine, with xMemFreeAll() all memory at once.        *
 *   By changing the current remember key with xSetRemember() you can    *
 *   define a local memory area, which can be freed by only one call of  *
 *   xMemFreeAll() (see xSetRemember() / xGetRemember()).                *
 *                                                                       *
 *************************************************************************/

void xMemAllo (Size, DataPointer)

unsigned long Size;
unsigned char **DataPointer;
{
   struct MEM_CHUNK        *MemChunk, *MemChunkPred;
   struct MEM_ENTRY        *MemEntry, *MemEntryPred;
   long int                 NewSize;
   unsigned short           FoundFlag;
#ifdef DEBUG
   unsigned char           *ptr;
#endif

   while (Size % 4) Size++;

   if (Size > (MEM_CHUNK_SIZE - sizeof(struct MEM_CHUNK) - 
                  sizeof(struct MEM_ENTRY)))
   {
      NewSize = Size + sizeof(struct MEM_CHUNK) + sizeof(struct MEM_ENTRY);

      if (MemChunk = (*xRememberPtr))
      {
         do
         {
            MemChunkPred = MemChunk;
         } while (MemChunk = MemChunk->Succ);
      }
      else MemChunkPred = (struct MEM_CHUNK *) &(*xRememberPtr);

      MemChunk = MemChunkPred->Succ = (struct MEM_CHUNK *) malloc (NewSize);
      xAllocatedMemory += NewSize;

#ifdef DEBUG
      for (ptr = (unsigned char *) MemChunk; ptr < (unsigned char *)
            (MemChunk) + NewSize; ptr++)
             *ptr = (((unsigned long)ptr)&1) ? 0x55 : 0xAA;
#endif

      if (!MemChunk)
      {
#ifdef DEBUG
         logPrintf (0, "Not enough memory...\r\n");
#endif
         exit (1);
      }

      MemChunk->Size = NewSize;
      MemChunk->Pred = MemChunkPred;
      MemChunk->Succ = NULL;
      MemChunk->FirstFreeMemEntry = NULL;
      MemChunk->FirstUsedMemEntry =
         MemEntry = (struct MEM_ENTRY *) ((unsigned char *)MemChunk +
                       sizeof(struct MEM_CHUNK));

      MemEntry->Size = Size;
      MemEntry->Pred = (struct MEM_ENTRY *) &MemChunk->FirstUsedMemEntry;
      MemEntry->Succ = NULL;

      *DataPointer = (unsigned char *) ((unsigned char *)MemEntry +
                                           sizeof(struct MEM_ENTRY));
#ifdef DEBUG_CALLS
      logPrintf (0, "xMemAlloc: %x, %d bytes\r\n", *DataPointer, Size);
#endif
      return;
   }

   MemEntry = NULL;
   FoundFlag = 0;

   if (MemChunk = (*xRememberPtr))
   {
      do
      {
         if (MemEntry = MemChunk->FirstFreeMemEntry)
         do
         {
            if (Size <= MemEntry->Size) FoundFlag = 1;
         } while ((FoundFlag == 0) && (MemEntry = MemEntry->Succ));
         MemChunkPred = MemChunk;
      } while ((FoundFlag == 0) && (MemChunk = MemChunk->Succ));
   }
   else MemChunkPred = (struct MEM_CHUNK *) &(*xRememberPtr);

   if (!MemEntry)
   {
      MemChunk = MemChunkPred->Succ =
         (struct MEM_CHUNK *) malloc (MEM_CHUNK_SIZE);
      xAllocatedMemory += MEM_CHUNK_SIZE;

#ifdef DEBUG
      for (ptr = (unsigned char *) MemChunk; ptr < (unsigned char *)
            (MemChunk) + MEM_CHUNK_SIZE; ptr++)
             *ptr = (((unsigned long)ptr)&1) ? 0x55 : 0xAA;
#endif

      if (!MemChunk)
      {
#ifdef DEBUG
         logPrintf (0, "Not enough memory...\r\n");
#endif
         exit (1);
      }

      MemChunk->Size = MEM_CHUNK_SIZE;
      MemChunk->Pred = MemChunkPred;
      MemChunk->Succ = NULL;
      MemChunk->FirstUsedMemEntry = NULL;
      MemChunk->FirstFreeMemEntry =
         MemEntry = (struct MEM_ENTRY *) 
		               ((unsigned char *)MemChunk + sizeof(struct MEM_CHUNK));

      MemEntry->Size = MEM_CHUNK_SIZE - sizeof(struct MEM_CHUNK) - 
	                                       sizeof(struct MEM_ENTRY);
      MemEntry->Pred = (struct MEM_ENTRY *) &MemChunk->FirstFreeMemEntry;
      MemEntry->Succ = NULL;
   }

   NewSize = MemEntry->Size - sizeof(struct MEM_ENTRY) - Size;

   MemEntry->Size = Size;
   *DataPointer = (unsigned char *) 
                     ((unsigned char *)MemEntry + sizeof(struct MEM_ENTRY));

#ifdef DEBUG
   for (ptr = *DataPointer; ptr < (unsigned char *)
            (*DataPointer) + Size; ptr++)
   {
     if (((unsigned long )ptr)&1)
     {  if (*ptr != 0x55)
           logPrintf (0, "freed memory was used\r\n"); }
     else { if (*ptr != 0xAA)
           logPrintf (0, "freed memory was used\r\n"); }
   }
#endif

   if (MemEntry->Succ) 
      ((struct MEM_ENTRY *)MemEntry->Succ)->Pred = MemEntry->Pred;
   ((struct MEM_ENTRY *)MemEntry->Pred)->Succ = MemEntry->Succ;

   if (MemChunk->FirstUsedMemEntry)
      MemChunk->FirstUsedMemEntry->Pred = MemEntry;
   MemEntry->Succ = MemChunk->FirstUsedMemEntry;
   MemChunk->FirstUsedMemEntry = MemEntry;
   MemEntry->Pred = (struct MEM_ENTRY *) &MemChunk->FirstUsedMemEntry;

   if (NewSize > 0)
   {
      MemEntry = (struct MEM_ENTRY *) 
	          ((unsigned char *)MemEntry + sizeof(struct MEM_ENTRY) + Size);
      MemEntry->Size = NewSize;

      if (MemChunk->FirstFreeMemEntry)
         MemChunk->FirstFreeMemEntry->Pred = MemEntry;
      MemEntry->Succ = MemChunk->FirstFreeMemEntry;
      MemChunk->FirstFreeMemEntry = MemEntry;
      MemEntry->Pred = (struct MEM_ENTRY *) &MemChunk->FirstFreeMemEntry;
   }
#ifdef DEBUG_CALLS
   logPrintf (0, "xMemAlloc: %x, %d bytes\r\n", *DataPointer, Size);
#endif
   return;
}



/*************************************************************************
 *                                                                       *
 *     function  :   xMemFree                                            *
 *                                                                       *
 *     parameter :   DataPointer - data pointer                          *
 *                                                                       *
 *     return    :   none                                                *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xMemFree() frees with xMemAlloc() allocated memory.                 *
 *                                                                       *
 *************************************************************************/

void xMemFre (DataPointer)

unsigned char *DataPointer;
{
   struct MEM_CHUNK            *MemChunk, *MemChunkPred;
   struct MEM_ENTRY            *MemEntry, *TempEntry, *PredEntry, *SuccEntry;
   unsigned short               FoundFlag;
#ifdef DEBUG
   unsigned char           *ptr;
#endif

   if (!DataPointer)
   {
      return;
   }
   else
   {
      MemEntry = NULL;
      FoundFlag = 0;

      if (MemChunk = (*xRememberPtr))
      do
      {
         if (MemEntry = MemChunk->FirstUsedMemEntry)
         do
         {
            if (DataPointer == (unsigned char *) ((unsigned char *) MemEntry +
                   sizeof(struct MEM_ENTRY))) FoundFlag = 1;
         } while ((FoundFlag == 0) && (MemEntry = MemEntry->Succ));
      } while ((FoundFlag == 0) && (MemChunk = MemChunk->Succ));

      if (FoundFlag == 1)
      {
#ifdef DEBUG_CALLS
   logPrintf (0, "xMemFree: %x, %d bytes\r\n", DataPointer, MemEntry->Size);
#endif
         if (MemEntry->Succ)
            ((struct MEM_ENTRY *)MemEntry->Succ)->Pred = MemEntry->Pred;
         ((struct MEM_ENTRY *)MemEntry->Pred)->Succ = MemEntry->Succ;

         if (!MemChunk->FirstUsedMemEntry)
         {
            if (MemChunk->Succ)
               ((struct MEM_CHUNK *)MemChunk->Succ)->Pred = MemChunk->Pred;
            ((struct MEM_CHUNK *)MemChunk->Pred)->Succ = MemChunk->Succ;
            if (xAllocatedMemory > 0) xAllocatedMemory -= MemChunk->Size;
            free (MemChunk);
            return;
         }

         FoundFlag = 0;
         PredEntry = NULL;
         SuccEntry = NULL;
         if (TempEntry = MemChunk->FirstFreeMemEntry)
         do
         {
            if ((struct MEM_ENTRY *)((unsigned char *)TempEntry +
                  TempEntry->Size + sizeof(struct MEM_ENTRY)) == MemEntry)
            {
               FoundFlag ++;
               PredEntry = TempEntry;
            }
            if ((struct MEM_ENTRY *)((unsigned char *)MemEntry +
                  MemEntry->Size + sizeof(struct MEM_ENTRY)) == TempEntry)
            {
               FoundFlag ++;
               SuccEntry = TempEntry;
            }
         } while ((FoundFlag != 2) && (TempEntry = TempEntry->Succ));

         if (PredEntry)
         {
            if (SuccEntry)
            {
               /* Vorgdnger + Nachfolger */

               if (SuccEntry->Succ)
                  ((struct MEM_ENTRY *)SuccEntry->Succ)->Pred = SuccEntry->Pred;
               ((struct MEM_ENTRY *)SuccEntry->Pred)->Succ = SuccEntry->Succ;

               PredEntry->Size += MemEntry->Size + sizeof(struct MEM_ENTRY) +
                                  SuccEntry->Size + sizeof(struct MEM_ENTRY);
            }
            else
            {
               /* nur Vorgaenger */

               PredEntry->Size += MemEntry->Size + sizeof(struct MEM_ENTRY);
            }
#ifdef DEBUG
            for (ptr = (unsigned char *) (PredEntry) + sizeof(struct MEM_ENTRY);
                ptr < (unsigned char *) (PredEntry) + sizeof(struct MEM_ENTRY) +
                PredEntry->Size; ptr++)
                  *ptr = (((unsigned long)ptr)&1) ? 0x55 : 0xAA;
#endif
         }
         else
         {
            if (SuccEntry)
            {
               /* nur Nachfolger */

               if (SuccEntry->Succ)
                  ((struct MEM_ENTRY *)SuccEntry->Succ)->Pred = SuccEntry->Pred;
               ((struct MEM_ENTRY *)SuccEntry->Pred)->Succ = SuccEntry->Succ;

               MemEntry->Size += SuccEntry->Size + sizeof(struct MEM_ENTRY);
            }

            if (MemChunk->FirstFreeMemEntry)
               MemChunk->FirstFreeMemEntry->Pred = MemEntry;
            MemEntry->Succ = MemChunk->FirstFreeMemEntry;
            MemChunk->FirstFreeMemEntry = MemEntry;
            MemEntry->Pred = (struct MEM_ENTRY *) &MemChunk->FirstFreeMemEntry;
#ifdef DEBUG
            for (ptr = (unsigned char *) (MemEntry) + sizeof(struct MEM_ENTRY);
                ptr < (unsigned char *) (MemEntry) + sizeof(struct MEM_ENTRY) +
                MemEntry->Size; ptr++)
                 *ptr = (((unsigned long)ptr)&1) ? 0x55 : 0xAA;
#endif
         }
      }
#ifdef DEBUG_CALLS
   else
   logPrintf (0, "xMemFree: tried to free unallocated data %x\r\n", DataPointer);
#endif
   }
   return;
}



/*************************************************************************
 *                                                                       *
 *     function  :   xMemFreeAll                                         *
 *                                                                       *
 *     parameter :   RememberPtr                                         *
 *                                                                       *
 *     return    :   none                                                *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xMemFreeAll() frees all with xMemAlloc() allocated memory. If Re-   *
 *   memberPtr is not NULL, the MEM_CHUNK structure from the specified   *
 *   Address is freed, otherwise the natural MEM_CHUNK will be done.     *
 *                                                                       *
 *************************************************************************/


void xMemFreeAll (RememberPtr)

struct MEM_CHUNK **RememberPtr;
{
   struct MEM_CHUNK            *MemChunk, *MemChunkPred;

   if (RememberPtr)
   {
      if (MemChunkPred = (*RememberPtr))
      do
      {
         MemChunk = MemChunkPred->Succ;
         if (xAllocatedMemory > 0) xAllocatedMemory -= MemChunkPred->Size;
         free (MemChunkPred);
      } while (MemChunkPred = MemChunk);
      *RememberPtr = NULL;
   }
   else
   {
      if (MemChunkPred = (*xRememberPtr))
      do
      {
         MemChunk = MemChunkPred->Succ;
         if (xAllocatedMemory > 0) xAllocatedMemory -= MemChunkPred->Size;
         free (MemChunkPred);
      } while (MemChunkPred = MemChunk);
      *xRememberPtr = NULL;
   }
}


/*************************************************************************
 *                                                                       *
 *     function  :   xMemMerge                                           *
 *                                                                       *
 *     parameter :   RememberPtr                                         *
 *                                                                       *
 *     return    :   none                                                *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xMemMerge() merges the memory area pointed to by RememberKey with   *
 *   the currently used in xRememberPtr.                                 *
 *                                                                       *
 *************************************************************************/

void xMemMerge (RememberPtr)

struct MEM_CHUNK **RememberPtr;
{
   struct MEM_CHUNK            *MemChunk, *MemChunkPred;

   if (RememberPtr)
   {
      if (MemChunk = (*xRememberPtr))
      {
         while (MemChunk->Succ) MemChunk = MemChunk->Succ;
         MemChunk->Succ = (*RememberPtr);
         *RememberPtr = NULL;
      }
      else (*xRememberPtr = *RememberPtr);
   }
   return;
}

/*************************************************************************
 *                                                                       *
 *     function  :   xGetRemember                                        *
 *                                                                       *
 *     parameter :   none                                                *
 *                                                                       *
 *     return    :   pointer to a MEM_CHUNK tree                         *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xGetRemember() returns the currently used MEM_CHUNK tree.           *
 *                                                                       *
 *************************************************************************/


struct MEM_CHUNK **xGetRemember ()
{
   return (xRememberPtr);
}


/*************************************************************************
 *                                                                       *
 *     function  :   xSetRemember                                        *
 *                                                                       *
 *     parameter :   pointer to a MEM_CHUNK tree                         *
 *                                                                       *
 *     return    :   none                                                *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xSetRemember() redefines the currently used MEM_CHUNK pointer. If   *
 *   RememberPtr is NULL, the natural MEM_CHUNK is reloaded.             *
 *                                                                       *
 *************************************************************************/


void xSetRemember (RememberPtr)

struct MEM_CHUNK **RememberPtr;
{
   if (RememberPtr)
      xRememberPtr = RememberPtr;
   else
      xRememberPtr = &xRememberKey;
}

/*************************************************************************
 *                                                                       *
 *     function  :   xPrintMemList                                       *
 *                                                                       *
 *     parameter :   pointer to a MEM_CHUNK tree                         *
 *                                                                       *
 *     return    :   none                                                *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xPrintMemList() prints the currently allocated memory blocks of     *
 *   the specified RememberPtr.                                          *
 *                                                                       *
 *************************************************************************/


void xPrintMemList (Remember)

struct MEM_CHUNK **Remember;
{
   struct MEM_CHUNK           *MemChunk;
   struct MEM_ENTRY           *MemEntry;

   fprintf (stderr, "MemChunkPtr = %x\n", (int) Remember);

   if (MemChunk = *Remember)
   do
   {
      fprintf (stderr, "\tMemChunk at %x with Size %d\n", (int) MemChunk, 
                 (int) MemChunk->Size);

      if (MemEntry = MemChunk->FirstFreeMemEntry)
      do
      {
         fprintf (stderr, "\t\tFree MemEntry at %x (%x) with Size %d\n", 
                   (int) MemEntry, (int)((unsigned char *)MemEntry + 
                      sizeof(struct MEM_ENTRY)), (int) MemEntry->Size);

      } while (MemEntry = MemEntry->Succ);

      if (MemEntry = MemChunk->FirstUsedMemEntry)
      do
      {
         fprintf (stderr, "\t\tUsed MemEntry at %x (%x) with Size %d\n", 
                   (int) MemEntry, (int)((unsigned char *)MemEntry + 
                      sizeof(struct MEM_ENTRY)), (int) MemEntry->Size);

      } while (MemEntry = MemEntry->Succ);

   } while (MemChunk = MemChunk->Succ);
   else fprintf (stderr, "\tNo current MemChunk\n");
}


/*************************************************************************
 *                                                                       *
 *     function  :   xGetMemSize                                         *
 *                                                                       *
 *     parameter :   pointer to a MEM_CHUNK tree                         *
 *                                                                       *
 *     return    :   none                                                *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xGetMemSize() gets the size of the currently allocated memory       *
 *   blocks of the specified (or natural if NULL) RememberPtr            *
 *                                                                       *
 *************************************************************************/


unsigned long xGetMemSize (RememberPtr)

struct MEM_CHUNK **RememberPtr;
{
   struct MEM_CHUNK           *MemChunk;
   struct MEM_ENTRY           *MemEntry;
   unsigned long               Result = 0;

   if (RememberPtr) MemChunk = *RememberPtr;
   else MemChunk = xRememberKey;

   if (MemChunk)
   do { Result += (unsigned long) MemChunk->Size; }
   while (MemChunk = MemChunk->Succ);

   return (Result);
}


/*************************************************************************
 *                                                                       *
 *     function  :   xSetText                                            *
 *                                                                       *
 *     arguments :   xText - pointer to a string                         *
 *                                                                       *
 *     return    :   pointer to an new allocated string                  *
 *                                                                       *
 *-----------------------------------------------------------------------*
 *                                                                       *
 *   xSetText() allocates memory for the string pointed to by 'xText'    *
 *   and duplicates it.                                                  *
 *                                                                       *
 *************************************************************************/

char *xSetText (xText)

char *xText;
{
   char   *NewText;

   if (!xText) return (NULL);

   xMemAlloc (strlen(xText) + 1, &NewText);
   strcpy (NewText, xText);

   return (NewText);   
}