static char *SccsId = "@(#)new.c 4.7 (TU-Delft) 04/26/93";
/**********************************************************

Name/Version      : aux/4.7

Language          : C
Operating system  : UNIX SYSTEM V
Host machine      : HP9000

Authors           : A.J. van Genderen
                  : N.P. van der Meijs
Creation date     : 15-Mar-1988
Modified by       :
Modification date :


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 786234

        COPYRIGHT (C) 1988. All rights reserved.
**********************************************************/
#define MSTAT		1 /* should read config.h */

#include <stdio.h>
#include "aux.h"

#ifdef MSTAT
#define	Max(a,b)	((a) > (b) ? (a) : (b))

static int  start;
static int  allocated,
	    freed,
	    maxOccupied,
	    remaining,
	    allocs,
	    frees,
	    nowPending,
	    maxPending;

static int profile = 0;
static FILE * fpmap;
static FILE * fpdat;

static char * File;
static int    Line;

struct map {
    char * file;
    int  line;
    int mapnumber;
    struct map * next;
};

struct dat {
    int mapnumber;
    int amount;
};

# define MAPSIZE 1000

static struct map * maptab[MAPSIZE];

private char * s_malloc  ();
private char * s_realloc ();
private        s_free    ();
private struct map * lookup ();
private struct map * install ();
private int hash ();
static int statistics = 0;

#endif /* MSTAT */

char * new_p (size, num, file, line)
unsigned    size, num;
char * file;
int line;
{
    char * p;
    File = file;
    Line = line;
    p = new (size, num);
    File = NULL;
    Line = 0;
    return (p);
}

char * new (size, num)
unsigned    size, num;
{
    char * malloc ();
    char * sbrk ();
    char * p;
    extern int end;

    size *= num;

#ifdef MSTAT
    if (statistics) {
        if (!(p = s_malloc (size, File, Line))) {
	    say ("No more core.");
	    say ("Already allocated %d bytes (bruto %d), cannot get %d more.",
		allocated - freed, (int) ((int *) sbrk (0) - &end), size);
	    die ();
	}
    }
    else
#endif /* MSTAT */

    if (!(p = malloc (size))) {
	say ("No more core.");
	say ("Already allocated %d bytes, cannot get %d more.",
	    (int) ((int *) sbrk (0) - &end), size);
	die ();
    }


    return (p);
}

dispose (p)
char * p;
{
#   ifdef MSTAT
    if (statistics)
	s_free (p);
    else
#   endif /* MSTAT */
	free (p);
}

char * resize_p (old, size, num, file, line)
char * old;
unsigned size, num;
char * file;
int line;
{
    char * p;
    File = file;
    Line = line;
    p = resize (old, size, num);
    File = NULL;
    Line = 0;
    return (p);
}

char * resize (old, size, num)
char * old;
unsigned size, num;
{
    char * realloc ();
    char * sbrk ();
    char * p;
    extern int end;

    if (!old) {       
	/* on some machines (sun), realloc does not handle realloc(null, ..) */
	return (new (size, num));
    }

    size *= num;

#ifdef MSTAT
    if (statistics) {
	if (!(p = s_realloc (old, size, File, Line))) {
	    say ("No more core.");
	    say ("Already allocated %d bytes (bruto %d), can't get another %d.",
		allocated - freed, (int) ((int *) sbrk (0) - &end), size);
	    die ();
	}
    }
    else
#endif /* MSTAT */

    if (!(p = realloc (old, size))) {
	say ("No more core.");
	say ("Already allocated %d bytes, cannot get %d more.",
	    (int) ((int *) sbrk (0) - &end), size);
	die ();
    }

    return (p);
}



#ifdef MSTAT

/*
 * Alignment boundary of structures.
 * We increase the size of the allocation
 * in order to do administration.
 * ALIGN should be big enough to 'store' an int
 */

#if gould || hp9000s800
#define ALIGN (sizeof (double))
#else
#define ALIGN (sizeof (int))
#endif

private char * s_malloc (amount, file, line)
unsigned    amount;
char * file;
int line;
{
    char * new, * malloc ();
    unsigned extra = ALIGN + ALIGN;

    if (new = malloc (amount + extra)) {
	*((int *) new) = (int) amount;

        if (profile)
            *(int *) (new + ALIGN) = memprof ((int) (amount), file, line);
        else
            *(int *) (new + ALIGN) = 0;

	allocated += amount;
	maxOccupied = Max (allocated - freed, maxOccupied);
	allocs++;
	maxPending = Max (allocs - frees, maxPending);
	new += extra;
    }
    return (new);
}

static char * s_realloc (old, amount, file, line)
char * old;
unsigned  amount;
char * file;
int line;
{
    char * new, * realloc ();
    int extra = ALIGN + ALIGN;
    int old_amount;

    if (old) {
	old -= extra;
	freed += (old_amount = *((int *) old));
        if (profile) {
            memproffree (old_amount, (int) *(int *) (old + ALIGN));
        }
	frees++;
    }

    if (new = realloc (old, amount + extra)) {
	*((int *) new) = (int) amount;

        if (profile)
            *(int *) (new + ALIGN) = memprof ((int) (amount), file, line);
        else
            *(int *) (new + ALIGN) = 0;

	allocated  += amount;
	maxOccupied = Max (allocated - freed, maxOccupied);
	allocs++;
	maxPending = Max (allocs - frees, maxPending);
	new += extra;
    }
    return (new);
}

private s_free (p, file, line)
char * p;
char * file;
int line;
{
    int extra = ALIGN + ALIGN;
    int amount;
    if (p) {
        p -= extra;
        freed += (amount = *((int *) p));
        if (profile) {
            memproffree (amount, (int) *(int *) (p + ALIGN));
        }
        frees++;
        free (p);
    }
    else say ("cannot free null pointer"), die ();

    /* if (profile) memprof (0 - (int) amount, file, line); */
}

/* VARARGS1 */
mstat (cmd, fp)
int cmd;
FILE * fp;
{
    int mstatlevel;
    char * sbrk ();

    /*
     * environment var MSTATLEVEL:
     * 0: simple counting, no malloc overhead
     * 1: more details, incurs increased malloc blocks.
     * 2: memory profiling
     */

    switch (cmd) {
       case M_INIT:
	  start = (int) sbrk (0);
	  mstatlevel = getenv ("MSTATLEVEL") ? atoi (getenv ("MSTATLEVEL")) : 0;
	  if (mstatlevel >= 1) statistics = 1;
	  if (mstatlevel >= 2) profile = 1;
	  break;

       case M_PRINT: printInfo (fp); break;
       default: say ("mstat: illegal cmd %d", cmd); break;
    }
}

static printInfo (fp)
FILE * fp;
{
    char * sbrk ();
    remaining  += allocated - freed;
    nowPending += allocs - frees;
    fprintf (fp, "\n");
    fprintf (fp, "\tbruto allocation : %7d\n", (int) sbrk (0) - start);
    if (statistics) {
	fprintf (fp, "\tbytes allocated  : %7d\n", allocated);
	fprintf (fp, "\tbytes freed      : %7d\n", freed    );
	fprintf (fp, "\tmax occupied     : %7d\n", maxOccupied);
	fprintf (fp, "\tnow occupied     : %7d\n", remaining);
	fprintf (fp, "\tallocs           : %7d\n", allocs   );
	fprintf (fp, "\tfrees            : %7d\n", frees    );
	fprintf (fp, "\tmax pending      : %7d\n", maxPending);
	fprintf (fp, "\tnow pending      : %7d\n", nowPending);
    }
    fprintf (fp, "\n");
    maxOccupied = remaining, allocated = freed = 0;
    maxPending = nowPending, allocs = frees = 0;
}


private int memprof (amount, file, line)
int amount;
char * file;
int line;
{
    static int init = 0;
    static int mapnumber = 0;
    struct map item;
    struct map * found;
    struct dat dat;

    if (init == 0) {
	mapnumber = 0;
	fpmap = cfopen ("malloc.map", "w");
	fpdat = cfopen ("malloc.dat", "w");
	init++;
    }

    if (file == (char *) NULL) {
	if (1 == 0) {
	    file = NULL;
	}
    }

    item.file  = file ? file : "<unknown>";
    item.line  = line;
    if ((found = lookup (item)) == NULL) {
	item.mapnumber = mapnumber++;
	fprintf (fpmap, "%s %d\n", item.file, item.line);
	fflush (fpmap);
	found = install (item);
    }
    if (found) {
	dat.mapnumber = found -> mapnumber;
	dat.amount   = amount;
	fwrite (&dat, sizeof (dat), 1, fpdat);
    }

    /*
    fprintf (stderr, "memprof %d %d\n", amount, found -> mapnumber);
    */
    return (found -> mapnumber);
}

memproffree (amount, mapnumber)
int amount;
int mapnumber;
{
    struct dat dat;
    /* fprintf (stderr, "free %d %d\n", amount, mapnumber); */
    dat.mapnumber = mapnumber;
    dat.amount    = 0 - amount;
    fwrite (&dat, sizeof (dat), 1, fpdat);
}

private struct map * lookup (item)
struct map item;
{
    struct map * mp;
    for (mp = maptab [hash (item.file, item.line)];
	mp != NULL; mp = mp -> next) {
	if (item.line == mp -> line
        && (strcmp (item.file, mp -> file) == NULL)) {
	    return (mp); /* found */
	}
    }

    return (NULL);	/* not found */
}

private struct map * install (item)
struct map item;
{
    int hashval;
    struct map * new;

    new = (struct map *) malloc (sizeof (struct map));
    if (new == NULL)
	return (NULL);

    new -> file = malloc ((unsigned) strlen (item.file) + 1);
    if (new -> file == NULL)
	return (NULL);

    new -> mapnumber = item.mapnumber;
    strcpy (new -> file, item.file);
    new -> line = item.line;

    hashval = hash (new -> file, new -> line);
    new -> next = maptab[hashval];
    maptab[hashval] = new;

    return (new);
}

/*  see the "Dragon" book by Aho, Sethi and Ullman */
private int hash (file, line)
char * file;
int line;
{
    unsigned long h = 0;
    unsigned long g;
    char * s = file;

    while (*s) {
	h = (h << 4) + *s++;
	if (g = (h & 0xf0000000)) {
	    h ^= (g >> 24);
	    h ^= g;
	}
    }

    h = (h + line) % MAPSIZE;
    return (h);
}
# endif /* MSTAT */


# ifdef DRIVER
main () {
    struct x {
	int i;
	char c;
	double d;
    } * p;
    static struct x y = {
	1,
	'a',
	123.456
    };
    struct x *q;
    mstat (M_INIT, 0);
    p = NEW (struct x, 100);
    p = NEW (struct x, mallocFit (sizeof (struct x), 100));
    p = NEW (struct x, 1);
    q = NEW (struct x, 2);
    p = RESIZE (p, struct x, 2000);
    (void) strsave ("pietje puk");
    memcpy (p, &y, sizeof (struct x));
    printf ("i: %d, c: %c, d: %g\n", p -> i, p -> c, p -> d);
    DISPOSE (p);
    mstat (M_PRINT, stdout);
}
# endif /* DRIVER */
