
/* Pocket Smalltalk
   Copyright (c) 1998,1999 by Andrew Brault
   http://www.pocketsmalltalk.com
   See LICENSE.TXT for license information */

/* Memory management and Pilot database support (for images) */


#include "main.h"



Value ** object_table;
uint8 ** class_table;


typedef struct {
  Object * object;
  uint16 position;
  uint16 length;
} MarkStackEntry;

/* NOTE: this value determines the maximum noncyclic "nesting
   depth" of objects pointing to other objects.  The garbage
   collector will fail (with an error message) if this depth
   is exceeded. */
#define MARK_STACK_SIZE  50


uint16 static_high_water = 0;   /* last statically allocated object */
Object first_character;

SystemProperties system_properties;
Object globals_array;


#ifdef PROFILING
uint32 allocated_bytes = 0;
#endif


#ifdef DEBUG_INFO
static Segment * selector_segments = NIL;
static uint8 * class_name_data = NIL;
static uint16 class_name_data_size;
#endif

static Segment * static_segments = NIL;
static Segment * dynamic_segments = NIL;
static Segment * current_segment = NIL;
static ClassSegment * class_segments = NIL;
static uint16 class_count = 0;
static MarkStackEntry * mark_stack;
static Object free_object_table_list;
static DmOpenRef ImageDatabaseHandle;


static void create_object_table(void);
static void create_characters(void);
static void thread_object_table(void);
static void add_dynamic_segment(uint16 words);
static Value * allocate_space(uint16 words);
static Object instantiate_shell(Object cls, uint16 length,
				boolean has_pointers);

static void mark(void);
static void mark_object(Object object);
static void fix_closure_lists(void);
static void compact(void);
static void compact_segment(Segment * segment);


static Boolean load_resources(void);
static Boolean load_resources_of_type(uint32 type);
static void load_resource(VoidHand handle, uint32 type);

static void load_object_segment(uint8 * data, uint32 size);
static void load_class_segment(uint8 * data, uint32 size);
static void load_class_size_segment(uint8 * data, uint32 size);
static void load_system_properties(uint8 * data, uint32 size);

#ifdef DEBUG_INFO
static void load_selector_data_segment(uint8 * data, uint32 size);
static void load_class_name_segment(uint8 * data, uint32 size);
#endif

static void release_resources(void);


/* These macros get and set 16 bits of a 32 bit object
   table entry.  This is used for threading the free
   object table entry list and for temporary storage
   during sliding compaction. */
#define GET_OBJTABLE_WORD(index) \
	((uint16 *)(object_table + index))[0]

#define SET_OBJTABLE_WORD(index, value) \
	((uint16 *)(object_table + index))[0] = (value)



Boolean initialize_memory(void)
{
  mark_stack = (MarkStackEntry *)
	allocate_chunk(sizeof(MarkStackEntry) * MARK_STACK_SIZE);
  if (!load_resources()) return FALSE;
  add_dynamic_segment(system_properties.heap_size);
  create_characters();
  thread_object_table();

  globals_array = instantiate_indexed
        (Array, system_properties.global_count);
  return TRUE;
}


void shutdown_memory(void)
{
  release_resources();
}


int slow_memcmp(void * p1, void * p2, int bytes)
{
  uint8 * s1 = (uint8 *)p1, * s2 = (uint8 *)p2;

  while(bytes-- > 0)
    if(*s1++ != *s2++)
      return 1;
  return 0;
}


void * allocate_chunk(uint16 size)
{
  void * ptr;

#ifdef PROFILING
  allocated_bytes += size;
#endif

#ifdef PARANOIA
  if(size == 0)
    panic("zero allocation");
#endif
  ptr = (void *)MemPtrNew(size);
  if(ptr)
    return ptr;
  else {
    panic("out of memory");
    return NIL;
  }
}


/* Allocate in the dynamic heap---should only be done for
   small allocations */
void * allocate_small_chunk(uint16 size)
{
  return allocate_chunk(size);
}


static void create_object_table(void)
{
  uint16 n;

  object_table = (Value **)
	allocate_chunk(sizeof(Value *) * 
		       system_properties.object_table_size);
  for(n = 0; n < system_properties.object_table_size; n++)
    object_table[n] = NIL;
}


/* Make 255 extra object pointers to the single Character
   object (the last object loaded), and update the
   static_high_water pointer */
static void create_characters(void)
{
  uint16 n;

  first_character = static_high_water - 1;
  for(n = 1; n < 256; n++) {
    object_table[first_character + n] =
	object_table[first_character];
  }
  static_high_water += 255;

  /* Convert first_character into an objref for later use */
  first_character <<= 1;
}


/* After the static objects have been read in and
   static_high_water set, thread the "free" object
   table entries into a linked list */
static void thread_object_table(void)
{
  uint16 n;

  /* Thread it in reverse order, so that we get the smaller
     object table entries first in the list.
     No real reason for this; it just seems easier to debug */
  free_object_table_list = 0;
  for(n = system_properties.object_table_size - 1; 
      n >= static_high_water; n--) {
    SET_OBJTABLE_WORD(n, free_object_table_list);
    free_object_table_list = n;
  }
}


/* Add a new segment of dynamic memory, of the given size.
   Answer whether allocation succeeded. */
static void add_dynamic_segment(uint16 words)
{
  Segment * segment;

  segment = (Segment *)allocate_chunk(sizeof(Segment));
  segment->size = words;
  segment->allocation = 0;
  segment->data = (Value *)
	allocate_chunk(sizeof(Value) * words);
  segment->next = dynamic_segments;
  dynamic_segments = segment;
  current_segment = segment;
}


/* Reserve some space in a dynamic memory segment.
   If there is not enough memory, return NIL. */
static Value * allocate_space(uint16 words)
{
  Segment * original_segment;
  Value * ptr;

  original_segment = current_segment;
  do {
    /* Enough space in this segment? */
    if(current_segment->size - current_segment->allocation >= words) {
      /* Allocate it here. */
      ptr = current_segment->data + current_segment->allocation;
      current_segment->allocation += words;
      return ptr;
    }
    current_segment = current_segment->next;
    if(!current_segment)
      current_segment = dynamic_segments;
  } while(current_segment != original_segment);

  /* No suitable chunk was found. */
  return NIL;
}


/* Allocate the "shell" of an object - this contains the
   header, class, and backpointer, plus the given number
   of cells.  It also includes an object table entry. */
static Object instantiate_shell(Object cls, uint16 length,
				boolean has_pointers)
{
  Object objref;
  Value * body;
  uint16 n;
  static boolean recursion_lock = FALSE;

  length += 3;  /* for header */

  /* Try to acquire an object table entry first.
     If that's impossible, garbage collect ONCE then
     try again -- if it fails then, we've no choice
     but to give up. */
  if(!(objref = free_object_table_list)) {
    collect_garbage();
    if(!(objref = free_object_table_list))
      panic("out of object table slots");
  }
  free_object_table_list = GET_OBJTABLE_WORD(objref);

  /* Convert object table index to objref */
  objref <<= 1;

  /* Try allocating the memory for the object. */
  body = allocate_space(length);
  if(!body) {
    if(recursion_lock) {
      /* Should add another segment, then try again... */
      panic("memory exhausted");
    }
    else {
      /* Return the object pointer to the free list
	 before retrying */
      objref >>= 1;
      SET_OBJTABLE_WORD(objref, free_object_table_list);
      free_object_table_list = objref;
      collect_garbage();
      recursion_lock = TRUE;
      objref = instantiate_shell(cls, length - 3, has_pointers);
      recursion_lock = FALSE;
      return objref;
    }
  }

  /* Set object pointer */
  LOOKUP(objref) = body;

  /* Write header fields */
  body[0] = length;
  if(has_pointers) body[0] |= 0x4000U;
  body[1] = cls;
  body[2] = objref;

  /* Write 0s into the body; fortunately 0 works
     for pointer objects (nil) and byte objects (0) */
  for(n = 3; n < length; n++)
    body[n] = 0;  /* use memset instead */

  return objref;
}


/* Instantiate a pointer object with no indexed variables */
Object instantiate_normal(Object cls)
{
  uint16 length;

  length = FROM_SMALLINT(OBJECT_ACCESS(cls, Behavior_layout));
  return instantiate_shell(cls, length & 4095, TRUE);
}


/* Instantate a class with indexed instance variables */
Object instantiate_indexed(Object cls, uint16 indexed)
{
  uint16 layout;

  layout = FROM_SMALLINT(OBJECT_ACCESS(cls, Behavior_layout));
  return instantiate_shell(cls, (layout & 4095) + indexed, TRUE);
}


/* Instantiate a class with byte-indexed instance variables.
   The 'indexed' argument gives the number of bytes */
Object instantiate_byte_indexed(Object cls, uint16 indexed)
{
  Object object;

  object = instantiate_shell(cls, (indexed >> 1) + 1, FALSE);

  /* Set the first byte as length adjustment */
  OBJECT_BYTE_ACCESS(object, -1) = (indexed & 1) ? 1 : 2;
  return object;
}


/* Make a shallow copy of any object.
   Immediates are returned unchanged. */
Object shallow_copy(Object object)
{
  Object copy;

  if(IS_SMALLINT(object))
    return object;

  /* Pass bogus arguments to instantiate_shell...
     all we really need is the memory block */
  copy = instantiate_shell(nil, OBJECT_SIZE(object), FALSE);
#ifdef PARANOIA
  if(OBJECT_LENGTH(copy) != OBJECT_LENGTH(object))
    panic("shallow_copy");
#endif
  MEMCOPY(LOOKUP(copy), LOOKUP(object),
	  OBJECT_LENGTH(object) << 1);

  /* Replace the objref pointer in the copy */
  OBJPTR_OBJREF(LOOKUP(copy)) = copy;

  return copy;
}


/* VM-level function for #basicAt: (for all objects).
   If access fails (i.e., object is not indexable, or
   index is out of bounds), answers Failure_value. */
/* NOTE: 'index' is 0-origin here !! */
Object basic_at(Object object, uint16 index)
{
  Object layout, size;

  if(IS_SMALLINT(object))
    return Failure_value;  /* Not indexable */

  if(OBJECT_HAS_POINTERS(object)) {
    size = OBJECT_SIZE(object);
    layout = OBJECT_ACCESS(OBJECT_CLASS(object), Behavior_layout);
    layout = (layout >> 1) & 4095;  /* # of named inst vars */
    if(index >= (size - layout))
      return Failure_value;  /* out of range */
    return OBJECT_ACCESS(object, layout + index);
  }
  else {
    /* Pointerless object -- access by byte index */
    if(index >= OBJECT_BYTE_SIZE(object))
      return Failure_value;
    else return TO_SMALLINT(OBJECT_BYTE_ACCESS(object, index));
  }
}


/* VM-level function for #basicAt:put: (for all objects).
   If access fails (i.e., object is not indexable, or
   index is out of bounds, or object cannot contain the
   new value, answers FALSE.  On success, answers TRUE. */
/* NOTE: 'index' is 0-origin here !! */
boolean basic_at_put(Object object, uint16 index, Object new_value)
{
  Object layout, size;

  if(IS_SMALLINT(object))
    return FALSE;

  if(OBJECT_HAS_POINTERS(object)) {
    size = OBJECT_SIZE(object);
    layout = OBJECT_ACCESS(OBJECT_CLASS(object), Behavior_layout);
    layout = (layout >> 1) & 4095;
    if(index >= (size - layout))
      return FALSE;
    else {
      OBJECT_SET(object, index + layout, new_value);
      return TRUE;
    }
  }
  else {
    if(index >= OBJECT_BYTE_SIZE(object))
      return FALSE;
    if((IS_SMALLINT(new_value)) &&
       (FROM_SMALLINT(new_value) >= 0) &&
       (FROM_SMALLINT(new_value) <= 255)) {
      OBJECT_BYTE_ACCESS(object, index) = FROM_SMALLINT(new_value);
      return TRUE;
    }
    else return FALSE;
  }
}


/* Answers Failure_value if the index is invalid.
   index is 0-origin here. */
Object inst_var_at(Object object, int16 index)
{
  Object layout;
  uint16 layout_value;

  if(index < 0)
    return Failure_value;

  layout = OBJECT_ACCESS(OBJECT_CLASS(object), Behavior_layout);
  layout_value = FROM_SMALLINT(layout);

  /* Pointerless objects cannot have any inst var slots.
     The indexed bytes do not count (maybe they should?) */
  if(layout_value & 8192)
    return Failure_value;
  if(index > OBJECT_SIZE(object))
    return Failure_value;
  return OBJECT_ACCESS(object, index);
}


boolean inst_var_at_put(Object object, int16 index, 
                        Object new_value)
{
  Object layout;
  uint16 layout_value;

  if(index < 0)
    return FALSE;

  layout = OBJECT_ACCESS(OBJECT_CLASS(object), Behavior_layout);
  layout_value = FROM_SMALLINT(layout);

  /* Pointerless objects cannot have any inst var slots.
     The indexed bytes do not count (maybe they should?) */
  if(layout_value & 8192)
    return FALSE;
  if(index > OBJECT_SIZE(object))
    return FALSE;

  OBJECT_SET(object, index, new_value);
  return TRUE;
}


uint32 total_free_memory(void)
{
  uint32 total = 0;
  Segment * segment;

  for(segment = dynamic_segments; segment; segment = segment->next)
    total += segment->size - segment->allocation;
  return total;
}


uint16 total_free_objrefs(void)
{
  uint16 count = 0, ptr;

  for(ptr = free_object_table_list; ptr != 0;
      ptr = GET_OBJTABLE_WORD(ptr))
    count++;
  return count;
}


/* Swap the identities of first and second.
   Does no error checking! */
void become(Object first, Object second)
{
  Value * first_ptr, * second_ptr;
  Object objref;

  first_ptr = LOOKUP(first);
  second_ptr = LOOKUP(second);

  /* Swap objrefs */
  objref = OBJPTR_OBJREF(first_ptr);
  OBJPTR_OBJREF(first_ptr) = OBJPTR_OBJREF(second_ptr);
  OBJPTR_OBJREF(second_ptr) = objref;

  /* Swap object table slots */
  LOOKUP(first) = second_ptr;
  LOOKUP(second) = first_ptr;
}


void collect_garbage(void)
{
#ifdef PROFILING
  garbage_collections++;
#endif

  mark();

  /* Now all the reachable objects are known.
     Scan through each closure_chain in the active
     stack frames, and squeeze out the closures that
     are not being referenced. */
  fix_closure_lists();

  /* Compact each dynamic segment, update object table,
     and clear mark bits. */
  compact();
}


static void mark(void)
{
  uint16 n;

#ifdef PROCESSES
  int p;
  for(p = 0; p < MAX_PROCESSES; p++) {
    if(process_status[p].data_stack) {
      for(n = 0; n < process_status[p].data_stack_ptr; n++)
        mark_object(process_status[p].data_stack[n]);
    }
  }
#else  
  for(n = 0; n < data_stack_ptr; n++)
    mark_object(data_stack[n]);    
#endif

  mark_object(globals_array);
}


/* Mark an object and all its referents */
static void mark_object(Object object)
{
  uint16 stack_ptr;
  Object referent, * refptr;
  MarkStackEntry * stack = mark_stack;
  MarkStackEntry current;

  if(IS_SMALLINT(object))
    return;

  stack_ptr = 0;
  current.object = LOOKUP(object);
  if(OBJPTR_MARKED(current.object))
    return;
  OBJPTR_SET_MARK(current.object);
  if(!OBJPTR_HAS_POINTERS(current.object))
    return;  /* i.e., no referents */
  current.length = OBJPTR_LENGTH(current.object);
  current.position = 3;  /* first data slot */

  while(1) {
    while(current.position < current.length) {
      referent = current.object[current.position++];
      if(IS_SMALLINT(referent))
	continue;
      refptr = LOOKUP(referent);
      if(OBJPTR_MARKED(refptr))
	continue;
      OBJPTR_SET_MARK(refptr);
      if(OBJPTR_HAS_POINTERS(refptr)) {
	/* Push the current object onto the stack, and
	   start scanning the new object. */
	if(stack_ptr > (MARK_STACK_SIZE-3))
	  panic("mark stack overflow");
	stack[stack_ptr++] = current;  /* struct copy */
	current.object = refptr;
	current.length = OBJPTR_LENGTH(refptr);
	current.position = 3;
      }
    }
    /* Finished scanning this object.
       Pop the most recent object from the stack
       (if there is one) and continue scanning */
    if(stack_ptr)
      current = stack[--stack_ptr];
    else
      return;  /* Finished ! */
  }
}


static void fix_closure_lists(void)
{
  uint16 n;
  Object closure, next, prev;

  next = nil;  /* placate compiler */
  save_call_frame();  /* get current list on stack */
  for(n = 1; n < call_stack_ptr; n++) {
    /* Splice out each element of the linked list
       that does not have its mark bit set. */
    prev = nil;
    for(closure = call_stack[n].closure_chain;
	closure; closure = next) {
      next = OBJECT_ACCESS(closure, Block_nextLink);
      if(next) next--;  /* unalias */
      if(OBJECT_MARKED(closure)) {
	/* Leave it in the list */
	prev = closure;
      }
      else {
	/* Remove it from the list. */
	if(prev)
          OBJECT_SET(prev, Block_nextLink, next | 1);  /* alias next */
	else
          call_stack[n].closure_chain = next;  /* no alias here */
	/* Note that prev is not updated here */
      }
    }
  }
  restore_call_frame();
}


/* Slide objects toward the beginning of each segment. */
static void compact(void)
{
  Segment * segment;

  for(segment = dynamic_segments; segment; segment = segment->next)
    compact_segment(segment);
}


/* Live objects are slid toward the beginning of the segment,
   while unreferenced objects are "squeezed" out and their
   object table entries recycled */
static void compact_segment(Segment * segment)
{
  uint16 position, limit, destination, length;
  Object objref;
  Value * data;

  position = destination = 0;
  limit = segment->allocation;
  data = segment->data;
  while(position < limit) {
    objref = OBJPTR_OBJREF(data + position);
    if(OBJPTR_MARKED(data + position)) {
      /* Preserve this object.
	 First update its object table entry. */
      LOOKUP(objref) = data + destination;
      /* Clear the mark bit. */
      OBJPTR_CLEAR_MARK(data + position);
      /* Now copy it down. */
      length = OBJPTR_LENGTH(data + position);
      if(position != destination) {
	/* Could use MemCopy here? */
	while(length-- > 0)
	  data[destination++] = data[position++];
      }
      else {
	/* No movement necessary */
	position += length;
	destination += length;
      }
    }
    else {
      /* Unreferenced object. */
      /* Free the object table entry */
      objref >>= 1;  /* convert to objtable index */
      SET_OBJTABLE_WORD(objref,	free_object_table_list);
      free_object_table_list = objref;
      /* Skip over the object data */
      position += OBJPTR_LENGTH(data + position);
    }
  }
  /* Reset allocation counter to after last remaining object */
  segment->allocation = destination;
}

/* Return true if everything went ok, otherwise return false
*  and assume that nothing was allocated and no image was found. */


static Boolean find_image(void)
{
	DmSearchStatePtr stateInfoP;
	UInt16 cardNo, chosenCardNo;
	LocalID dbID, chosenDbID;
	Err rc;
	VoidHand handle;
	Int i;
	Char name[32];

	handle = DmGetResource('SysP', 1);
		// See if we can find an image resource located in the VM itself
	if (handle) {
		DmReleaseResource(handle);
		return TRUE;
	}
		// No local image resource, so look for one
	stateInfoP = MemPtrNew(sizeof(DmSearchStateType));
  	rc = DmGetNextDatabaseByTypeCreator(true, stateInfoP, NULL, 'PkST', true, &cardNo, &dbID);
  	i = 0;
  	if (rc == dmErrCantFind) {
  		MemPtrFree(stateInfoP);
  		FrmCustomAlert (2020, "Cannot find any image resource to execute.", "", "");
  		return FALSE;
  		};
 	do {
  		DmDatabaseInfo(cardNo, dbID, name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
  		FrmCustomAlert (2020, name, "", "");
  		chosenCardNo = cardNo;
  		chosenDbID = dbID;
  		rc = DmGetNextDatabaseByTypeCreator(false, stateInfoP, NULL, 'PkST', true, &cardNo, &dbID);
  	} while (!rc);
  	MemPtrFree(stateInfoP);
	ImageDatabaseHandle = DmOpenDatabase(chosenCardNo, chosenDbID, dmModeReadOnly);
  	return TRUE;
}


/* Return true if everything went ok, otherwise return false
*  and assume that nothing was allocated and no image was found. */

static Boolean load_resources(void)
{

  if (!find_image()) return FALSE;
  load_resources_of_type('SysP');
  create_object_table();  /* now that we know sizes */
  load_resources_of_type('ObjD');
  load_resources_of_type('ClsD');
  load_resources_of_type('ClsO');
#ifdef DEBUG_INFO
  load_resources_of_type('SelD');
  load_resources_of_type('ClsN');
#endif
  static_high_water++;
  return TRUE;
}


static Boolean load_resources_of_type(uint32 type)
{
  uint16 n;
  VoidHand handle;

  n = 1;
  while((handle = DmGetResource(type, n)) != 0) {
    load_resource(handle, type);
    n++;
  }
  return n > 1;
}


static void load_resource(VoidHand handle, uint32 type)
{
  uint32 size;
  uint8 * data;

  size = MemHandleSize(handle);
  data = (uint8 *)MemHandleLock(handle);
  switch(type) {
    case 'ObjD': load_object_segment(data, size); break;
    case 'ClsD': load_class_segment(data, size); break;
    case 'ClsO': load_class_size_segment(data, size); break;
    case 'SysP': load_system_properties(data, size); break;
#ifdef DEBUG_INFO
    case 'SelD': load_selector_data_segment(data, size); break;
    case 'ClsN': load_class_name_segment(data, size); break;
#endif
#ifdef PARANOIA
    default: 
      panic("unknown segment type");
#endif
  }
}


static void load_object_segment(uint8 * data, uint32 size)
{
  uint16 n, words, objref;
  Segment * segment;

  words = (uint16)(size / 2);
  segment = (Segment *)allocate_chunk(sizeof(Segment));
  segment->size = words;
  segment->allocation = 0;
  segment->data = (uint16 *)data;
  segment->next = static_segments;
  static_segments = segment;

  /* Add objects to the object table */
  n = 0;
  while(n < words) {
    objref = OBJPTR_OBJREF(segment->data + n) >> 1;
#ifdef PARANOIA
    if(objref > system_properties.object_table_size)
      panic("too many objects!");
#endif          
    object_table[objref] = segment->data + n;
    if(objref > static_high_water)
      static_high_water = objref;
    n += OBJPTR_LENGTH(segment->data + n);    
  }
}


static void load_class_segment(uint8 * data, uint32 size)
{
  ClassSegment * segment, * ptr;

  segment = (ClassSegment *)
	allocate_small_chunk(sizeof(ClassSegment));
#ifdef PARANOIA
  if(!segment)
    panic("couldn't allocate class segment");
#endif    
  segment->size = size;
  segment->data = data;

  /* Add the segment to the _end_ of the list */
  for(ptr = class_segments; ptr && ptr->next; ptr = ptr->next)
    ;
  segment->next = NIL;
  if(ptr)
    ptr->next = segment;
  else class_segments = segment;
}


static void load_class_size_segment(uint8 * data, uint32 size)
{
  uint16 n, offset;
  ClassSegment * segment;
  uint16 * data16;
  VoidHand handle;

  data16 = (uint16 *)data;
  class_count = size / 2;  /* # of entries */
  class_table = (uint8 **)
        allocate_small_chunk(sizeof(uint8 *) * (class_count + 3));

  offset = 0;
  segment = class_segments;
  for(n = 0; n < 3; n++)
    class_table[n] = NIL;
  for(n = 0; n < class_count; n++) {
    class_table[n+3] = segment->data + offset;
    offset += data16[n];
#ifdef PARANOIA
    if(offset > segment->size)
      panic("oops");
#endif
    if(offset == segment->size) {
      offset = 0;
      segment = segment->next;
    }
  }

  handle = MemPtrRecoverHandle(data);
    // 1.6 Fix Start
#ifdef PARANOIA
	if (handle == 0)
		panic("There should be a handle to this");
#endif
  MemHandleUnlock(handle);
  	// 1.6 Fix End
  DmReleaseResource(handle);
}


static void load_system_properties(uint8 * data, uint32 size)
{
  VoidHand handle;

  system_properties = *((SystemProperties *)data);
  handle = MemPtrRecoverHandle(data);
    // 1.6 Fix Start
#ifdef PARANOIA
	if (handle == 0)
		panic("There should be a handle to this");
#endif
  MemHandleUnlock(handle);
  	// 1.6 Fix End
  DmReleaseResource(handle);
}


#ifdef DEBUG_INFO

static void load_selector_data_segment(uint8 * data, uint32 size)
{
  Segment * segment, * ptr;
  
  segment = (Segment *)allocate_small_chunk(sizeof(Segment));
  segment->size = size;
  segment->data = (Value *)data;
  
  for(ptr = selector_segments; ptr && ptr->next; ptr = ptr->next)
    ;
  segment->next = NIL;
  if(ptr)
    ptr->next = segment;
  else selector_segments = segment;
}


static void load_class_name_segment(uint8 * data, uint32 size)
{
  class_name_data_size = size;
  class_name_data = data;
}



uint8 * text_of_selector(uint16 selector)
{
  uint16 offset;
  Segment * ptr;
  
  selector--;
  for(ptr = selector_segments; ptr; ptr = ptr->next) {
    offset = 0;
    while(offset < ptr->size) {
      if(selector == 0)
        return ((uint8 *)(ptr->data)) + offset;
      selector--;
      while((offset < ptr->size) && ((uint8 *)(ptr->data))[offset])
        offset++;
      if(offset < ptr->size)
        offset++;
    }
  }
  return NIL;
}


uint8 * name_of_class(Object class_obj)
{
  uint16 offset, id;

  class_obj >>= 1;  /* convert to index */
  offset = 0;
  while(offset < class_name_data_size) {
    id = class_name_data[offset];
    id <<= 8;
    id |= class_name_data[offset+1];
    offset += 2;
    if(id == class_obj)
      return class_name_data + offset;
    while(class_name_data[offset])
      offset++;
    offset++;
  }
  return NIL;
}

#endif


Object infer_class(uint8 * ip)
{
  uint16 n;
  
  for(n = 0; n < class_count-1; n++) {
    if(ip > class_table[n+3] &&
       ip <= class_table[n+4])
      return (Object)((n + 3) << 1);
  }
  return (Object)((n + 3) << 1);
}


static void release_resources(void)
{
  Segment * segment;
  ClassSegment * class_seg;
  VoidHand handle;

  for(segment = static_segments; segment; segment = segment->next) {
    handle = MemPtrRecoverHandle(segment->data);
    	// 1.6 Fix Start
	if (handle) {
	  	MemHandleUnlock(handle);
    	DmReleaseResource(handle);
   	};
  		// 1.6 Fix End
  }

  for(class_seg = class_segments; class_seg; class_seg = class_seg->next) {
    handle = MemPtrRecoverHandle(class_seg->data);
    	// 1.6 Fix Start
	if (handle) {
	  MemHandleUnlock(handle);
      DmReleaseResource(handle);
    };
  		// 1.6 Fix End
  }

#ifdef DEBUG_INFO
  for(segment = selector_segments; segment; segment = segment->next) {
    	// 1.6 Fix Start
    handle = MemPtrRecoverHandle(segment->data);
	if (handle) {
  	  MemHandleUnlock(handle);
      DmReleaseResource(handle);
    };
  		// 1.6 Fix End
  };
  if(class_name_data) {
    	// 1.6 Fix Start
  	handle = MemPtrRecoverHandle(class_name_data);
	if (handle) {
  	  MemHandleUnlock(handle);
      DmReleaseResource(handle);
    };
  		// 1.6 Fix End
  };
#endif
  if (ImageDatabaseHandle) DmCloseDatabase(ImageDatabaseHandle);
}


