/* Copyright (c) 1992, 1999, 2001 John E. Davis
 * This file is part of the S-Lang library.
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Perl Artistic License.
 */
#include "slinclud.h"

#ifdef SL_MALLOC_DEBUG
# undef SL_MALLOC_DEBUG
#endif

#include "slang.h"
#include "_slang.h"

#ifdef __alpha
# define Chunk 8
#else
# define Chunk 4
#endif

static long Total_Allocated;
static long Max_Single_Allocation;
static long Max_Allocated;
/* #define SLDEBUG_DOUT */

#ifdef SLDEBUG_DOUT
static FILE *dout;
#endif

void SLmalloc_dump_statistics (void)
{
#ifdef SLDEBUG_DOUT
   fflush (dout);
#endif
   fprintf (stderr, "Total Allocated: %ld\nHighest single allocation: %ld\nHighest Total Allocated:%ld\n",
	    Total_Allocated, Max_Single_Allocation, Max_Allocated);
}

static void register_at_exit_fun (void)
{
   static int is_registered = 0;
   if (is_registered)
     return;
   is_registered = 1;

#ifdef SLDEBUG_DOUT
   if (dout == NULL) dout = fopen ("malloc.out", "w");
#endif
   SLang_add_cleanup_function (SLmalloc_dump_statistics);
}

static void fixup (unsigned char *p, unsigned long n, char *what)
{
   register_at_exit_fun ();

   p += Chunk;
   *(p - 4)= (unsigned char) ((n >> 24) & 0xFF);
   *(p - 3) = (unsigned char) ((n >> 16) & 0xFF);
   *(p - 2) = (unsigned char) ((n >> 8) & 0xFF);
   *(p - 1) = (unsigned char) (n & 0xFF);
   *(p + (int) n) = 27;
   *(p + (int) (n + 1)) = 182;
   *(p + (int) (n + 2)) = 81;
   *(p + (int) (n + 3)) = 86;
   Total_Allocated += (long) n;
   if (Total_Allocated > Max_Allocated) Max_Allocated = Total_Allocated;
   if ((long) n > Max_Single_Allocation)
     Max_Single_Allocation = (long) n;

#ifdef SLDEBUG_DOUT
   fprintf (dout, "ALLOC: %s\t%p %ld\n", what, p, (long) n);
#else
   (void) what;
#endif
}

static void SLmalloc_doerror (char *buf)
{
   SLang_doerror (buf);
}

static int check_memory (unsigned char *p, char *what)
{
   char buf[128];
   unsigned long n;

   register_at_exit_fun ();

   n = ((unsigned long) *(p - 4)) << 24;
   n |= ((unsigned long) *(p - 3)) << 16;
   n |= ((unsigned long) *(p - 2)) << 8;
   n |= (unsigned long) *(p - 1);

   if (n == 0xFFFFFFFFUL)
     {
	sprintf (buf, "%s: %p: Already FREE! Abort NOW.", what, (void*)p - Chunk);
	SLmalloc_doerror (buf);
	return -1;
     }

   if ((*(p + (int) n) != 27)
       || (*(p + (int) (n + 1)) != 182)
       || (*(p + (int) (n + 2)) != 81)
       || (*(p + (int) (n + 3)) != 86))
     {
	sprintf (buf, "\007%s: %p: Memory corrupt! Abort NOW.", what, (void*)p);
	SLmalloc_doerror (buf);
	return -1;
     }

   *(p - 4) = *(p - 3) = *(p - 2) = *(p - 1) = 0xFF;

   Total_Allocated -= (long) n;
   if (Total_Allocated < 0)
     {
	sprintf (buf, "\007%s: %p\nFreed %ld, Allocated is: %ld!\n",
		 what, (void*)p, (long) n, Total_Allocated);
	SLang_doerror (buf);
     }
#ifdef SLDEBUG_DOUT
   fprintf (dout, "FREE: %s:\t%p %ld\n", what, p, (long) n);
#endif
   return 0;
}

void SLdebug_free (char *p)
{
   if (p == NULL) return;
   if (-1 == check_memory ((unsigned char *) p, "FREE")) return;

   SLFREE (p - Chunk);
}

char *SLdebug_malloc (unsigned long n)
{
   char *p;

   if ((p = (char *) SLMALLOC (n + 2 * Chunk)) == NULL) return NULL;

   fixup ((unsigned char *) p, n, "MALLOC");
   return p + Chunk;
}

char *SLdebug_realloc (char *p, unsigned long n)
{
   if (-1 == check_memory ((unsigned char *) p, "REALLOC")) return NULL;
   if ((p = (char *) SLREALLOC (p - Chunk, n + 2 * Chunk)) == NULL) return NULL;
   fixup ((unsigned char *) p, n, "REALLOC");
   return p + Chunk;
}

char *SLdebug_calloc (unsigned long n, unsigned long size)
{
   char *p;
   int m;

   /* This is tough -- hope this is a good assumption!! */
   if (size >= Chunk) m = 1; else m = Chunk;

   if ((p = (char *) SLCALLOC (n + m + m, size)) == NULL) return NULL;
   fixup ((unsigned char *) p, size * n, "CALLOC");
   return p + Chunk;
}