/
driver3.2@242/autoconf/
driver3.2@242/doc/LPC/
driver3.2@242/hosts/
driver3.2@242/hosts/amiga/NetIncl/
driver3.2@242/hosts/amiga/NetIncl/netinet/
driver3.2@242/hosts/amiga/NetIncl/sys/
driver3.2@242/hosts/atari/
driver3.2@242/hosts/fcrypt/
driver3.2@242/mudlib/
driver3.2@242/mudlib/sys/
driver3.2@242/util/
driver3.2@242/util/indent/hosts/next/
driver3.2@242/util/make_docs/
#include <stdio.h>

#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "exec.h"
#define USES_SVALUE_STRLEN
#include "smalloc.h"
#include "lang.h"
#include "instrs.h"

#if defined(AMIGA)
#include "hosts/amiga/ixfile.h"
#endif

/*
 * Write statistics about objects on file.
 */

#ifdef IMPROVED_DUMPSTAT

/* Quick and dirty referenced-table management.
** The table in object.c is unfortunately single threaded.
*/

struct trecord {
  struct trecord *next;
  void *item;
};

static struct trecord *global_table[256];

static void init_gtable()
{
  int i;
  for (i = 256; i--;)
    global_table[i] = NULL;
}

static void free_gtable()
{
  int i;
  struct trecord *ptr;
  for (i = 256; i--;)
    while ((ptr = global_table[i]) != NULL)
    {
      global_table[i] = ptr->next;
      xfree(ptr);
    }
}

static int is_refd (void *item)
{
  int key;
  long pointer;
  struct trecord *this;

  pointer = (long)item;
  key = pointer ^ pointer >> 16;
  key ^= key >> 8;
  key &= 0xff;

  this = global_table[key];
  while (this != NULL)
  {
    if (this->item == item)
      return 1;
    this = this->next;
  }
  this = xalloc(sizeof (struct trecord));
  if (!this) return 0;
  this->item = item;
  this->next = global_table[key];
  global_table[key] = this;
  return 0;
}

#define SETTOTAL(x)   *rtotal = (x);
#define INCTOTAL(x)   *rtotal += (x);
#define REFTOTAL(i,x) if (!is_refd((void *)(i))) *rtotal = (x);
#define REFITOTAL(i,x) if (!is_refd((void *)(i))) *rtotal += (x);

#else
#define SETTOTAL(x)
#define INCTOTAL(x)
#define REFTOTAL(i,x)
#define REFITOTAL(i,x)
#endif

#ifdef MAPPINGS
#ifdef IMPROVED_DUMPSTAT
static int svalue_size PROT((struct svalue *, int *));
#else
static int svalue_size PROT((struct svalue *));
#endif

struct svalue_size_locals {
    mp_int total;
#ifdef IMPROVED_DUMPSTAT
    mp_int rtotal;
#endif
    int num_values;
};

void svalue_size_map_filter(key, values, extra)
    struct svalue *key;
    struct svalue *values;
    char *extra;
{
    struct svalue_size_locals *locals;
    int i;

    locals = (struct svalue_size_locals*)extra;
#ifdef IMPROVED_DUMPSTAT
    { int tmp;
      locals->total += svalue_size(key, &tmp);
      locals->rtotal += tmp;
      for(i = locals->num_values; --i >= 0; ) {
	  locals->total += svalue_size(values++, &tmp) + sizeof(struct svalue);
	  locals->rtotal += tmp + sizeof(struct svalue);
      }
    }
#else
    locals->total += svalue_size(key);
    for(i = locals->num_values; --i >= 0; ) {
	locals->total += svalue_size(values++) + sizeof(struct svalue);
    }
#endif
}
#endif

extern struct object *obj_list;

#ifndef IMPROVED_DUMPSTAT
static int svalue_size(v)
    struct svalue *v;
#else
static int svalue_size(v, rtotal)
    struct svalue *v;
    int *rtotal;
#endif
{
    mp_int i, total;

    SETTOTAL(0)
    switch(v->type) {
    case T_OBJECT:
    case T_NUMBER:
#ifdef FLOATS
    case T_FLOAT:
#endif
	return 0;
    case T_STRING:
	/* Include some malloc overhead. */
	if (v->x.string_type == STRING_SHARED)
        {
            REFTOTAL(v->u.string, strlen(v->u.string) + sizeof(short) + 3*sizeof(char*) & ~(sizeof(char *) - 1))
 	    return strlen(v->u.string) + sizeof(short) + 3*sizeof(char*) &
	      ~(sizeof(char *) - 1);
	}
	SETTOTAL(svalue_strlen(v) + 2*sizeof(char *) & ~(sizeof(char *) - 1))
	return svalue_strlen(v) + 2*sizeof(char *) & ~(sizeof(char *) - 1);
    case T_SYMBOL:
        REFTOTAL(v->u.string, strlen(v->u.string) + sizeof(short) + 3*sizeof(char*) & ~(sizeof(char *) - 1))
	return strlen(v->u.string) + sizeof(short) + 3*sizeof(char*) &
	  ~(sizeof(char *) - 1);
#ifdef MAPPINGS
    case T_MAPPING:
    {
	extern int register_pointer PROT((char *));
	extern void walk_mapping PROT((
		struct mapping *,
		void (*)(struct svalue *, struct svalue *, char *),
		char *
	));

	struct svalue_size_locals locals;
	struct condensed_mapping *cm;

	if (register_pointer( (char *)(v->u.map) ) ) return 0;

	cm = v->u.map->condensed;
	locals.total = cm->string_size + cm->misc_size;
#ifdef IMPROVED_DUMPSTAT
	locals.rtotal = cm->string_size + cm->misc_size;
#endif
	locals.num_values = v->u.map->num_values;
	walk_mapping(v->u.map, svalue_size_map_filter, (char *)&locals);
	if (v->u.map->hash)
	    locals.total +=
	      sizeof(struct hash_mapping) +
		v->u.map->hash->mask * sizeof(struct map_chain *) +
		  v->u.map->hash->used *
		    (sizeof (struct map_chain) - sizeof(struct svalue));
#ifdef IMPROVED_DUMPSTAT
        if (!is_refd((void *)(v->u.map)))
        {
	  if (v->u.map->hash)
	      locals.rtotal +=
	        sizeof(struct hash_mapping) +
		  v->u.map->hash->mask * sizeof(struct map_chain *) +
		    v->u.map->hash->used *
		      (sizeof (struct map_chain) - sizeof(struct svalue));
          *rtotal += locals.rtotal;
        }
#endif
	return locals.total;
    }
#endif
    case T_POINTER:
    case T_QUOTED_ARRAY:
    {
	extern struct vector null_vector;

	extern int register_pointer PROT((char *));

	if (v->u.vec == &null_vector) return 0;
	if (register_pointer( (char *)(v->u.vec) ) ) return 0;
#ifdef MALLOC_smalloc
	total = malloced_size(v->u.vec) * sizeof(p_int);
#else
	total = sizeof *v->u.vec - sizeof v->u.vec->item +
	  sizeof(struct svalue) * v->u.vec->size + sizeof(char *);
#endif
#ifdef IMPROVED_DUMPSTAT
        if (!is_refd((void *)(v->u.vec)))
        {
          int tmp;
          *rtotal = total;
	  for (i=0; i < v->u.vec->size; i++) {
	    total += svalue_size(&v->u.vec->item[i], &tmp);
            *rtotal += tmp;
	  }
        }
#else
	for (i=0; i < v->u.vec->size; i++) {
	    total += svalue_size(&v->u.vec->item[i]);
	}
#endif
	return total;
    }
    case T_CLOSURE:
    {
	extern int register_pointer PROT((char *));
	int num_values;
	struct svalue *svp;
	struct lambda *l;

	if (!CLOSURE_MALLOCED(v->x.closure_type)) return 0;
	if (!CLOSURE_REFERENCES_CODE(v->x.closure_type)) {
	    /* CLOSURE_LFUN || CLOSURE_IDENTIFIER || CLOSURE_PRELIMINARY */
	    REFTOTAL(v->u.lambda,sizeof *v->u.lambda + sizeof(char *))
	    return sizeof *v->u.lambda + sizeof(char *);
	}
	/* CLOSURE_LAMBDA */
	total = 0;
        SETTOTAL(0)
	l = v->u.lambda;
	if (v->x.closure_type == CLOSURE_BOUND_LAMBDA)
	{
	    REFTOTAL(l, sizeof *l - sizeof l->function + sizeof l->function.lambda)
	    total += sizeof *l - sizeof l->function + sizeof l->function.lambda;
	    l = l->function.lambda;
	}
	num_values = EXTRACT_UCHAR(&l->function.code[0]);
	if (num_values == 0xff)
	    num_values = ((struct svalue *)l)[-0xff].u.number;
	svp = (struct svalue *)l - num_values;
	if (register_pointer( (char *)svp ) ) return 0;
#ifdef MALLOC_smalloc
	total += malloced_size(svp) * sizeof(p_int);
	REFITOTAL(svp, malloced_size(svp) * sizeof(p_int))
#else
	total += sizeof(struct svalue) * num_values + sizeof (char *);
	REFITOTAL(svp, sizeof(struct svalue) * num_values + sizeof (char *))
	{
	    char *p = &l->function.code[2];
	    do {
		switch(*++p) {
		  case F_RETURN -F_OFFSET:
		  case F_RETURN0-F_OFFSET:
		    break;
		}
	    } while (1);
	    total += p - (char *)l + (sizeof(char *) - 1) &
		~(sizeof(char *) - 1);
	    REFITOTAL(svp, p - (char *)l + (sizeof(char *) - 1) &
		~(sizeof(char *) - 1))
	}
#endif
	while (--num_values >= 0) {
#ifdef IMPROVED_DUMPSTAT
            int tmp;
	    total += svalue_size(svp, &tmp);
            REFITOTAL(svp++, tmp);
#else
	    total += svalue_size(svp++);
#endif
	}
	return total;
    }
    default:
	fatal("Illegal type: %d\n", v->type);
    }
    /*NOTREACHED*/
#ifdef lint
    return 0;
#endif
}

#ifdef IMPROVED_DUMPSTAT
static mp_int data_size(ob, rtotal)
    struct object *ob;
    int *rtotal;
#else
static mp_int data_size(ob)
    struct object *ob;
#endif
{
    struct pointer_record;
    extern void init_pointer_table PROT((struct pointer_record **));
    extern void free_pointer_table PROT((void));

    mp_int total = 0, i;
    struct pointer_record *pointer_table_space[256];

#ifdef IMPROVED_DUMPSTAT
    *rtotal = 0;
#endif
    init_pointer_table(pointer_table_space);
    if (ob->prog) {
        init_pointer_table(pointer_table_space);
        for (i = 0; i < ob->prog->num_variables; i++)
#ifdef IMPROVED_DUMPSTAT
        {
            int tmp;
    	    total += svalue_size(&ob->variables[i], &tmp) + sizeof (struct svalue);
            *rtotal += tmp;
	}
#else
    	    total += svalue_size(&ob->variables[i]) + sizeof (struct svalue);
#endif
    }
    free_pointer_table();
    return total;
}

void dumpstat() {
    FILE *f;
    struct object *ob;

    f = fopen("OBJ_DUMP", "w");
    if (f == 0)
	return;
    add_message("Dumping to OBJ_DUMP ...");
#ifdef IMPROVED_DUMPSTAT
    init_gtable();
#endif
    for (ob = obj_list; ob; ob = ob->next_all) {
	mp_int tmp;
#ifdef IMPROVED_DUMPSTAT
        mp_int rtotal;
#endif
	if (ob->prog && (ob->prog->ref == 1 || !(ob->flags & O_CLONE)))
	    tmp = ob->prog->total_size;
	else
	    tmp = 0;
#ifdef IMPROVED_DUMPSTAT
	fprintf(f, "%-20s %5d %5d (%5d) ref %2d %s %s (%ld) %s\n", ob->name,
		tmp + data_size(ob, &rtotal) + sizeof (struct object),
		tmp + rtotal + sizeof (struct object), tmp, ob->ref,
#else
	fprintf(f, "%-20s %5ld ref %2d %s %s (%ld) %s\n", ob->name,
		tmp + data_size(ob) + sizeof (struct object), ob->ref,
#endif
		ob->flags & O_HEART_BEAT ? "HB" : "  ",
		ob->super ? ob->super->name : "--",/*ob->cpu*/ 0L,
		ob->swap_num >=0 ? "SWAPPED" : "");
    }
#ifdef IMPROVED_DUMPSTAT
    free_gtable();
#endif
    add_message("done.\n");
    fclose(f);
}