/*@z31.c:Memory Allocator:DebugMemory()@**************************************/
/*                                                                           */
/*  THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.43)                       */
/*  COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston                             */
/*                                                                           */
/*  Jeffrey H. Kingston (jeff@it.usyd.edu.au)                                */
/*  School of Information Technologies                                       */
/*  The University of Sydney 2006                                            */
/*  AUSTRALIA                                                                */
/*                                                                           */
/*  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 3, 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, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA   */
/*                                                                           */
/*  FILE:         z31.c                                                      */
/*  MODULE:       Memory Allocator                                           */
/*  EXTERNS:      DebugMemory(), zz_free[], MemInit(), GetMemory()           */
/*                                                                           */
/*****************************************************************************/
#include "externs.h"

#define	MEM_CHUNK	1020		/* how many ALIGNs to get from sys   */


#if DEBUG_ON
static	int	no_of_calls	= 0;	/* number of calls to calloc()       */
static  int     recs_created    = 0;	/* number of records created         */
static  int     bytes_created   = 0;	/* number of bytes in created recs   */
	int	zz_newcount	= 0;	/* number of calls to New()          */
	int	zz_disposecount	= 0;	/* number of calls to Dispose()      */
	int	zz_listcount	= 0;	/* number of elements in zz_free[]   */

static	int	usage_nums[MEM_USAGE_MAX] = { 0 };
static	int	usage_bytes[MEM_USAGE_MAX] = { 0 };
static	int	max_usage_bytes[MEM_USAGE_MAX] = { 0 };
static	int	curr_total_bytes, max_total_bytes = 0;
static	char	*usage_strings[] = {
			"lout binary",
			"object memory chunks",
			"font tables",
			"lex buffers",
			"file tables",
			"cross reference tables",
			"plain text output grids",
			"database check tables",
			"in_memory database",
			"hyphenation pattern tables",
			"character mappings",
			"colour tables",
			"texture tables",
			"language tables",
};

/*****************************************************************************/
/*                                                                           */
/*  DebugRegisterUsage(typ, delta_num, delta_size)                           */
/*                                                                           */
/*  Register a change in the number of things of type typ that               */
/*  have been allocated memory, and the change in the number of bytes.       */
/*                                                                           */
/*****************************************************************************/

void DebugRegisterUsage(int typ, int delta_num, int delta_size)
{ int i;
  assert(0 <= typ && typ < MEM_USAGE_MAX, "DebugRegisterUsage!");
  usage_nums[typ] += delta_num;
  usage_bytes[typ] += delta_size;
  curr_total_bytes += delta_size;
  if( curr_total_bytes > max_total_bytes )
  { max_total_bytes = curr_total_bytes;
    for( i = 0; i < MEM_USAGE_MAX;  i++ )
      max_usage_bytes[i] = usage_bytes[i];
  }
} /* end DebugRegisterUsage */


/*****************************************************************************/
/*                                                                           */
/*  DebugMemory()                                                            */
/*                                                                           */
/*  Print memory usage.                                                      */
/*                                                                           */
/*****************************************************************************/

void DebugMemory(void)
{ int i, j;  OBJECT p;  int recs_free, bytes_free;

  recs_free = bytes_free = 0;
  for( i = 0;  i < MAX_OBJECT_REC;  i++ )
  { if( zz_free[i] != nilobj )
    { j = 0;
      for( p = zz_free[i];  p != nilobj;  p = pred(p, CHILD) )  j++;
      debug3(DMA, DD, "zz_free[%2d]: %5d (%d bytes)", i, j,
	i * j * sizeof(ALIGN));
      recs_free += j;
      bytes_free += i* j * sizeof(ALIGN);
    }
  }

  debug4(DMA, D, "%-35s %8s %8s %8s",
    "Summary of malloc() memory usage", "Quantity", "Bytes", "At max.");

  for( i = 1;  i < MEM_USAGE_MAX;  i++ )
  {
    debug4(DMA, D, "%-35s %8d %8d %8d", usage_strings[i], usage_nums[i],
      usage_bytes[i], max_usage_bytes[i]);
  }
  debug4(DMA, D, "%-35s %8s %8s %8s", "", "", "--------", "--------");
  debug4(DMA, D, "%-35s %8s %8d %8d", "","",curr_total_bytes,max_total_bytes);


  /***
  debug3(DMA, D, "%-12s %8s %8s", "", "records", "bytes");
  debug4(DMA, D, "%-12s %8s %8d (%d calls)", "calloc", "-",
    no_of_calls * MEM_CHUNK * sizeof(ALIGN), no_of_calls);
  debug3(DMA, D, "%-12s %8d %8d", "created", recs_created, bytes_created);
  debug3(DMA, D, "%-12s %8d %8d", "free (count)",    recs_free,    bytes_free);
  debug3(DMA, D, "%-12s %8d %8s", "free (var)",      zz_listcount, "-");
  debug3(DMA, D, "%-12s %8d %8s", "new-dispose",
    zz_newcount - zz_disposecount, "-");
  debug3(DMA, D, "%-12s %8d %8s", "created-free",
    recs_created - recs_free, "-");
  debug2(DMA, D, "newcount %d, disposecount %d", zz_newcount, zz_disposecount);
  *** */

  debug0(DMA, D, "");

} /* end DebugMemory */
#endif


/*@::zz_free[], zz_lengths[], MemInit()@**************************************/
/*                                                                           */
/*  OBJECT         zz_free[], zz_hold, zz_tmp, zz_res                        */
/*  int            zz_size                                                   */
/*  unsigned char  zz_lengths[]                                              */
/*                                                                           */
/*  zz_free[i]:    free records of size i*sizeof(ALIGN).                     */
/*  zz_lengths[i]: the number of ALIGNs in a record of type i.               */
/*  These variables are used only within the New() and Dispose() macros,     */
/*  and the list handling macros.                                            */
/*                                                                           */
/*****************************************************************************/

OBJECT		zz_free[MAX_OBJECT_REC], zz_hold, zz_tmp, zz_res;
int		zz_size;
unsigned char	zz_lengths[DISPOSED];		/* DISPOSED is 1 + max type */
OBJECT 		xx_link, xx_tmp, xx_res, xx_hold;


/*****************************************************************************/
/*                                                                           */
/*  MemInit()                                                                */
/*                                                                           */
/*  Initialise memory allocator.                                             */
/*                                                                           */
/*****************************************************************************/

void MemInit(void)
{
  zz_lengths[ WORD         ] = 0;
  zz_lengths[ QWORD        ] = 0;
  zz_lengths[ LINK         ] = ceiling(sizeof(struct link_type), sizeof(ALIGN));

  /* object types, except closure NB have actual() field in token phase! */
  zz_lengths[ CLOSURE      ] =
  zz_lengths[ NULL_CLOS    ] =
  zz_lengths[ PAGE_LABEL   ] =
  zz_lengths[ UNDER_REC    ] =
  zz_lengths[ CROSS        ] =
  zz_lengths[ FORCE_CROSS  ] =
  zz_lengths[ SPLIT        ] =
  zz_lengths[ PAR          ] =
  zz_lengths[ ROW_THR      ] =
  zz_lengths[ COL_THR      ] =
  zz_lengths[ HSPANNER     ] =
  zz_lengths[ VSPANNER     ] =
  zz_lengths[ ACAT         ] =
  zz_lengths[ HCAT         ] =
  zz_lengths[ VCAT         ] =
  zz_lengths[ BEGIN_HEADER ] =
  zz_lengths[ END_HEADER   ] =
  zz_lengths[ SET_HEADER   ] =
  zz_lengths[ CLEAR_HEADER ] =
  zz_lengths[ ONE_COL      ] =
  zz_lengths[ ONE_ROW      ] =
  zz_lengths[ WIDE         ] =
  zz_lengths[ HIGH         ] =
  zz_lengths[ HSHIFT       ] =
  zz_lengths[ VSHIFT       ] =
  zz_lengths[ HMIRROR      ] =
  zz_lengths[ VMIRROR      ] =
  zz_lengths[ HSCALE       ] =
  zz_lengths[ VSCALE       ] =
  zz_lengths[ HCOVER       ] =
  zz_lengths[ VCOVER       ] =
  zz_lengths[ SCALE        ] =
  zz_lengths[ KERN_SHRINK  ] =
  zz_lengths[ HCONTRACT    ] =
  zz_lengths[ VCONTRACT    ] =
  zz_lengths[ HLIMITED     ] =
  zz_lengths[ VLIMITED     ] =
  zz_lengths[ HEXPAND      ] =
  zz_lengths[ VEXPAND      ] =
  zz_lengths[ START_HVSPAN ] =
  zz_lengths[ START_HSPAN  ] =
  zz_lengths[ START_VSPAN  ] =
  zz_lengths[ HSPAN        ] =
  zz_lengths[ VSPAN        ] =
  zz_lengths[ PADJUST      ] =
  zz_lengths[ HADJUST      ] =
  zz_lengths[ VADJUST      ] =
  zz_lengths[ ROTATE       ] =
  zz_lengths[ BACKGROUND   ] =
  zz_lengths[ VERBATIM     ] =
  zz_lengths[ RAW_VERBATIM ] =
  zz_lengths[ CASE         ] =
  zz_lengths[ YIELD        ] =
  zz_lengths[ BACKEND      ] =
  zz_lengths[ FILTERED     ] =
  zz_lengths[ XCHAR        ] =
  zz_lengths[ FONT         ] =
  zz_lengths[ SPACE        ] =
  zz_lengths[ YUNIT        ] =
  zz_lengths[ ZUNIT        ] =
  zz_lengths[ BREAK        ] =
  zz_lengths[ UNDERLINE    ] =
  zz_lengths[ UNDERLINE_COLOUR ] =
  zz_lengths[ COLOUR       ] =
  zz_lengths[ TEXTURE      ] =
  zz_lengths[ OUTLINE      ] =
  zz_lengths[ LANGUAGE     ] =
  zz_lengths[ CURR_LANG    ] =
  zz_lengths[ CURR_FAMILY  ] =
  zz_lengths[ CURR_FACE    ] =
  zz_lengths[ CURR_YUNIT   ] =
  zz_lengths[ CURR_ZUNIT   ] =
  zz_lengths[ GET_CONTEXT  ] =
  zz_lengths[ SET_CONTEXT  ] =
  zz_lengths[ COMMON       ] =
  zz_lengths[ RUMP         ] =
  zz_lengths[ MELD         ] =
  zz_lengths[ INSERT       ] =
  zz_lengths[ ONE_OF       ] =
  zz_lengths[ NEXT         ] =
  zz_lengths[ PLUS         ] =
  zz_lengths[ MINUS        ] =
  zz_lengths[ ENV_OBJ      ] =
  zz_lengths[ ENV          ] =
  zz_lengths[ ENVA         ] =
  zz_lengths[ ENVB         ] =
  zz_lengths[ ENVC         ] =
  zz_lengths[ ENVD         ] =
  zz_lengths[ CENV         ] =
  zz_lengths[ CLOS         ] =
  zz_lengths[ LVIS         ] =
  zz_lengths[ LUSE         ] =
  zz_lengths[ LEO          ] =
  zz_lengths[ OPEN         ] =
  zz_lengths[ TAGGED       ] =
  zz_lengths[ INCGRAPHIC   ] =
  zz_lengths[ SINCGRAPHIC  ] =
  zz_lengths[ PLAIN_GRAPHIC] =
  zz_lengths[ GRAPHIC      ] =
  zz_lengths[ LINK_SOURCE  ] =
  zz_lengths[ LINK_DEST    ] =
  zz_lengths[ LINK_DEST_NULL] =
  zz_lengths[ LINK_URL     ] =
	ceiling(sizeof(struct closure_type), sizeof(ALIGN));

  zz_lengths[ HEAD         ] =
	ceiling(sizeof(struct head_type), sizeof(ALIGN));

  zz_lengths[ LBR          ] =
  zz_lengths[ RBR          ] =
  zz_lengths[ BEGIN        ] =
  zz_lengths[ END          ] =
  zz_lengths[ USE          ] =
  zz_lengths[ NOT_REVEALED ] =
  zz_lengths[ GSTUB_NONE   ] =
  zz_lengths[ GSTUB_INT    ] =
  zz_lengths[ GSTUB_EXT    ] =
  zz_lengths[ UNEXPECTED_EOF] =
  zz_lengths[ INCG_REPEATED ] =
  zz_lengths[ SINCG_REPEATED] =
  zz_lengths[ PREPEND      ] =
  zz_lengths[ SYS_PREPEND  ] =
  zz_lengths[ DATABASE     ] =
  zz_lengths[ SYS_DATABASE ] =
  zz_lengths[ DEAD         ] =
  zz_lengths[ UNATTACHED   ] =
  zz_lengths[ RECEPTIVE    ] =
  zz_lengths[ RECEIVING    ] =
  zz_lengths[ RECURSIVE    ] =
  zz_lengths[ PRECEDES     ] =
  zz_lengths[ FOLLOWS      ] =
  zz_lengths[ CROSS_FOLL   ] =
  zz_lengths[ CROSS_FOLL_OR_PREC] =
  zz_lengths[ GALL_FOLL    ] =
  zz_lengths[ GALL_FOLL_OR_PREC ] =
  zz_lengths[ CROSS_TARG   ] =
  zz_lengths[ GALL_TARG    ] =
  zz_lengths[ GALL_PREC    ] =
  zz_lengths[ CROSS_PREC   ] =
  zz_lengths[ PAGE_LABEL_IND] =
  zz_lengths[ SCALE_IND    ] =
  zz_lengths[ COVER_IND    ] =
  zz_lengths[ EXPAND_IND   ] =
  zz_lengths[ THREAD       ] =
  zz_lengths[ CR_LIST      ] =
  zz_lengths[ SCOPE_SNAPSHOT] =
	ceiling(sizeof(struct closure_type), sizeof(ALIGN));

  /* symbol types */
  zz_lengths[ MACRO        ] =
  zz_lengths[ LOCAL        ] =
  zz_lengths[ LPAR         ] =
  zz_lengths[ RPAR         ] =
  zz_lengths[ NPAR         ] =
	ceiling(sizeof(struct symbol_type), sizeof(ALIGN));

  /* gap objects */
  zz_lengths[ TSPACE       ] =
  zz_lengths[ TJUXTA       ] =
  zz_lengths[ GAP_OBJ      ] =
	ceiling(sizeof(struct gapobj_type), sizeof(ALIGN));

  /* cross-reference and data base types */
  zz_lengths[ CROSS_SYM    ] =
  zz_lengths[ CR_ROOT      ] = ceiling(sizeof(struct cr_type) , sizeof(ALIGN));

  /* external galley record */
  zz_lengths[ EXT_GALL  ] = ceiling(sizeof(struct ext_gall_type),sizeof(ALIGN));

} /* end MemInit() */


/*@::GetMemory()@*************************************************************/
/*                                                                           */
/*  OBJECT GetMemory(siz, pos)                                               */
/*                                                                           */
/*  Return a pointer to siz ALIGNs of memory (0 < siz < MAX_OBJECT_REC).     */
/*                                                                           */
/*****************************************************************************/

OBJECT GetMemory(int siz, FILE_POS *pos)
{ static ALIGN *next_free = (ALIGN *) nilobj;
  static ALIGN *top_free  = (ALIGN *) nilobj;
  OBJECT res;

  debug1(DMA, DDD, "GetMemory( %d )", siz);

#if USE_SYSTEM_MALLOC

#if DEBUG_ON
  DebugRegisterUsage(MEM_OBJECTS, 1, siz * sizeof(ALIGN));
#endif

#if USE_MALLOC_DEBUG

  /* malloc + debugging */

  next_free = (ALIGN *) calloc(siz + MALLOC_HEADER_SIZE + MALLOC_TRAILER_SIZE, sizeof(ALIGN));
  if (next_free != NULL)
  { next_free += MALLOC_HEADER_SIZE;
    malloc_otype(next_free) = -1;
    malloc_orec_size(next_free) = siz;
    malloc_osentinel(next_free) = MALLOC_SENTINEL;
  }
#else

  /* malloc, no debugging */

  next_free = (ALIGN *) calloc(siz, sizeof(ALIGN));

#endif

  /* malloc, generic */

  if( next_free == NULL )
    Error(31, 1, "exiting now (run out of memory)", FATAL, pos);
  ifdebug(DMA, D, no_of_calls++; )

  res = (OBJECT) next_free;

#else

#if USE_MALLOC_DEBUG

  /* built-in + debugging */

  /* get memory from operating system, if not enough left here */
  if( &next_free[ siz + MALLOC_HEADER_SIZE + MALLOC_TRAILER_SIZE ] > top_free )
  {
#if DEBUG_ON
    DebugRegisterUsage(MEM_OBJECTS, 1, MEM_CHUNK * sizeof(ALIGN));
#endif
    next_free = (ALIGN *) calloc(MEM_CHUNK, sizeof(ALIGN));
    ifdebug(DMA, D, no_of_calls++; )
    if( next_free == NULL )
      Error(31, 1, "exiting now (run out of memory)", FATAL, pos);
    top_free = &next_free[MEM_CHUNK];
    debug2(DMA, DD, "GetMemory: calloc returned %ld - %ld",
      (long) next_free, (long) top_free);
  }

  res = (OBJECT) (&next_free[MALLOC_HEADER_SIZE]);

  malloc_otype(res) = -1;
  malloc_orec_size(res) = siz;
  malloc_osentinel(res) = MALLOC_SENTINEL;

  next_free = &next_free[ siz + MALLOC_HEADER_SIZE + MALLOC_TRAILER_SIZE ];

#else

  /* built-in, no debugging */

  /* get memory from operating system, if not enough left here */
  if( &next_free[siz] > top_free )
  {
#if DEBUG_ON
    DebugRegisterUsage(MEM_OBJECTS, 1, MEM_CHUNK * sizeof(ALIGN));
#endif
    next_free = (ALIGN *) calloc(MEM_CHUNK, sizeof(ALIGN));
    ifdebug(DMA, D, no_of_calls++; )
    if( next_free == NULL )
      Error(31, 1, "exiting now (run out of memory)", FATAL, pos);
    top_free = &next_free[MEM_CHUNK];
    debug2(DMA, DD, "GetMemory: calloc returned %ld - %ld",
      (long) next_free, (long) top_free);
  }

  res = (OBJECT) next_free;
  next_free = &next_free[siz];

#endif
#endif

  /* Uncomment the next line to test that objects initialize themselves correctly. */
  /* Objects that are reused from the free list are not initialized. */
  /* This memset simulates that and makes problems happen sooner. */
  /* memset(res, 0xFF, siz * sizeof(ALIGN)); */

#if DEBUG_ON
  recs_created++; bytes_created += siz * sizeof(ALIGN);
#endif
  debug3(DMA, DDD, "GetMemory returning @%ld (next_free = @%ld, top_free = @%ld",
    (long) res, (long) next_free, (long) top_free);
  return res;
} /* end GetMemory */
