| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- //
- // LinearHashTable.h
- //
- // $Id: //poco/1.4/Foundation/include/Poco/LinearHashTable.h#1 $
- //
- // Library: Foundation
- // Package: Hashing
- // Module: LinearHashTable
- //
- // Definition of the LinearHashTable class.
- //
- // Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
- // and Contributors.
- //
- // SPDX-License-Identifier: BSL-1.0
- //
- #ifndef Foundation_LinearHashTable_INCLUDED
- #define Foundation_LinearHashTable_INCLUDED
- #include "Poco/Foundation.h"
- #include "Poco/Hash.h"
- #include <functional>
- #include <algorithm>
- #include <vector>
- #include <utility>
- #include <cstddef>
- namespace Poco {
- template <class Value, class HashFunc = Hash<Value> >
- class LinearHashTable
- /// This class implements a linear hash table.
- ///
- /// In a linear hash table, the available address space
- /// grows or shrinks dynamically. A linar hash table thus
- /// supports any number of insertions or deletions without
- /// lookup or insertion performance deterioration.
- ///
- /// Linear hashing was discovered by Witold Litwin in 1980
- /// and described in the paper LINEAR HASHING: A NEW TOOL FOR FILE AND TABLE ADDRESSING.
- ///
- /// For more information on linear hashing, see <http://en.wikipedia.org/wiki/Linear_hash>.
- ///
- /// The LinearHashTable is not thread safe.
- ///
- /// Value must support comparison for equality.
- ///
- /// Find, insert and delete operations are basically O(1) with regard
- /// to the total number of elements in the table, and O(N) with regard
- /// to the number of elements in the bucket where the element is stored.
- /// On average, every bucket stores one element; the exact number depends
- /// on the quality of the hash function. In most cases, the maximum number of
- /// elements in a bucket should not exceed 3.
- {
- public:
- typedef Value ValueType;
- typedef Value& Reference;
- typedef const Value& ConstReference;
- typedef Value* Pointer;
- typedef const Value* ConstPointer;
- typedef HashFunc Hash;
- typedef std::vector<Value> Bucket;
- typedef std::vector<Bucket> BucketVec;
- typedef typename Bucket::iterator BucketIterator;
- typedef typename BucketVec::iterator BucketVecIterator;
- class ConstIterator: public std::iterator<std::forward_iterator_tag, Value>
- {
- public:
- ConstIterator(): _initialized(false)
- {
- }
-
- ConstIterator(const BucketVecIterator& vecIt, const BucketVecIterator& endIt, const BucketIterator& buckIt):
- _vecIt(vecIt),
- _endIt(endIt),
- _buckIt(buckIt),
- _initialized(true)
- {
- }
- ConstIterator(const ConstIterator& it):
- _vecIt(it._vecIt),
- _endIt(it._endIt),
- _buckIt(it._buckIt),
- _initialized(it._initialized)
- {
- }
-
- ConstIterator& operator = (const ConstIterator& it)
- {
- ConstIterator tmp(it);
- swap(tmp);
- return *this;
- }
-
- void swap(ConstIterator& it)
- {
- using std::swap;
- // uninitialized iterators crash when swapped
- if (_initialized)
- {
- swap(_vecIt, it._vecIt);
- swap(_endIt, it._endIt);
- swap(_buckIt, it._buckIt);
- swap(_initialized, it._initialized);
- }
- else
- {
- _vecIt = it._vecIt;
- _endIt = it._endIt;
- _buckIt = it._buckIt;
- _initialized = it._initialized;
- }
- }
-
- bool operator == (const ConstIterator& it) const
- {
- return _vecIt == it._vecIt && (_vecIt == _endIt || _buckIt == it._buckIt);
- }
- bool operator != (const ConstIterator& it) const
- {
- return _vecIt != it._vecIt || (_vecIt != _endIt && _buckIt != it._buckIt);
- }
-
- const typename Bucket::value_type& operator * () const
- {
- return *_buckIt;
- }
- const typename Bucket::value_type* operator -> () const
- {
- return &*_buckIt;
- }
-
- ConstIterator& operator ++ () // prefix
- {
- if (_vecIt != _endIt)
- {
- ++_buckIt;
- while (_vecIt != _endIt && _buckIt == _vecIt->end())
- {
- ++_vecIt;
- if (_vecIt != _endIt) _buckIt = _vecIt->begin();
- }
- }
- return *this;
- }
-
- ConstIterator operator ++ (int) // postfix
- {
- ConstIterator tmp(*this);
- ++*this;
- return tmp;
- }
-
- protected:
- BucketVecIterator _vecIt;
- BucketVecIterator _endIt;
- BucketIterator _buckIt;
- bool _initialized;
-
- friend class LinearHashTable;
- };
-
- class Iterator: public ConstIterator
- {
- public:
- Iterator()
- {
- }
-
- Iterator(const BucketVecIterator& vecIt, const BucketVecIterator& endIt, const BucketIterator& buckIt):
- ConstIterator(vecIt, endIt, buckIt)
- {
- }
- Iterator(const Iterator& it):
- ConstIterator(it)
- {
- }
-
- Iterator& operator = (const Iterator& it)
- {
- Iterator tmp(it);
- ConstIterator::swap(tmp);
- return *this;
- }
-
- void swap(Iterator& it)
- {
- ConstIterator::swap(it);
- }
-
- typename Bucket::value_type& operator * ()
- {
- return *this->_buckIt;
- }
- const typename Bucket::value_type& operator * () const
- {
- return *this->_buckIt;
- }
- typename Bucket::value_type* operator -> ()
- {
- return &*this->_buckIt;
- }
- const typename Bucket::value_type* operator -> () const
- {
- return &*this->_buckIt;
- }
-
- Iterator& operator ++ () // prefix
- {
- ConstIterator::operator ++ ();
- return *this;
- }
-
- Iterator operator ++ (int) // postfix
- {
- Iterator tmp(*this);
- ++*this;
- return tmp;
- }
- friend class LinearHashTable;
- };
-
- LinearHashTable(std::size_t initialReserve = 64):
- _split(0),
- _front(1),
- _size(0)
- /// Creates the LinearHashTable, using the given initialReserve.
- {
- _buckets.reserve(calcSize(initialReserve));
- _buckets.push_back(Bucket());
- }
-
- LinearHashTable(const LinearHashTable& table):
- _buckets(table._buckets),
- _split(table._split),
- _front(table._front),
- _size(table._size)
- /// Creates the LinearHashTable by copying another one.
- {
- }
-
- ~LinearHashTable()
- /// Destroys the LinearHashTable.
- {
- }
-
- LinearHashTable& operator = (const LinearHashTable& table)
- /// Assigns another LinearHashTable.
- {
- LinearHashTable tmp(table);
- swap(tmp);
- return *this;
- }
-
- void swap(LinearHashTable& table)
- /// Swaps the LinearHashTable with another one.
- {
- using std::swap;
- swap(_buckets, table._buckets);
- swap(_split, table._split);
- swap(_front, table._front);
- swap(_size, table._size);
- }
-
- ConstIterator begin() const
- /// Returns an iterator pointing to the first entry, if one exists.
- {
- BucketVecIterator it(_buckets.begin());
- BucketVecIterator end(_buckets.end());
- while (it != end && it->empty())
- {
- ++it;
- }
- if (it == end)
- return this->end();
- else
- return ConstIterator(it, end, it->begin());
- }
-
- ConstIterator end() const
- /// Returns an iterator pointing to the end of the table.
- {
- return ConstIterator(_buckets.end(), _buckets.end(), _buckets.front().end());
- }
-
- Iterator begin()
- /// Returns an iterator pointing to the first entry, if one exists.
- {
- BucketVecIterator it(_buckets.begin());
- BucketVecIterator end(_buckets.end());
- while (it != end && it->empty())
- {
- ++it;
- }
- if (it == end)
- return this->end();
- else
- return Iterator(it, end, it->begin());
- }
-
- Iterator end()
- /// Returns an iterator pointing to the end of the table.
- {
- return Iterator(_buckets.end(), _buckets.end(), _buckets.front().end());
- }
-
- ConstIterator find(const Value& value) const
- /// Finds an entry in the table.
- {
- std::size_t addr = bucketAddress(value);
- BucketVecIterator it(_buckets.begin() + addr);
- BucketIterator buckIt(std::find(it->begin(), it->end(), value));
- if (buckIt != it->end())
- return ConstIterator(it, _buckets.end(), buckIt);
- else
- return end();
- }
- Iterator find(const Value& value)
- /// Finds an entry in the table.
- {
- std::size_t addr = bucketAddress(value);
- BucketVecIterator it(_buckets.begin() + addr);
- BucketIterator buckIt(std::find(it->begin(), it->end(), value));
- if (buckIt != it->end())
- return Iterator(it, _buckets.end(), buckIt);
- else
- return end();
- }
-
- std::size_t count(const Value& value) const
- /// Returns the number of elements with the given
- /// value, with is either 1 or 0.
- {
- return find(value) != end() ? 1 : 0;
- }
-
- std::pair<Iterator, bool> insert(const Value& value)
- /// Inserts an element into the table.
- ///
- /// If the element already exists in the table,
- /// a pair(iterator, false) with iterator pointing to the
- /// existing element is returned.
- /// Otherwise, the element is inserted an a
- /// pair(iterator, true) with iterator
- /// pointing to the new element is returned.
- {
- std::size_t hash = _hash(value);
- std::size_t addr = bucketAddressForHash(hash);
- BucketVecIterator it(_buckets.begin() + addr);
- BucketIterator buckIt(std::find(it->begin(), it->end(), value));
- if (buckIt == it->end())
- {
- split();
- addr = bucketAddressForHash(hash);
- it = _buckets.begin() + addr;
- buckIt = it->insert(it->end(), value);
- ++_size;
- return std::make_pair(Iterator(it, _buckets.end(), buckIt), true);
- }
- else
- {
- return std::make_pair(Iterator(it, _buckets.end(), buckIt), false);
- }
- }
-
- void erase(Iterator it)
- /// Erases the element pointed to by it.
- {
- if (it != end())
- {
- it._vecIt->erase(it._buckIt);
- --_size;
- merge();
- }
- }
-
- void erase(const Value& value)
- /// Erases the element with the given value, if it exists.
- {
- Iterator it = find(value);
- erase(it);
- }
-
- void clear()
- /// Erases all elements.
- {
- LinearHashTable empty;
- swap(empty);
- }
-
- std::size_t size() const
- /// Returns the number of elements in the table.
- {
- return _size;
- }
-
- bool empty() const
- /// Returns true iff the table is empty.
- {
- return _size == 0;
- }
-
- std::size_t buckets() const
- /// Returns the number of allocated buckets.
- {
- return _buckets.size();
- }
-
- protected:
- std::size_t bucketAddress(const Value& value) const
- {
- std::size_t n = _hash(value);
- if (n % _front >= _split)
- return n % _front;
- else
- return n % (2*_front);
- }
-
- std::size_t bucketAddressForHash(std::size_t hash)
- {
- if (hash % _front >= _split)
- return hash % _front;
- else
- return hash % (2*_front);
- }
-
- void split()
- {
- if (_split == _front)
- {
- _split = 0;
- _front *= 2;
- _buckets.reserve(_front*2);
- }
- Bucket tmp;
- _buckets.push_back(tmp);
- _buckets[_split].swap(tmp);
- ++_split;
- for (BucketIterator it = tmp.begin(); it != tmp.end(); ++it)
- {
- using std::swap;
- std::size_t addr = bucketAddress(*it);
- _buckets[addr].push_back(Value());
- swap(*it, _buckets[addr].back());
- }
- }
-
- void merge()
- {
- if (_split == 0)
- {
- _front /= 2;
- _split = _front;
- }
- --_split;
- Bucket tmp;
- tmp.swap(_buckets.back());
- _buckets.pop_back();
- for (BucketIterator it = tmp.begin(); it != tmp.end(); ++it)
- {
- using std::swap;
- std::size_t addr = bucketAddress(*it);
- _buckets[addr].push_back(Value());
- swap(*it, _buckets[addr].back());
- }
- }
-
- static std::size_t calcSize(std::size_t initialSize)
- {
- std::size_t size = 32;
- while (size < initialSize) size *= 2;
- return size;
- }
-
- private:
- // Evil hack: _buckets must be mutable because both ConstIterator and Iterator hold
- // ordinary iterator's (not const_iterator's).
- mutable BucketVec _buckets;
- std::size_t _split;
- std::size_t _front;
- std::size_t _size;
- HashFunc _hash;
- };
- } // namespace Poco
- #endif // Foundation_LinearHashTable_INCLUDED
|