+++ /dev/null
-/*
-Copyright (C) 2001-2006, William Joseph.
-All Rights Reserved.
-
-This file is part of GtkRadiant.
-
-GtkRadiant is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-GtkRadiant is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GtkRadiant; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#if !defined(INCLUDED_CONTAINER_HASHTABLE_H)
-#define INCLUDED_CONTAINER_HASHTABLE_H
-
-#include <cstddef>
-#include <algorithm>
-#include <functional>
-#include "debugging/debugging.h"
-
-
-namespace HashTableDetail
-{
- inline std::size_t next_power_of_two(std::size_t size)
- {
- std::size_t result = 1;
- while(result < size)
- {
- result <<= 1;
- }
- return result;
- }
-
- struct BucketNodeBase
- {
- BucketNodeBase* next;
- BucketNodeBase* prev;
- };
-
- inline void list_initialise(BucketNodeBase& self)
- {
- self.next = self.prev = &self;
- }
-
- inline void list_swap(BucketNodeBase& self, BucketNodeBase& other)
- {
- BucketNodeBase tmp(self);
- if(other.next == &other)
- {
- list_initialise(self);
- }
- else
- {
- self = other;
- self.next->prev = self.prev->next = &self;
- }
- if(tmp.next == &self)
- {
- list_initialise(other);
- }
- else
- {
- other = tmp;
- other.next->prev = other.prev->next = &other;
- }
- }
-
- inline void node_link(BucketNodeBase* node, BucketNodeBase* next)
- {
- node->next = next;
- node->prev = next->prev;
- next->prev = node;
- node->prev->next = node;
- }
- inline void node_unlink(BucketNodeBase* node)
- {
- node->prev->next = node->next;
- node->next->prev = node->prev;
- }
-
- template<typename Key, typename Value>
- struct KeyValue
- {
- const Key key;
- Value value;
-
- KeyValue(const Key& key_, const Value& value_)
- : key(key_), value(value_)
- {
- }
- };
-
- template<typename Key, typename Value, typename Hash>
- struct BucketNode : public BucketNodeBase
- {
- Hash m_hash;
- KeyValue<Key, Value> m_value;
-
- BucketNode(Hash hash, const Key& key, const Value& value)
- : m_hash(hash), m_value(key, value)
- {
- }
- BucketNode* getNext() const
- {
- return static_cast<BucketNode*>(next);
- }
- BucketNode* getPrev() const
- {
- return static_cast<BucketNode*>(prev);
- }
- };
-
- template<typename Key, typename Value, typename Hash>
- class BucketIterator
- {
- typedef BucketNode<Key, Value, Hash> Node;
- Node* m_node;
-
- void increment()
- {
- m_node = m_node->getNext();
- }
-
- public:
- typedef std::forward_iterator_tag iterator_category;
- typedef std::ptrdiff_t difference_type;
- typedef difference_type distance_type;
- typedef KeyValue<Key, Value> value_type;
- typedef value_type* pointer;
- typedef value_type& reference;
-
- BucketIterator(Node* node) : m_node(node)
- {
- }
-
- Node* node()
- {
- return m_node;
- }
-
- bool operator==(const BucketIterator& other) const
- {
- return m_node == other.m_node;
- }
- bool operator!=(const BucketIterator& other) const
- {
- return !operator==(other);
- }
- BucketIterator& operator++()
- {
- increment();
- return *this;
- }
- BucketIterator operator++(int)
- {
- BucketIterator tmp = *this;
- increment();
- return tmp;
- }
- value_type& operator*() const
- {
- return m_node->m_value;
- }
- value_type* operator->() const
- {
- return &(operator*());
- }
- };
-}
-
-
-/// A hash-table container which maps keys to values.
-///
-/// - Inserting or removing elements does not invalidate iterators.
-/// - Inserting or retrieving an element for a given key takes O(1) time on average.
-/// - Elements are stored in no particular order.
-///
-/// \param Key Uniquely identifies a value. Must provide a copy-constructor.
-/// \param Value The value to be stored . Must provide a default-constructor and a copy-constructor.
-/// \param Hasher Must provide 'std::size_t operator()(const Key&) const' which always returns the same result if the same argument is given.
-/// \param KeyEqual Must provide 'bool operator==(const Key&, const Key&) const' which returns true only if both arguments are equal.
-///
-/// \dontinclude container/hashtable.cpp
-/// \skipline HashTable example
-/// \until end example
-template<typename Key, typename Value, typename Hasher, typename KeyEqual = std::equal_to<Key> >
-class HashTable : private KeyEqual, private Hasher
-{
- typedef typename Hasher::hash_type hash_type;
- typedef HashTableDetail::KeyValue<Key, Value> KeyValue;
- typedef HashTableDetail::BucketNode<Key, Value, hash_type> BucketNode;
-
- inline BucketNode* node_create(hash_type hash, const Key& key, const Value& value)
- {
- return new BucketNode(hash, key, value);
- }
- inline void node_destroy(BucketNode* node)
- {
- delete node;
- }
-
- typedef BucketNode* Bucket;
-
- static Bucket* buckets_new(std::size_t count)
- {
- Bucket* buckets = new Bucket[count];
- std::uninitialized_fill(buckets, buckets + count, Bucket(0));
- return buckets;
- }
- static void buckets_delete(Bucket* buckets)
- {
- delete[] buckets;
- }
-
- std::size_t m_bucketCount;
- Bucket* m_buckets;
- std::size_t m_size;
- HashTableDetail::BucketNodeBase m_list;
-
- BucketNode* getFirst()
- {
- return static_cast<BucketNode*>(m_list.next);
- }
- BucketNode* getLast()
- {
- return static_cast<BucketNode*>(&m_list);
- }
-
-public:
-
- typedef KeyValue value_type;
- typedef HashTableDetail::BucketIterator<Key, Value, hash_type> iterator;
-
-private:
-
- void initialise()
- {
- list_initialise(m_list);
- }
- hash_type hashKey(const Key& key)
- {
- return Hasher::operator()(key);
- }
-
- std::size_t getBucketId(hash_type hash) const
- {
- return hash & (m_bucketCount - 1);
- }
- Bucket& getBucket(hash_type hash)
- {
- return m_buckets[getBucketId(hash)];
- }
- BucketNode* bucket_find(Bucket bucket, hash_type hash, const Key& key)
- {
- std::size_t bucketId = getBucketId(hash);
- for(iterator i(bucket); i != end(); ++i)
- {
- hash_type nodeHash = i.node()->m_hash;
-
- if(getBucketId(nodeHash) != bucketId)
- {
- return 0;
- }
-
- if(nodeHash == hash && KeyEqual::operator()((*i).key, key))
- {
- return i.node();
- }
- }
- return 0;
- }
- BucketNode* bucket_insert(Bucket& bucket, BucketNode* node)
- {
- // link node into list
- node_link(node, bucket_next(bucket));
- bucket = node;
- return node;
- }
- BucketNode* bucket_next(Bucket& bucket)
- {
- Bucket* end = m_buckets + m_bucketCount;
- for(Bucket* i = &bucket; i != end; ++i)
- {
- if(*i != 0)
- {
- return *i;
- }
- }
- return getLast();
- }
-
- void buckets_resize(std::size_t count)
- {
- BucketNode* first = getFirst();
- BucketNode* last = getLast();
-
- buckets_delete(m_buckets);
-
- m_bucketCount = count;
-
- m_buckets = buckets_new(m_bucketCount);
- initialise();
-
- for(BucketNode* i = first; i != last;)
- {
- BucketNode* node = i;
- i = i->getNext();
- bucket_insert(getBucket((*node).m_hash), node);
- }
- }
- void size_increment()
- {
- if(m_size == m_bucketCount)
- {
- buckets_resize(m_bucketCount == 0 ? 8 : m_bucketCount << 1);
- }
- ++m_size;
- }
- void size_decrement()
- {
- --m_size;
- }
-
- HashTable(const HashTable& other);
- HashTable& operator=(const HashTable& other);
-public:
- HashTable() : m_bucketCount(0), m_buckets(0), m_size(0)
- {
- initialise();
- }
- HashTable(std::size_t bucketCount) : m_bucketCount(HashTableDetail::next_power_of_two(bucketCount)), m_buckets(buckets_new(m_bucketCount)), m_size(0)
- {
- initialise();
- }
- ~HashTable()
- {
- for(BucketNode* i = getFirst(); i != getLast();)
- {
- BucketNode* node = i;
- i = i->getNext();
- node_destroy(node);
- }
- buckets_delete(m_buckets);
- }
-
- iterator begin()
- {
- return iterator(getFirst());
- }
- iterator end()
- {
- return iterator(getLast());
- }
-
- bool empty() const
- {
- return m_size == 0;
- }
- std::size_t size() const
- {
- return m_size;
- }
-
- /// \brief Returns an iterator pointing to the value associated with \p key if it is contained by the hash-table, else \c end().
- iterator find(const Key& key)
- {
- hash_type hash = hashKey(key);
- if(m_bucketCount != 0)
- {
- Bucket bucket = getBucket(hash);
- if(bucket != 0)
- {
- BucketNode* node = bucket_find(bucket, hash, key);
- if(node != 0)
- {
- return iterator(node);
- }
- }
- }
-
- return end();
- }
- /// \brief Adds \p value to the hash-table associated with \p key if it does not exist.
- iterator insert(const Key& key, const Value& value)
- {
- hash_type hash = hashKey(key);
- if(m_bucketCount != 0)
- {
- Bucket& bucket = getBucket(hash);
- if(bucket != 0)
- {
- BucketNode* node = bucket_find(bucket, hash, key);
- if(node != 0)
- {
- return iterator(node);
- }
- }
- }
-
- size_increment();
- return iterator(bucket_insert(getBucket(hash), node_create(hash, key, value)));
- }
-
- /// \brief Removes the value pointed to by \p i from the hash-table.
- ///
- /// \p i must be a deferenceable iterator into the hash-table.
- void erase(iterator i)
- {
- Bucket& bucket = getBucket(i.node()->m_hash);
- BucketNode* node = i.node();
-
- // if this was the last node in the bucket
- if(bucket == node)
- {
- bucket = (node->getNext() == getLast() || &getBucket(node->getNext()->m_hash) != &bucket) ? 0 : node->getNext();
- }
-
- node_unlink(node);
- ASSERT_MESSAGE(node != 0, "tried to erase a non-existent key/value");
- node_destroy(node);
-
- size_decrement();
- }
-
- /// \brief Returns the value identified by \p key if it is contained by the hash-table, else inserts and returns a new default-constructed value associated with \p key.
- Value& operator[](const Key& key)
- {
- hash_type hash = hashKey(key);
- if(m_bucketCount != 0)
- {
- Bucket& bucket = getBucket(hash);
- if(bucket != 0)
- {
- BucketNode* node = bucket_find(bucket, hash, key);
- if(node != 0)
- {
- return node->m_value.value;
- }
- }
- }
- size_increment();
- return bucket_insert(getBucket(hash), node_create(hash, key, Value()))->m_value.value;
- }
- /// \brief Removes the value associated with \p key from the hash-table.
- void erase(const Key& key)
- {
- erase(find(key));
- }
- /// \brief Swaps the contents of the hash-table with \p other.
- void swap(HashTable& other)
- {
- std::swap(m_buckets, other.m_buckets);
- std::swap(m_bucketCount, other.m_bucketCount);
- std::swap(m_size, other.m_size);
- HashTableDetail::list_swap(m_list, other.m_list);
- }
- /// \brief Removes all values from the hash-table.
- void clear()
- {
- HashTable tmp;
- tmp.swap(*this);
- }
-};
-
-#endif