// $RCSfile: catalog.cc,v $
// $State: Exp $ 
// $Revision: 1.2 $
// $Author: roman $
// $Date: 90/07/19 21:09:24 $

//#define _DEBUG_

/*
 * A catalog is used to efficiently maintain a set of strings.
 */

#include <catalog.h>
#include <builtin.h>

#define nil 0

Catalog::Catalog (int n) {
    register EntryPtr* i;

    delfirst = nil;
    nelements = 8;
    while (nelements < n) {
	nelements <<= 1;
    }
    first = new EntryPtr[nelements];
    --nelements;
    last = &first[nelements];
    for (i = first; i <= last; i++) {
        *i = nil;
    }
}

Catalog::~Catalog () {
    register EntryPtr* i;
    register EntryPtr e;
    register EntryPtr next;

    for (i = first; i <= last; i++) {
	for (e = *i; e != nil; e = next) {
	    next = e->chain;
	    delete e->name;
	    delete e;
	}
    }
    delete first;
}

/*
 * Enter a new string into a catalog.
 */

SymbolID Catalog::Register (const char* name) {
    register unsigned h;
    int len;
    register EntryPtr e;
    h = Hash(name, len);
    e = first[h];

    while (e != nil && (e->len != len || strcmp(e->name, name) != 0)) {
	e = e->chain;
    }
    if (e != nil) {
        e->references++;
    } else {
        e = new CatalogEntry;
	e->name = new char[len+1];
	e->len = len;
	strcpy(e->name, name);
	e->references = 1;
	e->chain = first[h];
	first[h] = e;
    }
    return SymbolID(e);
}

/*
 * find a  string in the catalog.
 */

SymbolID Catalog::Find (const char* name) {
    register unsigned h;
    int len;
    register EntryPtr e;

    h = Hash(name, len);
    e = first[h];
    while (e != nil && (e->len != len || strcmp(e->name, name) != 0)) {
	e = e->chain;
    }
    if (e != nil) 
      return SymbolID(e);
    else
      return SymbolID(0);
}

/**
SymbolID Catalog::Register (SymbolID id) {
    register EntryPtr e;

    e = EntryPtr(id);

#ifdef _DEBUG_
    char* name;
    register unsigned h;
    int len;
    register EntryPtr prev;

    name = e->name;
    h = Hash(name, len);
    e = first[h];
    prev = nil;
    while (e != nil && (e->len != len || strcmp(e->name, name) != 0)) {
	prev = e;
	e = e->chain;
    }
#endif

    e->references++;
    return SymbolID(e);
}

**/

void Catalog::Unregister (SymbolID id) {
    register EntryPtr e;

    e = EntryPtr(id);

#ifdef _DEBUG_
    char* name;
    register unsigned h;
    int len;
    register EntryPtr prev;

    name = e->name;
    h = Hash(name, len);
    e = first[h];
    prev = nil;
    while (e != nil && (e->len != len || strcmp(e->name, name) != 0)) {
	prev = e;
	e = e->chain;
    }
#endif

    if (e != nil && e->references > 0) {
        if (--e->references == 0) {
	    // Code to insert this entry on the delete list.
#ifdef _DEBUG_
            Debug("Marking the symbol \"%s\" for deletion.  Do not use this SymbolID any more.",
		  e->name);
	    // But, for now, let's just corrupt the string.
	    // for (int i=0; i < e->len; i++) {
	    //   e->name[i] = '*';
	    // }
#endif
	}
    } else {
       exit(1);
//        Panic("Attempt to unregister a string which has no registered use.");
    }
}

/*
 * Return a hash value for a string name and set the length of the name
 * as a side effect.
 */

unsigned Catalog::Hash (const char *name, int& len) {
    register const char *c;
    register unsigned h;

    h = 0;
    c = name;
    while (*c != '\0') {
	h = (h << 1) ^ ((int) (*c));
	++c;
    }
    len = (c - name);
    return h & nelements;
}

void Catalog::DeleteUnregistered () {

    // Code to delete everything on the delete list with a reference count
    // of zero.

}

char * CatalogIter::next()
{ 
   if(chainptr){
      if(iter) return nextiter();
      chainptr++;
      if(nextchain()) return nextiter();
   }
   // some code is missing here
}

// dummy
int CatalogIter::nextchain()
{
   return 1;
}
