/*
 * nsrefarr.c - Implementation of the nsRef array (mem allocation, garbage
 *            collection, handle management, etc.)
 *
 * Copyright (C) 1997, George Madrid
 * All rights reserved.
 */

#include <stdlib.h>
#include <assert.h>

#include "config.h"
#include "nsobj.h"
#include "nsref.h"
#include "nsrefarr.h"

/*
 * This is a HARD data abstraction.
 *
 * Keep an array of references to nsRef objects.  The index of an object in
 * this array is used in the nsObj to find it again.  This allows garbage
 * collection to move stuff around, and allows ReplaceObject to work.
 */

/*
 * For now (a VERY initial implementation), everything will be alloc'ed and no
 * memory management is being done here.
 */

static nsRef *ref_array = NULL;
static size_t ref_array_size;
static size_t next_empty;

/*
 * Conventions:
 *
 * Index zero ALWAYS contains the "Global" environment
 *
 *
 * Slot contents:
 *
 * When a slot is used, it contains a pointer to the nsRef.
 * When a slot is unused, it contains the index of the next free slot.  If we
 * ever encounter a next slot that is 0, we've run out of slots.  Uh oh.  (We
 * can do this since 0 is never free because it is the 'nil' symbol object.)
 */

/* ------------------------------------------------------------------------ */
int
init_ref_array(size_t size)
{
     int i;

     ref_array = calloc(size, sizeof(*ref_array));
     if (!ref_array) goto punt;
     ref_array_size = size;
     
     /* Should also initialize the 'nil' symbol object here, too.  --gam */

     /* Slot one is always first, because slot zero is the 'nil' object */
     next_empty = 1;

     /* Now initialize all of the empty slots */
     for (i = 1; i < ref_array_size; i++) {
	  ref_array[i] = (nsRef)(i + 1); /* The next one is free */
     }

     /* There is no next slot after the last one. */
     ref_array[ref_array_size - 1] = 0;

     return 0;

     /* Get out! */
     /* Quick, quick, quick, while we can still say we made a mistake! */
   punt:
     if (ref_array) free(ref_array);
     return 1;			/* Error code */
}

/* ------------------------------------------------------------------------ */
/*
 * alloc_slot(size, nsRefp) -> index
 *
 * Low level routine to allocate a slot pointing to <size> bytes of free
 * memory.  Returns the index of the slot.  If it ever returns '0', we're
 * out of memory.  Put the address of the new slot in <nsRefp>.
 *
 * WARNING:  Running out of slots is a crash offense and will bring the
 * interpreter to a screeching halt, warning you to increase the number of
 * slots.  Maybe I should make it realloc.  Who knows?  --gam
 */
size_t
alloc_slot(size_t size, nsRef *nsRefp)
{
     size_t index;

     index = next_empty;
     next_empty = (size_t)ref_array[index];

     if (index == 0) {
	  /* Oh, my God!  We're out of slots.  Flail and look stupid. */
	  exit(99);  /* Make this quit a little more gracefully, please. --gam */
     }

     ref_array[index] = malloc(size);
     ref_array[index]->size = size; /* Be nice, fill in the size */
     if (NULL == ref_array[index]) return 0; /* Out of memory */
     if (NULL != nsRefp) *nsRefp = ref_array[index];
     return index;
}

/* ------------------------------------------------------------------------ */
/*
 * free_slot(index)
 *
 * Low level routine to free a slot by index.  Opens the slot for reuse, and
 * frees up the memory it used.
 */
void
free_slot(size_t index)
{
     free(ref_array[index]);
     ref_array[index] = (nsRef)next_empty;
     next_empty = index;
}


/* ------------------------------------------------------------------------ */
nsRef
find_index(size_t index)
{
     assert(index < ref_array_size);

     return ref_array[index];
}
