root/src/bin/bfs_tools/lib/Hashtable.cpp
/* Hashtable - a general purpose hash table
**
** Copyright 2001 pinc Software. All Rights Reserved.
** Released under the terms of the MIT license.
*/


#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include "Hashtable.h"


/************************** standard string hash functions **************************/


unsigned int stringHash(const char *c)
{
        int len = strlen(c);

        return(*(int *)(c + len - 4));  // erstmal zum Testen
}


bool stringCompare(const char *a, const char *b)
{
        return(!strcmp(a, b));
}


// #pragma mark - Hashtable


Hashtable::Hashtable(int capacity, float loadFactor)
        :
        fIteratorIndex(-1)
{
        if (capacity < 0 || loadFactor <= 0)
                return;

        if (!capacity)
                capacity = 1;

        if (!(fTable = (struct Entry **)malloc(capacity * sizeof(void *))))
                return;
        memset(fTable,0,capacity * sizeof(void *));

        fThreshold = (int)(capacity * loadFactor);
        fModCount = 0;
        fLoadFactor = loadFactor;
        fCapacity = capacity;

        fHashFunc = (uint32 (*)(const void *))stringHash;
        fCompareFunc = (bool (*)(const void *, const void *))stringCompare;
}


Hashtable::~Hashtable()
{
        struct Entry **table = fTable;

        for(int32 index = fCapacity;--index >= 0;)
        {
                struct Entry *entry,*next;

                for(entry = table[index];entry;entry = next)
                {
                        next = entry->next;
                        delete entry;
                }
        }
        free(table);
}


void Hashtable::SetHashFunction(uint32 (*func)(const void *))
{
        fHashFunc = func;
}


void Hashtable::SetCompareFunction(bool (*func)(const void *, const void *))
{
        fCompareFunc = func;
}


bool Hashtable::IsEmpty() const
{
        return fCount == 0;
}


bool Hashtable::ContainsKey(const void *key)
{
        return GetHashEntry(key) ? true : false;
}


void *Hashtable::GetValue(const void *key)
{
        Entry *entry = GetHashEntry(key);

        return entry ? entry->value : NULL;
}


bool Hashtable::Put(const void *key, void *value)
{
        Entry *entry = GetHashEntry(key);
        int hash = fHashFunc(key);
        int index;

        if (entry)
                return true;

        fModCount++;
        if (fCount >= fThreshold)
                Rehash();

        index = hash % fCapacity;

        fTable[index] = new Entry(fTable[index], key, value);
        fCount++;

        return true;
}


void *Hashtable::Remove(const void *key)
{
        Entry **table,*entry,*prev;
        uint32 hash,(*func)(const void *);
        int32 index;

        table = fTable;
        hash = (func = fHashFunc)(key);
        index = hash % fCapacity;

        for(entry = table[index],prev = NULL;entry;entry = entry->next)
        {
                if ((func(entry->key) == hash) && fCompareFunc(entry->key,key))
                {
                        void *value;

                        fModCount++;
                        if (prev)
                                prev->next = entry->next;
                        else
                                table[index] = entry->next;

                        fCount--;
                        value = entry->value;
                        delete entry;

                        return value;
                }
                prev = entry;
        }
        return NULL;
}


status_t Hashtable::GetNextEntry(void **value)
{
        if (fIteratorIndex == -1)
        {
                fIteratorIndex = 0;
                fIteratorEntry = fTable[0];
        }
        else if (fIteratorEntry)
                fIteratorEntry = fIteratorEntry->next;

        // get next filled slot
        while (!fIteratorEntry && fIteratorIndex + 1 < fCapacity)
                fIteratorEntry = fTable[++fIteratorIndex];

        if (fIteratorEntry)
        {
                *value = fIteratorEntry->value;
                return B_OK;
        }

        return B_ENTRY_NOT_FOUND;
}


void Hashtable::Rewind()
{
        fIteratorIndex = -1;
}


void
Hashtable::MakeEmpty(int8 keyMode,int8 valueMode)
{
        fModCount++;

        for (int32 index = fCapacity; --index >= 0;) {
                Entry *entry, *next;

                for (entry = fTable[index]; entry; entry = next) {
                        switch (keyMode) {
                                case HASH_EMPTY_DELETE:
                                        // TODO: destructors are not called!
                                        delete (void*)entry->key;
                                        break;
                                case HASH_EMPTY_FREE:
                                        free((void*)entry->key);
                                        break;
                        }
                        switch (valueMode) {
                                case HASH_EMPTY_DELETE:
                                        // TODO: destructors are not called!
                                        delete entry->value;
                                        break;
                                case HASH_EMPTY_FREE:
                                        free(entry->value);
                                        break;
                        }
                        next = entry->next;
                        delete entry;
                }
                fTable[index] = NULL;
        }
        fCount = 0;
}


size_t
Hashtable::Size() const
{
        return fCount;
}


/** The hash table will be doubled in size, and rebuild.
 *  @return true on success
 */

bool Hashtable::Rehash()
{
        uint32 (*hashCode)(const void *) = fHashFunc;
        struct Entry **oldTable = fTable,**newtable;
        int oldCapacity = fCapacity;
        int newCapacity,i;

        newCapacity = oldCapacity * 2 + 1;
        if (!(newtable = (struct Entry **)malloc(newCapacity * sizeof(struct Entry *))))
                return false;
        memset(newtable,0,newCapacity*sizeof(struct Entry *));

        fModCount++;
        fThreshold = (int)(newCapacity * fLoadFactor);
        fTable = newtable;
        fCapacity = newCapacity;

        for (i = oldCapacity;i-- > 0;) {
                Entry *oldEntry,*entry = NULL;
                int index;

                for (oldEntry = oldTable[i];oldEntry;) {
                        entry = oldEntry;  oldEntry = oldEntry->next;

                        index = hashCode(entry->key) % newCapacity;
                        entry->next = newtable[index];
                        newtable[index] = entry;
                }
        }
        free(oldTable);

        return true;
}


Hashtable::Entry *Hashtable::GetHashEntry(const void *key)
{
        Entry **table,*entry;
        uint32 hash,(*func)(const void *);

        table = fTable;
        hash = (func = fHashFunc)(key);

        for(entry = table[hash % fCapacity];entry;entry = entry->next)
        {
                if ((func(entry->key) == hash) && fCompareFunc(entry->key,key))
                        return entry;
        }
        return NULL;
}