/* ---------------------------------------------------------------------
   (c) ED 1999-2008
   Projet       : CLIB
   Fonction     : allocation memoire
   Module       : SYSALLOC
   Fichier      : SYSALLOC.C
   Creation     : 10-10-1999
   Modification : 27-08-2008
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Journal

   1.22 du 27-08-2008 Dans sys_free() : traitements avant free() véritable.
   .                  Ajout de sys_mem_sver() et sys_mem_sid()
   .                  Mise au point compteur d'allocation
   1.21 du 06-04-2008 Mise au point du mode SYS_MEMCHK=0
   1.20 du 05-04-2008 Suppression eMEM
   1.19 du 25-02-2008 ajoute macro strdup()
   1.18 du 29-04-2005 'line' devient int au lieu de size_t
   1.17 du 08-12-2003 sys_init(), sys_trace() et sys_counters() deviennent
   .                  sys_mem_*()
   1.16 du 11-07-2003 Ajout de sys_counters()
   .                  deplace sys_time() et sys_clock() dans sys.h/c
   .                  Prise en compte du compilateur DIAB
   1.15 du 16-05-2003 Mise au point realloc() et free()
   1.14 du 04-04-2003 Ajoute FREE()
   1.13 du 11-12-2002 Ajoute un #include <alloc.h> preventif
   1.12 du 08-12-2002 Ajoute macro realloc(). Mise au point realloc()
   1.11 du 11-10-2002 Ajoute macro SYS_INIT()
   1.10 du 13-07-2002 LCLint
   1.9 du 24-04-2002 Dans la trace, ajout du nombre de bytes liberes
   1.8 du 03-09-2001 Ajout de la macro SYS_MEMCHK
   1.7 du 17-01-2001 Ajoute un parametre "cfg" a sys_mem_init()
   1.6 du 02-08-2000 deplace le _ a la fin pour etre conforme a l'ISO
   1.5 du 12-04-2000 Ajout de sys_clock() et sys_time()
   1.4 du 17-01-2000 remplace 'fred' par 'freed'
   1.3 du 08-12-1999 Suppression de MEM_VOL
   1.2 du 23-11-1999 Matching en TR. Optimisation
   1.1 du 19-11-1999 Ajout d'un traceur
   1.0 du 10-10-1999 Version operationelle
   0.0 du 10-10-1999 Creation
   --------------------------------------------------------------------- */
#ifdef __cplusplus
#error This source file is not C++ but C. Please use a C compiler.
#endif

#undef DBG_SYSALLOC
#define DBG 1

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

#include "ed/inc/sysalloc.h"

#if DBG

#include "ed/inc/sys.h"
#include "ed/inc/cnt.h"

#include <stdio.h>
#define PC_pause(a) (void)getchar()

#endif

#undef sys_mem_init
#undef sys_mem_trace
#undef sys_malloc
#undef sys_calloc
#undef sys_realloc
#undef sys_free

#if defined (__BORLANDC__)
#include <alloc.h>
#if 1
#define CORELEFT() coreleft()
#else
#define CORELEFT() farcoreleft()
#endif
#elif defined (__GNUC__)
#define CORELEFT() ((ulong) -1)
#elif defined (__DIAB)
#define CORELEFT() ((ulong) -1)
#elif defined (_MSC_VER)
#define CORELEFT() ULONG_MAX
#else
#error CORELEFT was not defined for this platform
#endif

/* macros ============================================================== */

#define MODULE "SYSALLOC"
#define VER "1.22"
#define ID MODULE " Module \"C\" (c) ED 1999-2008"

/* trace en TR */
#define RT_TRACE 1

#if DBG
typedef enum
{
   ACT_MALLOC = 'M',
   ACT_CALLOC = 'C',
   ACT_REALLOC = 'R',
   ACT_FREE = 'F',
   ACT_NB
}
eACT;

#define CHECK() ASSERT(strcmp(pTrace->szTag,MODULE)==0)
/*#define CHECK */
#endif

/* constants =========================================================== */
/* types =============================================================== */
/* structures ========================================================== */

#if DBG
typedef struct
{
   cnt_s allocated;
}
count_s;

typedef struct
{
   void const *p;
   void *pOld;
   size_t len;
   eACT act;
   char const *szFile;
   int line;
}
sREC;

typedef struct
{
   ulong mem;
   ulong mem_min;
   size_t cpt;
   size_t max;
   eTRACE eTrace;
   sREC *ap;
   int ovl;                     /* depassement */
   count_s count;
   char szTag[1 + sizeof MODULE];

}
sTRACE;
#endif

/* private data ======================================================== */

#if DBG
static sTRACE G_Trace;
#endif

/* private functions =================================================== */

#if DBG

/* ---------------------------------------------------------------------
   GetAlloc()
   ---------------------------------------------------------------------
   Role : retourne le malloc, calloc ou realloc si il existe
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static sREC *GetAlloc (sREC * const apRec, size_t const max,
                       void const *const p)
{
   sREC *pRec = NULL;
   int found = 0;
   size_t rec;

   for (rec = 0; !found && (rec < max); rec++)
   {
      sREC *const pAlloc = apRec + rec;
      int act = pAlloc->act;

      switch (act)
      {
      case ACT_MALLOC:
      case ACT_CALLOC:
      case ACT_REALLOC:

         if (pAlloc->p == p)
         {
            found = 1;
            pRec = pAlloc;
         }

         break;
      }
   }
   return pRec;
}

/* ---------------------------------------------------------------------
   GetFree()
   ---------------------------------------------------------------------
   Role : retourne le free correspondant si il existe
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static sREC *GetFree (sREC * const apRec, size_t const max,
                      void const *const p)
{
   sREC *pRec = NULL;
   size_t rec;
   int found = 0;

   for (rec = 0; !found && (rec < max); rec++)
   {
      sREC *const pFree = apRec + rec;
         int act = pFree->act;

      switch (act)
      {
      case ACT_FREE:

         if (pFree->p == p)
         {
            found = 1;
            pRec = pFree;
         }

         break;
      }
   }
   return pRec;
}

/* ---------------------------------------------------------------------
   action()
   ---------------------------------------------------------------------
   Role : retourne le free correspondant si il existe
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static const char *action (int act)
{
   const char *sz = "?";

   switch (act)
   {
   case ACT_MALLOC:
      sz = "malloc";
      break;
   case ACT_CALLOC:
      sz = "calloc";
      break;
   case ACT_REALLOC:
      sz = "realloc";
      break;
   }
   return sz;
}

/* ---------------------------------------------------------------------
   list()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static void list (sREC * const apRec, size_t const count)
{
   size_t rec;

   {
      int first = 1;

      for (rec = 0; rec < count; rec++)
      {
         sREC *const pRec = &apRec[rec];

         if (pRec->p != NULL)
         {
            int const act = pRec->act;

            switch (act)
            {
            case ACT_MALLOC:
            case ACT_CALLOC:
            case ACT_REALLOC:

               if (first)
               {
                  printf (MODULE " Err: Not-matched list:\n");
                  first = 0;
               }

               printf (MODULE " Bloc %p (%lu byte%s) %s'ed at line %lu "
                       "of '%s' not freed\n", (void const *) pRec->p,
                       (ulong) pRec->len, pRec->len > 2 ? "s" : "",
                       action (act), (ulong) pRec->line, pRec->szFile);
               break;

            case ACT_FREE:

               if (first)
               {
                  printf (MODULE " Err: Not-matched list:\n");
                  first = 0;
               }

               printf (MODULE
                       " Bloc %p freed at line %lu of '%s' not allocated\n",
                       (void const *) pRec->p, (ulong) pRec->line,
                       pRec->szFile);

               break;

            default:
               printf (MODULE " '%c %p %lu %4lu:%s'\n", pRec->act,
                       (void const *) pRec->p, (ulong) pRec->len,
                       (ulong) pRec->line, pRec->szFile);
            }
         }
      }

      if (first)
      {
         printf (MODULE " All-matched\n");
         first = 0;             /* inutile pour le moment */
      }
   }
}

/* ---------------------------------------------------------------------
   GetFreeRec()
   ---------------------------------------------------------------------
   Chercher un emplacement libre
   ---------------------------------------------------------------------
   E : contexte de trace
   S :
   --------------------------------------------------------------------- */
static sREC *GetFreeRec (sTRACE * pTrace)
{
   sREC *pRec = NULL;
   size_t i;

   for (i = 0; i < pTrace->max; i++)
   {
      if (pTrace->ap[i].p == NULL)
      {
         pRec = &pTrace->ap[i];
         break;
      }
   }

   return pRec;
}

/* ---------------------------------------------------------------------
   put_rec()
   ---------------------------------------------------------------------
   Enregister une action
   ---------------------------------------------------------------------
   E : contexte de l'action
   E : pointeur
   E : longueur
   E : action
   S :
   --------------------------------------------------------------------- */
static void put_rec (sREC * pRec, void const *const p, size_t const len,
                     eACT const act, char const *szFile, int line)
{
   pRec->p = p;
   pRec->len = len;
   pRec->act = act;
   pRec->szFile = szFile;
   pRec->line = line;
}

/* ---------------------------------------------------------------------
   clr_rec()
   ---------------------------------------------------------------------
   Effacer une action
   ---------------------------------------------------------------------
   E : contexte de l'action
   S :
   --------------------------------------------------------------------- */
static void clr_rec (sREC * pRec)
{
   pRec->p = NULL;
   pRec->len = 0;
   pRec->act = -1;
   pRec->szFile = MODULE;
   pRec->line = __LINE__;
}

/* ---------------------------------------------------------------------
   mem()
   ---------------------------------------------------------------------
   Noter les min et max memoire
   ---------------------------------------------------------------------
   E : contexte de la trace
   S :
   --------------------------------------------------------------------- */
static void mem_minmax (sTRACE * pTrace)
{
   ulong mem = (ulong) CORELEFT ();

   if (pTrace->mem_min > mem)
   {
      pTrace->mem_min = mem;
   }

}

/* ---------------------------------------------------------------------
   put()
   ---------------------------------------------------------------------
   Enregister une action
   ---------------------------------------------------------------------
   E : contexte de trace
   E : pointeur
   E : longueur
   E : action
   S : nombre d'elements ou EOF (fin de liste)
   --------------------------------------------------------------------- */
static int put (sTRACE * const pTrace, void const *const p, size_t const len,
                eACT const act, char const *szFile, int line, eTRACE eTrace, int *p_err)
{
   int cr = EOF;
   size_t nb_elem = 0;
   int err = 0;

   if (eTrace)
   {
#if RT_TRACE
      printf (MODULE " '%c %p %6lu %6lu %4d:%s'\n", act, (void const *) p,
              (ulong) len, (ulong) CORELEFT (), line, szFile);
#endif
   }

   if (pTrace->ap != NULL)
   {
      if (pTrace->cpt < pTrace->max)
      {
         switch (act)
         {
         case ACT_FREE:
            {
               /* faire l'appairage */
               sREC *const pAlloc = GetAlloc (pTrace->ap, pTrace->max, p);

               if (pAlloc != NULL)
               {
                  ASSERT (pAlloc->p != NULL);

                  if (pTrace->eTrace)
                  {
                     printf (MODULE " %p matched\n", pAlloc->p);
                  }

                  clr_rec (pAlloc);

                  ASSERT (pTrace->cpt > 0);
                  pTrace->cpt--;
                  nb_elem = pTrace->cpt;
               }
               else
               {
                  /* error */
                  sREC *const pRec = GetFreeRec (pTrace);

                  if (pRec != NULL)
                  {
                     put_rec (pRec, p, len, act, szFile, line);
                     mem_minmax (pTrace);

                     pTrace->cpt++;
                     nb_elem = pTrace->cpt;
                  }
                  else
                  {
                     /* depassement */
                     printf (MODULE " Overload (%lu rec)\n",
                             (ulong) pTrace->max);

                     pTrace->ovl = 1;
                  }

                  if (pTrace->eTrace)
                  {

                     printf (MODULE " %p not-matched\n", (void const *) p);
                     PC_pause ("");
                  }
                  err = 1;
               }
            }
            break;

         default:

            /* find a place */
            {
               /* check */
               sREC *const pFree = GetFree (pTrace->ap, pTrace->max, p);

               if (pFree != NULL)
               {
                  /* free warning */
                  printf (MODULE " Orphan free() ! (%p)\n", p);
               }

               {
                  sREC *const pRec = GetFreeRec (pTrace);

                  if (pRec != NULL)
                  {
                     put_rec (pRec, p, len, act, szFile, line);
                     mem_minmax (pTrace);

                     pTrace->cpt++;
                     nb_elem = pTrace->cpt;
                  }
                  else
                  {
                     /* depassement */
                     printf (MODULE " Overload (%lu rec)\n",
                             (ulong) pTrace->max);
                     pTrace->ovl = 1;
                  }
               }
            }
         }

      }
      else
      {
         /* depassement */
         printf (MODULE " Overload (%lu rec)\n", (ulong) pTrace->max);
         pTrace->ovl = 1;
      }
      /*      *pTrace->szTag=0; *//* detruire le tag */
   }

   /* LCLint */
   if (nb_elem != 0)
   {
      cr = (int) nb_elem;
   }

   if (p_err != NULL)
   {
      *p_err = err;
   }
   return cr;
}

/* ---------------------------------------------------------------------
   clear()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static void clear (sTRACE * const pTrace)
{

   while (-1 != put (pTrace, NULL, 0, -1, MODULE, __LINE__, TRACE_OFF, NULL))
   {
   }
   pTrace->cpt = 0;

}

#endif

/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   sys_malloc()
   ---------------------------------------------------------------------
   Demander un bloc de donnees
   ---------------------------------------------------------------------
   E : taille en octets
   S : adresse ou NULL
   --------------------------------------------------------------------- */
void *sys_malloc (size_t const len, char const *const szFile, int const line)
{
   void *const p = malloc (len);

   if (p != NULL)
   {
#if DBG
      put (&G_Trace, p, len, ACT_MALLOC, szFile, line, G_Trace.eTrace, NULL);
      cnt_inc (&G_Trace.count.allocated);
#else
      (void) szFile;
      (void) line;
#endif
   }
   return p;
}

/* ---------------------------------------------------------------------
   sys_realloc()
   ---------------------------------------------------------------------
   Demander un bloc de donnees
   ---------------------------------------------------------------------
   E : ancien bloc
   E : taille en octets
   S : adresse ou NULL
   --------------------------------------------------------------------- */
void *sys_realloc (void *const pOld, size_t const len,
                   char const *const szFile, int const line)
{
   void *pNew = realloc (pOld, len);

#if DBG
   if (pNew != NULL)
   {
      if (pOld != NULL)
      {
         put (&G_Trace, pOld, len, ACT_FREE, szFile, line, G_Trace.eTrace, NULL);
      }
      else
      {
         cnt_inc (&G_Trace.count.allocated);
      }
      put (&G_Trace, pNew, len, ACT_REALLOC, szFile, line, G_Trace.eTrace, NULL);
   }
#else
   (void) szFile;
   (void) line;
#endif
   return pNew;
}

/* ---------------------------------------------------------------------
   sys_calloc()
   ---------------------------------------------------------------------
   Demander un bloc de donnees
   ---------------------------------------------------------------------
   E : taille en octets
   S : adresse ou NULL
   --------------------------------------------------------------------- */
void *sys_calloc (size_t const len, size_t const nb, char const *const szFile,
                  int const line)
{
   void *const p = calloc (len, nb);

   if (p != NULL)
   {
#if DBG
      put (&G_Trace, p, len * nb, ACT_CALLOC, szFile, line, G_Trace.eTrace, NULL);
      cnt_inc (&G_Trace.count.allocated);
#else
      (void) szFile;
      (void) line;
#endif
   }
   return p;
}

/* ---------------------------------------------------------------------
   sys_free()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E : adresse
   S :
   --------------------------------------------------------------------- */
void sys_free (void *const p, char const *const szFile, int const line)
{
#if DBG
   if (p != NULL)
   {
      int err = 0;

      put (&G_Trace, p, 0, ACT_FREE, szFile, line, G_Trace.eTrace, &err);
      if (!err)
      {
         cnt_dec (&G_Trace.count.allocated);
      }
   }
#else
   (void) szFile;
   (void) line;
#endif

   free (p);
}

/* ---------------------------------------------------------------------
   sys_mem_init()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E : adresse du buffer
   E : longueur du buffer en byte
   E : Flag de trace
   E : Config eventuelle
   S :
   --------------------------------------------------------------------- */
void sys_mem_init (void *buf, size_t len, eTRACE trace, void const *pCfg)
{
#if DBG
   sTRACE *const pTrace = &G_Trace;
   int cr = 0;

   ASSERT (pTrace != NULL);

   pTrace->mem = (ulong) CORELEFT ();

#ifdef __BORLANDC__
   printf (MODULE " %s Ver %X.%X %s %lu %s\n", (char *) COMP_NAME,
           (uint) SYS_high (COMP_VER), (uint) SYS_low (COMP_VER),
           (char *) T_MEM, (ulong) pTrace->mem, (char *) OS);
#endif

   ASSERT (pTrace->ap == NULL);
   if (buf)
   {
      if (len)
      {
         size_t const max = len / sizeof (sREC);

         if (max > 0)
         {
            memset (buf, 0, len);

            pTrace->max = max;
            pTrace->ap = buf;
            strcpy (pTrace->szTag, MODULE);
            clear (pTrace);
            pTrace->mem_min = (ulong) - 1L;

            pTrace->ovl = 0;

            printf (MODULE " Successful initialization: "
                    "%u record%s available\n", (uint) max,
                    ((max > 1) ? "s" : ""));
            cr = 1;
         }

         cnt_init (&pTrace->count.allocated, ULONG_MAX);

      }
      pTrace->eTrace = trace;

      CHECK ();
   }

   if (!cr)
   {
      printf (MODULE " Err: Initialization failure\n");
   }
#else
   (void) buf;
   (void) len;
   (void) trace;
#endif

   (void) pCfg;
}

/* ---------------------------------------------------------------------
   sys_mem_trace()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
void sys_mem_trace (void)
{
#if DBG
   sTRACE const *const pTrace = &G_Trace;
#define LISTE 0

   ASSERT (pTrace != NULL);

   if (pTrace->ap != NULL)
   {
      CHECK ();
#if LISTE
      printf (MODULE " ndx-A : Address   Length Line File\n");
#endif
      {
         size_t lim = pTrace->max;
#if LISTE
         size_t i;
         for (i = 0; i < lim; i++)
         {
            sREC const *const pRec = &pTrace->ap[i];

            ASSERT (pRec != NULL);
            printf (MODULE " %3u-%c : %p %6lu %4lu:%s\n", i, pRec->act,
                    (void *) pRec->p, (ulong) pRec->len, (ulong) pRec->line,
                    pRec->szFile);
         }
#endif
         printf (MODULE " min=%lu max=%lu delta=%lu\n",
                 (ulong) pTrace->mem_min, (ulong) pTrace->mem,
                 (ulong) pTrace->mem - pTrace->mem_min);

         /* afficher les orphelins */
         if (pTrace->ovl)
         {
            printf (MODULE " Overload (no valid trace available)\n");
         }
         else
         {
            list (pTrace->ap, lim);
         }
      }
   }
   else
   {
      printf (MODULE " No trace allocated\n");
   }

   {
      ulong delta = (ulong) (pTrace->mem - CORELEFT ());

      if (delta)
      {
         printf (MODULE " Err: Memory leak (%lu byte%s)\n", (ulong) delta,
                 (delta > 1) ? "s" : "");
      }
      else
      {
         printf (MODULE " Released Memory\n");
      }
   }
#endif

}

/* ---------------------------------------------------------------------
   sys_mem_counters()
   ---------------------------------------------------------------------
   Role : Lister les compteurs d'allocation.

   count.malloc   : 99999 (including count.calloc: 99999)
   count.free     : 99999
   count.allocated: 99999
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
void sys_mem_counters (void)
{
#if DBG
   sTRACE const *const pTrace = &G_Trace;

   if (pTrace)
   {
      count_s const *const p = &pTrace->count;

      printf (MODULE " count.allocated: %lu (MAX %lu)\n",
              (ulong) p->allocated.count, (ulong) p->allocated.mem_max);
   }
#endif
}

/* ---------------------------------------------------------------------
   sys_mem_sver()
   ---------------------------------------------------------------------
   Role : retourner la chaine de caracteres 'version'
   ---------------------------------------------------------------------
   E :
   S : adresse de la chaine litterale 'version'
   --------------------------------------------------------------------- */
char const *sys_mem_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   sys_mem_sid()
   ---------------------------------------------------------------------
   Role : retourner la chaine de caracteres 'identification'
   ---------------------------------------------------------------------
   E :
   S : adresse de la chaine litterale 'identification'
   --------------------------------------------------------------------- */
char const *sys_mem_sid (void)
{
   return ID;
}

/* public data ========================================================= */

/* File generated by 'NEW.EXE' Ver 1.17 (c) ED 1998-99 */
