// // SimpleHashTable.h // // $Id: //poco/1.2/Foundation/include/Poco/SimpleHashTable.h#2 $ // // Library: Foundation // Package: Core // Module: SimpleHashTable // // Definition of the SimpleHashTable class. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #ifndef Foundation_SimpleHashTable_INCLUDED #define Foundation_SimpleHashTable_INCLUDED #include "Poco/Foundation.h" #include "Poco/Exception.h" #include "Poco/HashFunction.h" #include "Poco/HashStatistic.h" #include #include #include namespace Poco { template< class Key, class Value, class KeyHashFunction = HashFunction< Key> > class SimpleHashTable /// A SimpleHashTable stores a key value pair that can be looked up via a hashed key. /// /// In comparision to a HashTable, this class handles collisions by sequentially searching the next /// free location. This also means that the maximum size of this table is limited, i.e. if the hash table /// is full, it will throw an exception and that this class does not support remove operations. /// On the plus side it is faster than the HashTable. /// /// This class is NOT thread safe. { public: class HashEntry { public: Key key; Value value; HashEntry(const Key k, const Value v): key(k), value(v) { } }; typedef HashEntry** HashTableVector; SimpleHashTable(UInt32 initialSize = 251): _entries(0), _size(0), _maxCapacity(initialSize) /// Creates the SimpleHashTable. { _entries = new HashEntry*[initialSize]; memset(_entries, '\0', sizeof(HashEntry*)*initialSize); } SimpleHashTable(const SimpleHashTable& ht): _entries (new HashEntry*[ht._maxCapacity]), _size(ht._size), _maxCapacity(ht._maxCapacity) { for (int i = 0; i < _maxCapacity; ++i) { if (ht._entries[i]) _entries[i] = new HashEntry(*ht._entries[i]); else _entries[i] = 0; } } ~SimpleHashTable() /// Destroys the SimpleHashTable. { clear(); } SimpleHashTable& operator = (const SimpleHashTable& ht) { if (this != &ht) { clear(); _maxCapacity = ht._maxCapacity; delete[] _entries; _entries = new HashEntry*[_maxCapacity]; _size = ht._size; for (int i = 0; i < _maxCapacity; ++i) { if (ht._entries[i]) _entries[i] = new HashEntry(*ht._entries[i]); else _entries[i] = 0; } } return *this; } void clear() { if (!_entries) return; for (int i = 0; i < _maxCapacity; ++i) { if (_entries[i]) delete _entries[i]; } delete[] _entries; _entries = 0; _size = 0; _maxCapacity = 0; } UInt32 insert(const Key& key, const Value& value) /// Returns the hash value of the inserted item. /// Throws an exception if the entry was already inserted { UInt32 hsh = hash(key); insertRaw(key, hsh, value); return hsh; } void insertRaw(const Key& key, UInt32 hsh, const Value& value) /// Returns the hash value of the inserted item. /// Throws an exception if the entry was already inserted { if (!_entries[hsh]) _entries[hsh] = new HashEntry(key, value); else { UInt32 origHash = hsh; while (_entries[hsh % _maxCapacity]) { poco_assert_dbg(_entries[hsh % _maxCapacity]->key != key); if (hsh - origHash > _maxCapacity) throw PoolOverflowException("SimpleHashTable full"); hsh++; } _entries[hsh % _maxCapacity] = new HashEntry(key, value); } _size++; } UInt32 update(const Key& key, const Value& value) /// Returns the hash value of the inserted item. /// Replaces an existing entry if it finds one { UInt32 hsh = hash(key); updateRaw(key, hsh, value); return hsh; } void updateRaw(const Key& key, UInt32 hsh, const Value& value) /// Returns the hash value of the inserted item. /// Replaces an existing entry if it finds one { if (!_entries[hsh]) _entries[hsh] = new HashEntry(key, value); else { UInt32 origHash = hsh; while (_entries[hsh % _maxCapacity]) { if (_entries[hsh % _maxCapacity]->key == key) { _entries[hsh % _maxCapacity]->value = value; return; } if (hsh - origHash > _maxCapacity) throw PoolOverflowException("SimpleHashTable full"); hsh++; } _entries[hsh % _maxCapacity] = new HashEntry(key, value); } _size++; } UInt32 hash(const Key& key) const { return KeyHashFunction::hash(key, _maxCapacity); } const Value& get(const Key& key) const /// Throws an exception if the value does not exist { UInt32 hsh = hash(key); return getRaw(key, hsh); } const Value& getRaw(const Key& key, UInt32 hsh) const /// Throws an exception if the value does not exist { UInt32 origHash = hsh; while (true) { if (_entries[hsh % _maxCapacity]) { if (_entries[hsh % _maxCapacity]->key == key) { return _entries[hsh % _maxCapacity]->value; } } else throw InvalidArgumentException("value not found"); if (hsh - origHash > _maxCapacity) throw InvalidArgumentException("value not found"); hsh++; } } const Key& getKeyRaw(const Key& key, UInt32 hsh) /// Throws an exception if the key does not exist. returns a reference to the internally /// stored key. Useful when someone does an insert and wants for performance reason only to store /// a pointer to the key in another collection { UInt32 origHash = hsh; while (true) { if (_entries[hsh % _maxCapacity]) { if (_entries[hsh % _maxCapacity]->key == key) { return _entries[hsh % _maxCapacity]->key; } } else throw InvalidArgumentException("key not found"); if (hsh - origHash > _maxCapacity) throw InvalidArgumentException("key not found"); hsh++; } } bool get(const Key& key, Value& v) const /// Sets v to the found value, returns false if no value was found { UInt32 hsh = hash(key); return getRaw(key, hsh, v); } bool getRaw(const Key& key, UInt32 hsh, Value& v) const /// Sets v to the found value, returns false if no value was found { UInt32 origHash = hsh; while (true) { if (_entries[hsh % _maxCapacity]) { if (_entries[hsh % _maxCapacity]->key == key) { v = _entries[hsh % _maxCapacity]->value; return true; } } else return false; if (hsh - origHash > _maxCapacity) return false; hsh++; } } bool exists(const Key& key) { UInt32 hsh = hash(key); return existsRaw(key, hsh); } bool existsRaw(const Key& key, UInt32 hsh) { UInt32 origHash = hsh; while (true) { if (_entries[hsh % _maxCapacity]) { if (_entries[hsh % _maxCapacity]->key == key) { return true; } } else return false; if (hsh - origHash > _maxCapacity) return false; hsh++; } } size_t size() const /// Returns the number of elements already inserted into the SimpleHashTable { return _size; } UInt32 maxCapacity() const { return _maxCapacity; } void resize(UInt32 newSize) /// Resizes the hashtable, rehashes all existing entries. Expensive! { if (_maxCapacity != newSize) { HashTableVector cpy = _entries; _entries = 0; UInt32 oldSize = _maxCapacity; _maxCapacity = newSize; _entries = new HashEntry*[_maxCapacity]; memset(_entries, '\0', sizeof(HashEntry*)*_maxCapacity); if (_size == 0) { // no data was yet inserted delete[] cpy; return; } _size = 0; for (int i=0; i < oldSize; ++i) { if (cpy[i]) { insert(cpy[i]->key, cpy[i]->value); delete cpy[i]; } } delete[] cpy; } } HashStatistic currentState(bool details = false) const /// Returns the current internal state { UInt32 numberOfEntries = (UInt32)_size; UInt32 numZeroEntries = 0; UInt32 maxEntriesPerHash = 0; std::vector detailedEntriesPerHash; #ifdef _DEBUG UInt32 totalSize = 0; #endif for (int i=0; i < _maxCapacity; ++i) { if (_entries[i]) { maxEntriesPerHash = 1; UInt32 size = 1; if (details) detailedEntriesPerHash.push_back(size); #ifdef DEBUG totalSize += size; #endif } else { numZeroEntries++; if (details) detailedEntriesPerHash.push_back(0); } } #ifdef DEBUG poco_assert_dbg(totalSize == numberOfEntries); #endif return HashStatistic(_maxCapacity, numberOfEntries, numZeroEntries, maxEntriesPerHash, detailedEntriesPerHash); } private: HashTableVector _entries; size_t _size; UInt32 _maxCapacity; }; } // namespace Poco #endif // Foundation_HashTable_INCLUDED