1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #if !defined(INCLUDED_CONTAINER_HASHTABLE_H)
23 #define INCLUDED_CONTAINER_HASHTABLE_H
24
25 #include <cstddef>
26 #include <algorithm>
27 #include <functional>
28 #include "debugging/debugging.h"
29
30
31 namespace HashTableDetail {
next_power_of_two(std::size_t size)32 inline std::size_t next_power_of_two(std::size_t size) {
33 std::size_t result = 1;
34 while (result < size) {
35 result <<= 1;
36 }
37 return result;
38 }
39
40 struct BucketNodeBase {
41 BucketNodeBase* next;
42 BucketNodeBase* prev;
43 };
44
list_initialise(BucketNodeBase & self)45 inline void list_initialise(BucketNodeBase& self) {
46 self.next = self.prev = &self;
47 }
48
list_swap(BucketNodeBase & self,BucketNodeBase & other)49 inline void list_swap(BucketNodeBase& self, BucketNodeBase& other) {
50 BucketNodeBase tmp(self);
51 if (other.next == &other) {
52 list_initialise(self);
53 } else {
54 self = other;
55 self.next->prev = self.prev->next = &self;
56 }
57 if (tmp.next == &self) {
58 list_initialise(other);
59 } else {
60 other = tmp;
61 other.next->prev = other.prev->next = &other;
62 }
63 }
64
node_link(BucketNodeBase * node,BucketNodeBase * next)65 inline void node_link(BucketNodeBase* node, BucketNodeBase* next) {
66 node->next = next;
67 node->prev = next->prev;
68 next->prev = node;
69 node->prev->next = node;
70 }
node_unlink(BucketNodeBase * node)71 inline void node_unlink(BucketNodeBase* node) {
72 node->prev->next = node->next;
73 node->next->prev = node->prev;
74 }
75
76 template<typename Key, typename Value>
77 struct KeyValue {
78 const Key key;
79 Value value;
80
KeyValueKeyValue81 KeyValue(const Key& key_, const Value& value_)
82 : key(key_), value(value_) {
83 }
84 };
85
86 template<typename Key, typename Value, typename Hash>
87 struct BucketNode : public BucketNodeBase {
88 Hash m_hash;
89 KeyValue<Key, Value> m_value;
90
BucketNodeBucketNode91 BucketNode(Hash hash, const Key& key, const Value& value)
92 : m_hash(hash), m_value(key, value) {
93 }
getNextBucketNode94 BucketNode* getNext() const {
95 return static_cast<BucketNode*>(next);
96 }
getPrevBucketNode97 BucketNode* getPrev() const {
98 return static_cast<BucketNode*>(prev);
99 }
100 };
101
102 template<typename Key, typename Value, typename Hash>
103 class BucketIterator {
104 typedef BucketNode<Key, Value, Hash> Node;
105 Node* m_node;
106
increment()107 void increment() {
108 m_node = m_node->getNext();
109 }
110
111 public:
112 typedef std::forward_iterator_tag iterator_category;
113 typedef std::ptrdiff_t difference_type;
114 typedef difference_type distance_type;
115 typedef KeyValue<Key, Value> value_type;
116 typedef value_type* pointer;
117 typedef value_type& reference;
118
BucketIterator(Node * node)119 BucketIterator(Node* node) : m_node(node) {
120 }
121
node()122 Node* node() {
123 return m_node;
124 }
125
126 bool operator==(const BucketIterator& other) const {
127 return m_node == other.m_node;
128 }
129 bool operator!=(const BucketIterator& other) const {
130 return !operator==(other);
131 }
132 BucketIterator& operator++() {
133 increment();
134 return *this;
135 }
136 BucketIterator operator++(int) {
137 BucketIterator tmp = *this;
138 increment();
139 return tmp;
140 }
141 value_type& operator*() const {
142 return m_node->m_value;
143 }
144 value_type* operator->() const {
145 return &(operator*());
146 }
147 };
148 }
149
150
151 /// A hash-table container which maps keys to values.
152 ///
153 /// - Inserting or removing elements does not invalidate iterators.
154 /// - Inserting or retrieving an element for a given key takes O(1) time on average.
155 /// - Elements are stored in no particular order.
156 ///
157 /// \param Key Uniquely identifies a value. Must provide a copy-constructor.
158 /// \param Value The value to be stored . Must provide a default-constructor and a copy-constructor.
159 /// \param Hasher Must provide 'std::size_t operator()(const Key&) const' which always returns the same result if the same argument is given.
160 /// \param KeyEqual Must provide 'bool operator==(const Key&, const Key&) const' which returns true only if both arguments are equal.
161 ///
162 /// \skipline HashTable example
163 /// \until end example
164 template < typename Key, typename Value, typename Hasher, typename KeyEqual = std::equal_to<Key> >
165 class HashTable : private KeyEqual, private Hasher {
166 typedef typename Hasher::hash_type hash_type;
167 typedef HashTableDetail::KeyValue<Key, Value> KeyValue;
168 typedef HashTableDetail::BucketNode<Key, Value, hash_type> BucketNode;
169
node_create(hash_type hash,const Key & key,const Value & value)170 inline BucketNode* node_create(hash_type hash, const Key& key, const Value& value) {
171 return new BucketNode(hash, key, value);
172 }
node_destroy(BucketNode * node)173 inline void node_destroy(BucketNode* node) {
174 delete node;
175 }
176
177 typedef BucketNode* Bucket;
178
buckets_new(std::size_t count)179 static Bucket* buckets_new(std::size_t count) {
180 Bucket* buckets = new Bucket[count];
181 std::uninitialized_fill(buckets, buckets + count, Bucket(0));
182 return buckets;
183 }
buckets_delete(Bucket * buckets)184 static void buckets_delete(Bucket* buckets) {
185 delete[] buckets;
186 }
187
188 std::size_t m_bucketCount;
189 Bucket* m_buckets;
190 std::size_t m_size;
191 HashTableDetail::BucketNodeBase m_list;
192
getFirst()193 BucketNode* getFirst() {
194 return static_cast<BucketNode*>(m_list.next);
195 }
getLast()196 BucketNode* getLast() {
197 return static_cast<BucketNode*>(&m_list);
198 }
199
200 public:
201
202 typedef KeyValue value_type;
203 typedef HashTableDetail::BucketIterator<Key, Value, hash_type> iterator;
204
205 private:
206
initialise()207 void initialise() {
208 list_initialise(m_list);
209 }
hashKey(const Key & key)210 hash_type hashKey(const Key& key) {
211 return Hasher::operator()(key);
212 }
213
getBucketId(hash_type hash)214 std::size_t getBucketId(hash_type hash) const {
215 return hash & (m_bucketCount - 1);
216 }
getBucket(hash_type hash)217 Bucket& getBucket(hash_type hash) {
218 return m_buckets[getBucketId(hash)];
219 }
bucket_find(Bucket bucket,hash_type hash,const Key & key)220 BucketNode* bucket_find(Bucket bucket, hash_type hash, const Key& key) {
221 std::size_t bucketId = getBucketId(hash);
222 for (iterator i(bucket); i != end(); ++i) {
223 hash_type nodeHash = i.node()->m_hash;
224
225 if (getBucketId(nodeHash) != bucketId) {
226 return 0;
227 }
228
229 if (nodeHash == hash && KeyEqual::operator()((*i).key, key)) {
230 return i.node();
231 }
232 }
233 return 0;
234 }
bucket_insert(Bucket & bucket,BucketNode * node)235 BucketNode* bucket_insert(Bucket& bucket, BucketNode* node) {
236 // link node into list
237 node_link(node, bucket_next(bucket));
238 bucket = node;
239 return node;
240 }
bucket_next(Bucket & bucket)241 BucketNode* bucket_next(Bucket& bucket) {
242 Bucket* end = m_buckets + m_bucketCount;
243 for (Bucket* i = &bucket; i != end; ++i) {
244 if (*i != 0) {
245 return *i;
246 }
247 }
248 return getLast();
249 }
250
buckets_resize(std::size_t count)251 void buckets_resize(std::size_t count) {
252 BucketNode* first = getFirst();
253 BucketNode* last = getLast();
254
255 buckets_delete(m_buckets);
256
257 m_bucketCount = count;
258
259 m_buckets = buckets_new(m_bucketCount);
260 initialise();
261
262 for (BucketNode* i = first; i != last;) {
263 BucketNode* node = i;
264 i = i->getNext();
265 bucket_insert(getBucket((*node).m_hash), node);
266 }
267 }
size_increment()268 void size_increment() {
269 if (m_size == m_bucketCount) {
270 buckets_resize(m_bucketCount == 0 ? 8 : m_bucketCount << 1);
271 }
272 ++m_size;
273 }
size_decrement()274 void size_decrement() {
275 --m_size;
276 }
277
278 HashTable(const HashTable& other);
279 HashTable& operator=(const HashTable& other);
280 public:
HashTable()281 HashTable() : m_bucketCount(0), m_buckets(0), m_size(0) {
282 initialise();
283 }
HashTable(std::size_t bucketCount)284 HashTable(std::size_t bucketCount) : m_bucketCount(HashTableDetail::next_power_of_two(bucketCount)), m_buckets(buckets_new(m_bucketCount)), m_size(0) {
285 initialise();
286 }
~HashTable()287 ~HashTable() {
288 for (BucketNode* i = getFirst(); i != getLast();) {
289 BucketNode* node = i;
290 i = i->getNext();
291 node_destroy(node);
292 }
293 buckets_delete(m_buckets);
294 }
295
begin()296 iterator begin() {
297 return iterator(getFirst());
298 }
end()299 iterator end() {
300 return iterator(getLast());
301 }
302
empty()303 bool empty() const {
304 return m_size == 0;
305 }
size()306 std::size_t size() const {
307 return m_size;
308 }
309
310 /// \brief Returns an iterator pointing to the value associated with \p key if it is contained by the hash-table, else \c end().
find(const Key & key)311 iterator find(const Key& key) {
312 hash_type hash = hashKey(key);
313 if (m_bucketCount != 0) {
314 Bucket bucket = getBucket(hash);
315 if (bucket != 0) {
316 BucketNode* node = bucket_find(bucket, hash, key);
317 if (node != 0) {
318 return iterator(node);
319 }
320 }
321 }
322
323 return end();
324 }
325 /// \brief Adds \p value to the hash-table associated with \p key if it does not exist.
insert(const Key & key,const Value & value)326 iterator insert(const Key& key, const Value& value) {
327 hash_type hash = hashKey(key);
328 if (m_bucketCount != 0) {
329 Bucket& bucket = getBucket(hash);
330 if (bucket != 0) {
331 BucketNode* node = bucket_find(bucket, hash, key);
332 if (node != 0) {
333 return iterator(node);
334 }
335 }
336 }
337
338 size_increment();
339 return iterator(bucket_insert(getBucket(hash), node_create(hash, key, value)));
340 }
341
342 /// \brief Removes the value pointed to by \p i from the hash-table.
343 ///
344 /// \p i must be a deferenceable iterator into the hash-table.
erase(iterator i)345 void erase(iterator i) {
346 Bucket& bucket = getBucket(i.node()->m_hash);
347 BucketNode* node = i.node();
348
349 // if this was the last node in the bucket
350 if (bucket == node) {
351 bucket = (node->getNext() == getLast() || &getBucket(node->getNext()->m_hash) != &bucket) ? 0 : node->getNext();
352 }
353
354 node_unlink(node);
355 ASSERT_MESSAGE(node != 0, "tried to erase a non-existent key/value");
356 node_destroy(node);
357
358 size_decrement();
359 }
360
361 /// \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.
362 Value& operator[](const Key& key) {
363 hash_type hash = hashKey(key);
364 if (m_bucketCount != 0) {
365 Bucket& bucket = getBucket(hash);
366 if (bucket != 0) {
367 BucketNode* node = bucket_find(bucket, hash, key);
368 if (node != 0) {
369 return node->m_value.value;
370 }
371 }
372 }
373 size_increment();
374 return bucket_insert(getBucket(hash), node_create(hash, key, Value()))->m_value.value;
375 }
376 /// \brief Removes the value associated with \p key from the hash-table.
erase(const Key & key)377 void erase(const Key& key) {
378 erase(find(key));
379 }
380 /// \brief Swaps the contents of the hash-table with \p other.
swap(HashTable & other)381 void swap(HashTable& other) {
382 std::swap(m_buckets, other.m_buckets);
383 std::swap(m_bucketCount, other.m_bucketCount);
384 std::swap(m_size, other.m_size);
385 HashTableDetail::list_swap(m_list, other.m_list);
386 }
387 /// \brief Removes all values from the hash-table.
clear()388 void clear() {
389 HashTable tmp;
390 tmp.swap(*this);
391 }
392 };
393
394 #endif
395