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