/******************************************************************

    $Header$

    Module: symtable.C

    Author: Jeff Lait

    Copyright 1997 Ytinasin.

    Description: Implemenation of symbol table.

 ******************************************************************/

/******************************************************************

  Revision Record

    Rev Date        Auth    Changes
    === ====        ====    =======

    0.0 12/8/97 jml     start

 ******************************************************************/

/******************************************************************
 * INCLUDES:
 ******************************************************************/
#include "defines.h"
#include "atom.h"
#include "symtable.h"
#include <string.h>
#include <memory.h>
#include <assert.h>

/******************************************************************
 * SYMTABLE functions
 ******************************************************************/
/******************************************************************
 * SYMTABLE::SYMTABLE
 ******************************************************************/
SYMTABLE::SYMTABLE(SYMTABLE *parent, ATOM_MNGR *atommngr)
{
    m_parent = parent;
    if (m_parent)
        m_parent->addRef();
    m_refcnt = 0;
    m_atommngr = atommngr;
}


/******************************************************************
 * SYMTABLE::~SYMTABLE
 ******************************************************************/
SYMTABLE::~SYMTABLE()
{
    if (m_parent)
        m_parent->removeRef();
}


/******************************************************************
 * SYMTABLE::addRef
 ******************************************************************/
void SYMTABLE::addRef()
{
    m_refcnt++;
}


/******************************************************************
 * SYMTABLE::removeRef
 ******************************************************************/
void SYMTABLE::removeRef()
{
    m_refcnt--;

    if (m_refcnt < 1)
        delete this;
}

/******************************************************************
 * SYMTABLE_HASH member funcs
 ******************************************************************/
/******************************************************************
 * SYMTABLE_HASH::SYMTABLE_HASH
 ******************************************************************/
SYMTABLE_HASH::SYMTABLE_HASH(SYMTABLE *parent, ATOM_MNGR *atommngr)
		: SYMTABLE(parent, atommngr)
{
    memset(m_hash, 0, 256 * sizeof(SYMENTRY *));
}
    
    
/******************************************************************
 * SYMTABLE_HASH::~SYMTABLE_HASH
 ******************************************************************/
SYMTABLE_HASH::~SYMTABLE_HASH()
{
    int i;

    for (i = 0; i < 256; i++)
        delete m_hash[i];
}


/******************************************************************
 * SYMTABLE_HASH::hash
 ******************************************************************/
int SYMTABLE_HASH::hash(char *name) const
{
    int hashval = 0;
    int idx = 0;

    // El cheeso hash func:
    while (*name)
    {
        hashval += (*name << idx) + (*name >> (8 - idx));
        name++;
        idx++;
        idx &= 7;
    }

    hashval &= 0xff;
    return hashval;
}


/******************************************************************
 * SYMTABLE_HASH::AddEntry
 ******************************************************************/
void SYMTABLE_HASH::AddEntry(SYMENTRY *entry)
{
    int     hashval;

    assert(!entry->next);
    entry->parent = this;
    hashval = hash(entry->name);
    entry->next = m_hash[hashval];
    m_hash[hashval] = entry;            // THIS MUST BE ATOMIC
}


/******************************************************************
 * SYMTABLE_HASH::LookupEntry
 ******************************************************************/
uNoMutex SYMENTRY *SYMTABLE_HASH::LookupEntry(char *name, int useGlobal, int hashval) const
{
    SYMENTRY *cur;

    if (!useGlobal && !m_parent)
    {
        return NULL;            // Ignore our level!
    }

    if (hashval == -1)
    {
        // Must calculate hash.  This ensures only calculated once per
        // search...
        hashval = hash(name);
    }

    for (cur = m_hash[hashval]; cur; cur = cur->next)
    {
        if (!strcmp(name, cur->name))
            break;                      // FOund match!
    }

    if (!cur && m_parent)
    {
        // No match, try parent, if exists:
        cur = m_parent->LookupEntry(name, useGlobal, hashval);
    }

    return cur;
}

/******************************************************************
 * SYMTABLE_HASH::print
 ******************************************************************/
void SYMTABLE_HASH::print(uOStream &output)
{
    int i;
    SYMENTRY *sym;

    output << "This level has defined: ";
    for (i = 0; i < 256; i++)
    {
#if 0
        if (m_hash[i])
            output << endl << i << ": ";
#endif
        for (sym = m_hash[i]; sym; sym = sym->next)
        {
            output << sym->name << ", ";
        }
    }
    //output << " and that is all..."<< endl;
    if (m_parent)
        m_parent->print(output);
}

/******************************************************************
 * SYMTABLE_LIST member funcs
 ******************************************************************/
/******************************************************************
 * SYMTABLE_LIST::SYMTABLE_LIST
 ******************************************************************/
SYMTABLE_LIST::SYMTABLE_LIST(SYMTABLE *parent, ATOM_MNGR *atommngr)
		: SYMTABLE(parent, atommngr)
{
    m_list = NULL;
}
    
    
/******************************************************************
 * SYMTABLE_LIST::~SYMTABLE_LIST
 ******************************************************************/
SYMTABLE_LIST::~SYMTABLE_LIST()
{
    delete m_list;
}


/******************************************************************
 * SYMTABLE_LIST::AddEntry
 ******************************************************************/
void SYMTABLE_LIST::AddEntry(SYMENTRY *entry)
{
    int     hashval;

    assert(!entry->next);
    entry->parent = this;
    entry->next = m_list;
    m_list = entry;            // THIS MUST BE ATOMIC
}


/******************************************************************
 * SYMTABLE_LIST::LookupEntry
 ******************************************************************/
uNoMutex SYMENTRY *SYMTABLE_LIST::LookupEntry(char *name, int useGlobal, int hashval) const
{
    SYMENTRY *cur;

    if (!useGlobal && !m_parent)
    {
        return NULL;            // Ignore our level!
    }

    for (cur = m_list; cur; cur = cur->next)
    {
        if (!strcmp(name, cur->name))
            break;                      // FOund match!
    }

    if (!cur && m_parent)
    {
        // No match, try parent, if exists:
        cur = m_parent->LookupEntry(name, useGlobal, hashval);
    }

    return cur;
}

/******************************************************************
 * SYMTABLE_LIST::print
 ******************************************************************/
void SYMTABLE_LIST::print(uOStream &output)
{
    int i;
    SYMENTRY *sym;

    output << "This level has defined: ";
#if 0
    output << endl << "This is a linked list symtable: ";
#endif
    for (sym = m_list; sym; sym = sym->next)
    {
	output << sym->name << ", ";
    }
    //output << " and that is all..."<< endl;
    if (m_parent)
        m_parent->print(output);
}

