

#ifndef _htbl_imp_cxx_ /* Sat Jan 27 10:56:56 1996 */
#define _htbl_imp_cxx_

#if defined(__GNUC__)
#pragma implementation
#endif


#include "base/hashtbl.h"
#include "base/genseq.h"
#include "base/segseq.h"
#include "base/string.h"
#include "base/basicops.cxx"

#include <iostream.h>

struct Node {
    CL_VoidPtr p;
    long       hashValue;
    Node (CL_VoidPtr data, long hash) : p (data), hashValue (hash) {};
};

typedef CL_GenericSequence MainSequence;


template <class BaseType>
long CL_HashFunction<BaseType>::operator() (CL_VoidPtr p)
{
    return (long) p;
}



template <class Key, class Value>
long CL_AssocHashFunction<Key, Value>::operator() (CL_VoidPtr p)
{
    CL_MapAssoc<Key,Value>& assoc = *(CL_MapAssoc<Key,Value>*) p;
    CL_HashFunction<Key> fn;
    return fn (CL_Basics<Key>::MakePointer (assoc.key));
}



enum {
#if defined(__MS_DOS__) || defined(__MS_WINDOWS__)
    INITIAL_HASH_TABLE_SIZE = 253
#else
    INITIAL_HASH_TABLE_SIZE = 1029
#endif
};

template <class BaseType>
CL_HashTable<BaseType>::CL_HashTable (CL_AbstractHashFunction* fn)
    :  _data (new CL_SegmentedSequence (INITIAL_HASH_TABLE_SIZE)),
       _size (0),
       _usedBuckets(0)
{
    if (fn)
        _hash = fn;
    else
        _hash = &_privateHash;
    for (long i = _data->Size() - 1; i >= 0; i--)
        (*_data)[i] = 0;
}



template <class BaseType>
CL_HashTable<BaseType>::~CL_HashTable()
{
    _DoDestroyContents ();
    if (_data)
        delete _data;
}



template <class BaseType>
void CL_HashTable<BaseType>::DestroyContents ()
{
    if (PrepareToChange()) {
        _DoDestroyContents ();
        Notify();
    }
}


template <class BaseType>
void CL_HashTable<BaseType>::_DoDestroyContents ()
{
    for (long i = _data->Size() - 1; i >= 0; i--) {
        MainSequence* sq = (MainSequence*) (*_data)[i];
        if (sq) {
            for (long j = sq->Size() - 1; j >= 0; j--) {
                Node* pNode = (Node*) (*sq)(j);
                CL_Basics<BaseType>::Destroy (pNode->p);
                delete pNode;
            }
            delete sq;
            (*_data)[i] = 0;
        }
    }
}


template <class BaseType>
BaseType& CL_HashTable<BaseType>::Find (const BaseType& obj)
{
    long mainIndex, auxIndex;
    if (_Find (obj, mainIndex, auxIndex)) {
        MainSequence& sq = *(MainSequence*) (*_data)[mainIndex];
        return CL_Basics<BaseType>::Deref (sq(auxIndex));
    }
    return _nullValue;
}



template <class BaseType>
bool CL_HashTable<BaseType>::Includes (const BaseType& obj) const
{
    long mainIndex, auxIndex;
    return _Find (obj, mainIndex, auxIndex);
}



template <class BaseType>
bool CL_HashTable<BaseType>::_Find (const BaseType& obj,
                                    long& mainIndex, long& auxIndex) const
{
    long hashVal = (*_hash) (CL_Basics<BaseType>::MakePointer (obj));
    long n = _data->Size();
    mainIndex = hashVal % n;
    if (mainIndex < 0)
        mainIndex += n;
    if ((*_data) [mainIndex]) {
        MainSequence* sq = (MainSequence*) (*_data)[mainIndex];
        if (!sq)
            return FALSE;
        for (long i = sq->Size() - 1; i >= 0; i--) {
            Node* node = (Node*) (*sq) (i);
            if (node->hashValue == mainIndex && CL_Basics<BaseType>::Compare
                (CL_Basics<BaseType>::Deref (node->p), obj) == 0) {
                auxIndex = i;
                return TRUE;
            }
        }
    }
    return FALSE;
}



template <class BaseType>
bool CL_HashTable<BaseType>::Add (const BaseType& obj)
{
    long mainIndex, auxIndex;
    if (_Find (obj, mainIndex, auxIndex))
        return FALSE;
        
    MainSequence* sq;
    if (! (*_data) [mainIndex]) {
        (*_data) [mainIndex] = sq = new MainSequence;
        _usedBuckets++;
    }
    else 
        sq = (MainSequence*) (*_data)[mainIndex];
    if (!PrepareToChange())
        return FALSE;
    Node* node = new Node (CL_Basics<BaseType>::MakeCopy (obj), mainIndex);
    sq->Add (node);
    _size++;
    Notify ();
    return TRUE;
}


template <class BaseType>
bool CL_HashTable<BaseType>::Remove (const BaseType& obj)
{
    if (!PrepareToChange())
        return FALSE;
    long mainIndex, auxIndex;
    if (!_Find (obj, mainIndex, auxIndex))
        return FALSE;
        
    if ((*_data) [mainIndex]) {
        MainSequence& sq = *(MainSequence*) (*_data)[mainIndex];
        Node* node = (Node*) sq (auxIndex);
        sq.Remove (auxIndex);
        delete node;
        if (sq.Size() <= 0) {
            delete (MainSequence*) (*_data)[mainIndex];
            (*_data)[mainIndex] = 0;
            _usedBuckets--;
        }
        _size--;
        Notify ();
        return TRUE;
    }
    return FALSE;
}



template <class BaseType>
void CL_HashTable<BaseType>::WriteStatsTo (ostream& strm) const
{
    long i = _data->Size() - 1, totalSize = 0;
    for (; i >= 0; i--)
        if ((*_data)[i])
            totalSize += ((MainSequence*) (*_data)[i])->Size();
    strm << "CL_HashTable stats: usedBuckets " << _usedBuckets <<
        " ave. bucket size " << ((float) totalSize) / _usedBuckets << endl;
}





// -----------------------------------------------------------------

template <class Key, class Value>
CL_HashMap<Key,Value>::CL_HashMap ()
: _table (&_fn)
{
}



template <class Key, class Value>
bool CL_HashMap<Key,Value>::Add (const Key& key, const Value& value)
{
    return _table.Add (CL_MapAssoc<Key,Value> (key, value));
}


template <class Key, class Value>
bool CL_HashMap<Key,Value>::Remove (const Key& key)
{
    return _table.Remove (CL_MapAssoc<Key,Value>
                          (key, CL_Basics<Value>::NullValue()));
}


template <class Key, class Value>
bool CL_HashMap<Key,Value>::Includes (const Key& key) const
{
    return _table.Includes (CL_MapAssoc<Key,Value>
                            (key, CL_Basics<Value>::NullValue()));
}



template <class Key, class Value>
Value& CL_HashMap<Key,Value>::operator[] (const Key& key)
{
    CL_MapAssoc<Key,Value>& assoc = _table.Find
        (CL_MapAssoc<Key,Value> (key, CL_Basics<Value>::NullValue()));
    return assoc.value;
}









// ---------------------------------------------------------------------


template <class BaseType>
CL_HashTableIterator<BaseType>::CL_HashTableIterator
    (const CL_HashTable<BaseType>& tbl)
: _table (tbl)
{
    Reset ();
}


template <class BaseType>
CL_HashTableIterator<BaseType>::~CL_HashTableIterator ()
{
}


template <class BaseType>
void CL_HashTableIterator<BaseType>::Reset ()
{
    _mainIndex = _auxIndex = _objRank = 0;
    CL_SegmentedSequence& data = *(CL_SegmentedSequence*) _table._data;
    long n = data.Size();
    while (_mainIndex < n && !data[_mainIndex])
        _mainIndex++;
}



template <class BaseType>
bool CL_HashTableIterator<BaseType>::More ()
{
    return _objRank < _table._size;
}


template <class BaseType>
const BaseType& CL_HashTableIterator<BaseType>::Next ()
{
    if (_objRank >= _table._size)
        return _table._nullValue;
    CL_SegmentedSequence& data = *(CL_SegmentedSequence*) _table._data;
    long n = data.Size();
    MainSequence* s = (MainSequence*) data[_mainIndex];
    if (!s || _auxIndex >= s->Size()) {
        // Hunt for next nonempty bucket
        _mainIndex++;
        for (; _mainIndex < n; _mainIndex++) {
            s = (MainSequence*) data[_mainIndex];
            if (s)
                break;
        }
        if (!s)
            return _table._nullValue;
        _auxIndex = 0;
    }
    Node* pNode = (Node*) (*s)(_auxIndex);
    _auxIndex++;
    _objRank++;
    return CL_Basics<BaseType>::Deref (pNode->p);
};
    
        


#if (__BORLANDC__ >= 0x0500)
class YACL_BASE CL_HashFunction<CL_String> {

public:
    long operator() (CL_VoidPtr p);
    
};



class YACL_BASE CL_HashFunction<CL_VoidPtr> {

public:
    long operator() (CL_VoidPtr p);
    
};

#endif

long CL_HashFunction<CL_String>::operator() (CL_VoidPtr p)
{
    CL_String* s = (CL_String*) p;
    if (!s)
        return 0;
    long h = 0;
    char* ptr = (char*) s->AsPtr();
    long n = s->Size();
    for (long i = 0; i < n; i += 4) {
        long t = 0;
        for (short j = 0; j < 4; j++) {
            if (i + j < n)
                t = (t << 8) + ptr[j];
            else
                break;
        }
        ptr += 4;
        h += t;
    }
    return h;
}
        

#endif /* _htbl_imp_cxx_ */
