/*-------------------------------------------------------------------------
   newalloc.c - SDCC Memory allocation functions

   These functions are wrappers for the standard malloc, realloc and free
   functions.

   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, 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <assert.h>
#include "newalloc.h"

#if OPT_ENABLE_LIBGC
#include <gc/gc.h>

#define MALLOC	GC_malloc
#define REALLOC GC_realloc
/* PENDING: This is a mild hack.  If we try to GC_free something
   allocated with malloc() then the program will segfault.  Might as
   well drop it and let the garbase collector take care of things.
*/
#define FREE(_a)

#else

#define MALLOC	malloc
#define REALLOC realloc
#define FREE	free

#endif

#define TRACEMALLOC	0

#if TRACEMALLOC
enum
{
  TRACESIZE = 4096
};

static int _allocs[TRACESIZE];
static int _above;

static void
_dumpTrace (int code, void *parg)
{
  int i;
  for (i = 0; i < TRACESIZE; i++)
    {
      if (_allocs[i])
        {
          printf ("%u %u\n", _allocs[i], i);
        }
    }
  printf ("%u above\n", _above);
}

static void
_log (int size)
{
  static int registered;

  if (registered == 0)
    {
      on_exit (_dumpTrace, NULL);
      registered = 1;
    }
  if (size == 12)
    {
      _above++;
    }

  if (size >= TRACESIZE)
    {
      _above++;
    }
  else
    {
      _allocs[size]++;
    }
}
#endif

/*
-------------------------------------------------------------------------------
Clear_realloc - Reallocate a memory block and clear any memory added with
out of memory error detection

-------------------------------------------------------------------------------
*/

void *
Clear_realloc (void *OldPtr, size_t OldSize, size_t NewSize)
{
  void *NewPtr;

  NewPtr = REALLOC (OldPtr, NewSize);

  if (!NewPtr)
    {
      printf ("ERROR - No more memory\n");
/*  werror(E_OUT_OF_MEM,__FILE__,NewSize);*/
      exit (1);
    }

  if (NewPtr)
    if (NewSize > OldSize)
      memset ((char *) NewPtr + OldSize, 0x00, NewSize - OldSize);

  return NewPtr;
}

/*
-------------------------------------------------------------------------------
Safe_realloc - Reallocate a memory block with out of memory error detection

-------------------------------------------------------------------------------
*/

void *
Safe_realloc (void *OldPtr, size_t NewSize)
{
  void *NewPtr;

  NewPtr = REALLOC (OldPtr, NewSize);

  if (!NewPtr)
    {
      printf ("ERROR - No more memory\n");
/*  werror(E_OUT_OF_MEM,__FILE__,NewSize);*/
      exit (1);
    }

  return NewPtr;
}

/*
-------------------------------------------------------------------------------
Safe_calloc - Allocate a block of memory from the application heap, clearing
all data to zero and checking for out of memory errors.

-------------------------------------------------------------------------------
*/

void *
Safe_calloc (size_t Elements, size_t Size)
{
  void *NewPtr;

  NewPtr = MALLOC (Elements * Size);
#if TRACEMALLOC
  _log (Elements * Size);
#endif

  if (!NewPtr)
    {
      printf ("ERROR - No more memory\n");
/*  werror(E_OUT_OF_MEM,__FILE__,Size);*/
      exit (1);
    }

  memset (NewPtr, 0, Elements * Size);

  return NewPtr;
}

/*
-------------------------------------------------------------------------------
Safe_malloc - Allocate a block of memory from the application heap
and checking for out of memory errors.

-------------------------------------------------------------------------------
*/

void *
Safe_malloc (size_t Size)
{
  void *NewPtr;

  NewPtr = MALLOC (Size);

#if TRACEMALLOC
  _log (Size);
#endif

  if (!NewPtr)
    {
      printf ("ERROR - No more memory\n");
/*  werror(E_OUT_OF_MEM,__FILE__,Size);*/
      exit (1);
    }

  return NewPtr;
}

void *
Safe_alloc (size_t Size)
{
  return Safe_calloc (1, Size);
}

void
Safe_free (void *p)
{
  FREE (p);
}

char *
Safe_strndup (const char *sz, size_t size)
{
  char *pret;
  assert (sz);

  pret = Safe_alloc (size + 1);
  strncpy (pret, sz, size);
  pret[size] = '\0';

  return pret;
}

char *
Safe_strdup (const char *sz)
{
  assert (sz);

  return Safe_strndup (sz, strlen (sz));
}

void *
traceAlloc (allocTrace * ptrace, void *p)
{
  assert (ptrace);
  assert (p);

  /* Also handles where max == 0 */
  if (ptrace->num == ptrace->max)
    {
      /* Add an offset to handle max == 0 */
      ptrace->max = (ptrace->max + 2) * 2;
      ptrace->palloced = Safe_realloc (ptrace->palloced, ptrace->max * sizeof (*ptrace->palloced));
    }
  ptrace->palloced[ptrace->num++] = p;

  return p;
}

void
freeTrace (allocTrace * ptrace)
{
  int i;
  assert (ptrace);

  for (i = 0; i < ptrace->num; i++)
    {
      Safe_free (ptrace->palloced[i]);
    }
  ptrace->num = 0;

  Safe_free (ptrace->palloced);
  ptrace->palloced = NULL;
  ptrace->max = 0;
}
