root/headers/private/util/MultiHashTable.h
/*
 * Copyright 2007, Hugo Santos. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *      Hugo Santos, hugosantos@gmail.com
 */
#ifndef _KERNEL_UTIL_MULTI_HASH_TABLE_H
#define _KERNEL_UTIL_MULTI_HASH_TABLE_H


#include <KernelExport.h>
#include <util/kernel_cpp.h>
#include <util/OpenHashTable.h>


// MultiHashTable is a container which acts a bit like multimap<>
// but with hash table semantics.

// refer to OpenHashTable.h for how to use this container.

template<typename Definition, bool AutoExpand = true,
        bool CheckDuplicates = false>
class MultiHashTable : private BOpenHashTable<Definition,
        AutoExpand, CheckDuplicates> {
public:
        typedef BOpenHashTable<Definition, AutoExpand, CheckDuplicates> HashTable;
        typedef MultiHashTable<Definition, AutoExpand, CheckDuplicates> MultiTable;

        typedef typename HashTable::Iterator Iterator;
        typedef typename Definition::KeyType KeyType;
        typedef typename Definition::ValueType ValueType;

        MultiHashTable()
                : HashTable() {}

        MultiHashTable(const Definition& definition)
                : HashTable(definition) {}

        status_t Init(size_t initialSize = HashTable::kMinimumSize)
        {
                return HashTable::Init(initialSize);
        }

        void Insert(ValueType *value)
        {
                if (AutoExpand
                        && HashTable::fItemCount >= (HashTable::fTableSize * 200 / 256))
                        _Resize(HashTable::fTableSize * 2);

                InsertUnchecked(value);
        }

        void InsertUnchecked(ValueType *value)
        {
                _Insert(HashTable::fTable, HashTable::fTableSize, value);
                HashTable::fItemCount++;
        }

        bool Remove(ValueType *value)
        {
                if (!HashTable::RemoveUnchecked(value))
                        return false;

                if (AutoExpand && HashTable::fTableSize > HashTable::kMinimumSize
                        && HashTable::fItemCount < (HashTable::fTableSize * 50 / 256))
                        _Resize(HashTable::fTableSize / 2);

                return true;
        }

        Iterator GetIterator() const { return HashTable::GetIterator(); }

        class ValueIterator : protected Iterator {
        public:
                ValueIterator(const HashTable *table, size_t index, ValueType *value)
                        : fOriginalIndex(index), fOriginalValue(value)
                {
                        Iterator::fTable = table;
                        Rewind();
                }

                bool HasNext() const
                {
                        if (Iterator::fNext == NULL)
                                return false;
                        if (Iterator::fNext == fOriginalValue)
                                return true;
                        return ((const MultiTable *)Iterator::fTable)->_Definition().CompareValues(
                                fOriginalValue, Iterator::fNext);
                }

                void Rewind()
                {
                        Iterator::fIndex = fOriginalIndex + 1;
                        Iterator::fNext = fOriginalValue;
                }

                ValueType *Next() { return Iterator::Next(); }

        private:
                size_t fOriginalIndex;
                ValueType *fOriginalValue;
        };

        ValueIterator Lookup(const KeyType &key) const
        {
                size_t index = 0;
                ValueType *slot = NULL;
                if (HashTable::fTableSize > 0) {
                        index = HashTable::fDefinition.HashKey(key)
                                & (HashTable::fTableSize - 1);
                        slot = HashTable::fTable[index];
                }

                while (slot) {
                        if (HashTable::fDefinition.Compare(key, slot))
                                break;
                        slot = HashTable::_Link(slot);
                }

                if (slot == NULL)
                        return ValueIterator(this, HashTable::fTableSize, NULL);

                return ValueIterator(this, index, slot);
        }

private:
        // for g++ 2.95
        friend class ValueIterator;

        const Definition &_Definition() const { return HashTable::fDefinition; }

        void _Insert(ValueType **table, size_t tableSize, ValueType *value)
        {
                size_t index = HashTable::fDefinition.Hash(value) & (tableSize - 1);

                ValueType *previous;

                // group values with the same key
                for (previous = table[index]; previous
                                && !HashTable::fDefinition.CompareValues(previous, value);
                                previous = HashTable::_Link(previous));

                if (previous) {
                        HashTable::_Link(value) = HashTable::_Link(previous);
                        HashTable::_Link(previous) = value;
                } else {
                        HashTable::_Link(value) = table[index];
                        table[index] = value;
                }
        }

        // TODO use BOpenHashTable's _Resize
        bool _Resize(size_t newSize)
        {
                ValueType **newTable = new ValueType *[newSize];
                if (newTable == NULL)
                        return false;

                for (size_t i = 0; i < newSize; i++)
                        newTable[i] = NULL;

                if (HashTable::fTable) {
                        for (size_t i = 0; i < HashTable::fTableSize; i++) {
                                ValueType *bucket = HashTable::fTable[i];
                                while (bucket) {
                                        ValueType *next = HashTable::_Link(bucket);
                                        _Insert(newTable, newSize, bucket);
                                        bucket = next;
                                }
                        }

                        delete [] HashTable::fTable;
                }

                HashTable::fTableSize = newSize;
                HashTable::fTable = newTable;
                return true;
        }
};

#endif  // _KERNEL_UTIL_MULTI_HASH_TABLE_H