TinyMAZE/
TinyMAZE/config/
TinyMAZE/doc/
TinyMAZE/run/msgs/
TinyMAZE/src/
TinyMAZE/src/db/
TinyMAZE/src/ident/
TinyMAZE/src/io/
TinyMAZE/src/prog/
TinyMAZE/src/softcode/
TinyMAZE/src/util/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "externs.h"
#include "db.h"

/* The next should be defined as either 1 or 2 */
/* 1 - The stack is only used for temporary allocation. */
/* 2 - The stack is used for temporary and permanent allocation. */
#define STACK_USAGE 1

typedef struct
{
  unsigned long size;
  void *heap;
} STACK;

typedef struct perm_stack
{
  char *file;
  int line;
  time_t time;
  STACK mem;
  struct perm_stack *next;
} PERM_STACK;

typedef struct temp_stack
{
  STACK mem;
  struct temp_stack *next;
} TEMP_STACK;

static PERM_STACK *perm_stack = NULL;
static TEMP_STACK *temp_stack = NULL;
static unsigned long maze_stack_size = 0L;
static unsigned long maze_stack_highest = 0L;

/* The free_stack is designed for efficiency. It allows for the game to */
/* make fewer malloc() calls which are slow. If you set the             */
/* the stack limit too low, you're not using it to it's full potential  */
/* and if you set it too high, some of it will never be used and will   */
/* waste memory. */
static TEMP_STACK *temp_free_stack = NULL;
static PERM_STACK *perm_free_stack = NULL;
static int temp_free_stack_limit = 200;
static int perm_free_stack_limit = 200;
static int temp_free_stack_size = 0;
static int perm_free_stack_size = 0;
static int temp_free_size_highest = 0;
static int perm_free_size_highest = 0;

static TEMP_STACK *get_temp_free_stack(void)
{
  TEMP_STACK *s;

  if(!(s = temp_free_stack))
    s = (TEMP_STACK *)malloc(sizeof(TEMP_STACK));
  else
  {
    temp_free_stack = temp_free_stack->next;
    temp_free_stack_size--;
  }

  return(s);
}

static PERM_STACK *get_perm_free_stack(void)
{
  PERM_STACK *s;

  if(!(s = perm_free_stack))
    s = (PERM_STACK *)malloc(sizeof(PERM_STACK));
  else
  {
    perm_free_stack = perm_free_stack->next;
    perm_free_stack_size--;
  }

  return(s);
}

static void make_temp_free_stack(TEMP_STACK *stack)
{
  if(temp_free_stack_size >= temp_free_stack_limit)
    free(stack);
  else
  {
    stack->next = temp_free_stack;
    temp_free_stack = stack;
    if(++temp_free_stack_size > temp_free_size_highest)
      temp_free_size_highest = temp_free_stack_size;
  }
}

static void make_perm_free_stack(PERM_STACK *stack)
{
  if(perm_free_stack_size >= perm_free_stack_limit)
    free(stack);
  else
  {
    stack->next = perm_free_stack;
    perm_free_stack = stack;
    if(++perm_free_stack_size > perm_free_size_highest)
      perm_free_size_highest = perm_free_stack_size;
  }
}

/* size is the number of bytes to allocate, permanent is either 1 or 0. */
/* 1 indicates that the memory allocated should not be freed by */
/* stack_unalloc(). If clear is 1 the allocated memory will be cleared */
/* (filled with 0s) to simulate a calloc() call. */
void *stack_alloc2(char *file, int line, unsigned long size,
                   bool permanent, bool clear)
{
  STACK *s;
  PERM_STACK *ps;
  TEMP_STACK *ts;

  if(STACK_USAGE == 1 && permanent)
    return(((clear)?calloc(1, size):malloc(size)));

  if(permanent)
  {
    ps = get_perm_free_stack();
    s = &ps->mem;
    ps->file = (char *)malloc(strlen(file)+1);
    strcpy(ps->file, file);
    ps->line = line;
    ps->time = now;
    ps->next = perm_stack;
    perm_stack = ps;
  }
  else
  {
    ts = get_temp_free_stack();
    s = &ts->mem;
    ts->next = temp_stack;
    temp_stack = ts;
  }

  s->heap = (clear)?calloc(1, size):malloc(size);
  s->size = size;

  if((maze_stack_size += size) > maze_stack_highest)
    maze_stack_highest = maze_stack_size;

  return(s->heap);
}

void *stack_realloc2(char *file, int line, void *ptr, unsigned long newsize,
                     bool chktemp)
{
  STACK *s;
  PERM_STACK *ps, *psprev = NULL;
  TEMP_STACK *ts = NULL, *tsprev = NULL;
  unsigned long oldsize;

  if(chktemp)
    for(ts = temp_stack;ts;ts = ts->next)
    {
      if(ts->mem.heap == ptr)
        break;
      tsprev = ts;
    }

  if(!ts && STACK_USAGE == 1)
    return(realloc(ptr, newsize));

  if(!ts)
  {
    for(ps = perm_stack;ps;ps = ps->next)
    {
      if(ps->mem.heap == ptr)
        break;
      psprev = ps;
    }

    if(!ps)
    {
      log_error(tprintf("stack_realloc() %s %d: Attempt to realloc nonexistent pointer. ptr = %p, newsize = %ld",
        file, line, ptr, newsize));
      return(stack_alloc(newsize, 1, 0));
    }
  }

  if(ts)
  {
    oldsize = ts->mem.size;

    if(!newsize)
    {
      if(!tsprev)
        temp_stack = ts->next;
      else
        tsprev->next = ts->next;

      free(ts->mem.heap);
      make_temp_free_stack(ts);
      maze_stack_size -= oldsize;
      return(NULL);
    }

    s = &ts->mem;
  }
  else
  {
    oldsize = ps->mem.size;

    if(!newsize)
    {
      if(psprev)
        psprev->next = ps->next;
      else
        perm_stack = ps->next;

      free(ps->file);
      free(ps->mem.heap);
      make_perm_free_stack(ps);
      maze_stack_size -= oldsize;
      return(NULL);
    }

    s = &ps->mem;
  }

  s->heap = realloc(s->heap, newsize);
  s->size = newsize;

  if((maze_stack_size += newsize-oldsize) > maze_stack_highest)
    maze_stack_highest = maze_stack_size;

  return(s->heap);
}

void stack_free2(char *file, int line, void *ptr, bool chktemp)
{
  PERM_STACK *ps, *psprev = NULL;
  TEMP_STACK *ts = NULL, *tsprev = NULL;

  if(chktemp)
    for(ts = temp_stack;ts;ts = ts->next)
    {
      if(ts->mem.heap == ptr)
        break;
      tsprev = ts;
    }

  if(!ts && STACK_USAGE == 1)
  {
    free(ptr);
    return;
  }

  if(!ts)
  {
    for(ps = perm_stack;ps;ps = ps->next)
    {
      if(ps->mem.heap == ptr)
        break;
      psprev = ps;
    }

    if(!ps)
    {
      log_error(tprintf("stack_free(): %s %d: Attempt to free a nonexistent pointer. ptr = %p",
        file, line, ptr));
      return;
    }
  }

  if(ts)
  {
    if(tsprev)
      tsprev->next = ts->next;
    else
      temp_stack = ts->next;

    maze_stack_size -= ts->mem.size;

    free(ts->mem.heap);
    make_temp_free_stack(ts);
  }
  else
  {
    if(psprev)
      psprev->next = ps->next;
    else
      perm_stack = ps->next;

    maze_stack_size -= ps->mem.size;

    free(ps->file);
    free(ps->mem.heap);
    make_perm_free_stack(ps);
  }
}

void stack_unalloc(void)
{
  TEMP_STACK *ts, *tsnext;

  for(ts = temp_stack;ts;ts = tsnext)
  {
    tsnext = ts->next;

    maze_stack_size -= ts->mem.size;

    free(ts->mem.heap);
    make_temp_free_stack(ts);
  }

  temp_stack = NULL;
}

char *stack_string_alloc2(char *file, int line, char *str, bool permanent)
{
  char *ptr;

  ptr = (char *)stack_alloc2(file, line, strlen(str)+1, permanent, 0);
  strcpy(ptr, str);

  return(ptr);
}

char *stack_string_realloc2(char *file, int line, char *ptr, char *str,
                            bool chktemp)
{
  STACK *s = NULL;
  TEMP_STACK *ts = NULL;
  PERM_STACK *ps;

  if(chktemp)
    for(ts = temp_stack;ts;ts = ts->next)
      if(ts->mem.heap == ptr)
      {
        s = &ts->mem;
        break;
      }

  if(!ts && STACK_USAGE == 1)
  {
    ptr = (char *)realloc(ptr, strlen(str)+1);
    strcpy(ptr, str);
    return(ptr);
  }

  if(!ts)
  {
    for(ps = perm_stack;ps;ps = ps->next)
      if(ps->mem.heap == ptr)
      {
        s = &ps->mem;
        break;
      }

    if(!ps)
    {
      log_error(tprintf("stack_string_realloc() %s %d: Attempt to realloc a nonexistent pointer (ptr = %p)",
        file, line, ptr));
      return(stack_string_alloc(str, 1));
    }
  }

  s->heap = (char *)realloc(s->heap, strlen(str)+1);

  if((maze_stack_size += (strlen(str)+1)-(s->size)) > maze_stack_highest)
    maze_stack_highest = maze_stack_size;

  s->size = strlen(str)+1;
  strcpy(s->heap, str);

  return(s->heap);
}

void info_stack(OBJ *player)
{
  unsigned long size, highest, num_temporary = 0L, num_permanent = 0L;
  unsigned long temp_size = 0L, perm_size = 0L, temp_overhead, perm_overhead;
  unsigned long temp_free_overhead, perm_free_overhead;
  int num_temp_free = 0, num_perm_free = 0;
  TEMP_STACK *ts;
  PERM_STACK *ps;

  size = maze_stack_size;
  highest = maze_stack_highest;

  for(ts = temp_stack;ts;ts = ts->next)
  {
    temp_size += ts->mem.size;
    num_temporary++;
  }
  for(ps = perm_stack;ps;ps = ps->next)
  {
    perm_size += ps->mem.size;
    num_permanent++;
  }
  for(ts = temp_free_stack;ts;ts = ts->next)
    num_temp_free++;
  for(ps = perm_free_stack;ps;ps = ps->next)
    num_perm_free++;

  temp_overhead = (sizeof(TEMP_STACK)+sizeof(STACK))*num_temporary;
  perm_overhead = (sizeof(PERM_STACK)+sizeof(STACK))*num_permanent;
  temp_free_overhead = (sizeof(TEMP_STACK)+sizeof(STACK))*num_temp_free;
  perm_free_overhead = (sizeof(PERM_STACK)+sizeof(STACK))*num_perm_free;

  notify(player, tprintf("|+Y|%s stack status:", config.maze_name));
  notify(player, tprintf("|+B|Stack Size     |+W|: |+C|%s bytes",
    comma(tprintf("%ld", size))));
  notify(player, tprintf("|+B|Stack Peak Size|+W|: |+C|%s bytes\n",
    comma(tprintf("%ld", highest))));

  if(STACK_USAGE == 2)
    notify(player, "|+Y|Temporary Stack|+W|:");

  notify(player,
    tprintf("|+B|Current Stack Allocs|+W|: |+C|%s |+W|(|+C|%s bytes|+W|)",
    comma(tprintf("%ld", num_temporary)),
    comma(tprintf("%ld", temp_size))));
  notify(player,
    tprintf("|+B|Stack Overhead      |+W|: |+C|%s bytes |+W|(|+C|%s total bytes|+W|)",
    comma(tprintf("%d", sizeof(TEMP_STACK)+sizeof(STACK))),
    comma(tprintf("%d", temp_overhead))));
  notify(player,
    tprintf("|+B|Free Links |+W|(|+B|highest|+W|): |+C|%s |+W|(|+C|%s|+W|)",
    comma(tprintf("%d", num_temp_free)),
    comma(tprintf("%d", temp_free_size_highest))));
  notify(player,
    tprintf("|+B|Free Stack Use      |+W|: |+C|%s bytes",
    comma(tprintf("%d", temp_free_overhead))));

  if(STACK_USAGE == 1)
    return;

  notify(player, "\n|+Y|Permanent Stack|+W|:");

  notify(player,
    tprintf("|+B|Current Stack Allocs|+W|: |+C|%s |+W|(|+C|%s bytes|+W|)",
    comma(tprintf("%ld", num_permanent)),
    comma(tprintf("%ld", perm_size))));
  notify(player,
    tprintf("|+B|Stack Overhead      |+W|: |+C|%s bytes |+W|(|+C|%s total bytes|+W|)",
    comma(tprintf("%d", sizeof(PERM_STACK)+sizeof(STACK))),
    comma(tprintf("%d", perm_overhead))));
  notify(player,
    tprintf("|+B|Free Links |+W|(|+B|highest|+W|): |+C|%s |+W|(|+C|%s|+W|)",
    comma(tprintf("%d", num_perm_free)),
    comma(tprintf("%d", perm_free_size_highest))));
  notify(player,
    tprintf("|+B|Free Stack Use      |+W|: |+C|%s bytes",
    comma(tprintf("%d", perm_free_overhead))));
}

void stack_report()
{
  FILE *fp;
  PERM_STACK *ps;
  unsigned long total = 0L, lines = 0L;
  char *b, *p, buf[100];

  if(!(fp = fopen("stack_report", "w")))
    return;

  for(ps = perm_stack;ps;ps = ps->next)
  {
    b = buf;
    for(p = ps->mem.heap;*p && p-(char *)(ps->mem.heap) < 15;p++)
    {
      if(isprint(*p))
        *b++ = *p;
      else
        if(b-buf)
          *b++ = '.';
        else
          break;
    }
    *b = '\0';
    fprintf(fp, "%s %s %d: %ld bytes: %s\n",
      mktm(ps->time, "D", root_obj), ps->file, ps->line, ps->mem.size, buf);
    total += ps->mem.size;
    lines++;
  }

  fprintf(fp, "-----\n%ld total bytes left unallocated.\n", total);
  fprintf(fp, "%ld total allocations not freed.\n", lines);

  fclose(fp);
}