The Pedigree Project 0.1
HashTable.h
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#ifndef KERNEL_UTILITIES_HASHTABLE_H
21#define KERNEL_UTILITIES_HASHTABLE_H
22
23#include "pedigree/kernel/processor/types.h"
24#include "pedigree/kernel/utilities/Iterator.h"
25#include "pedigree/kernel/utilities/Pair.h"
26#include "pedigree/kernel/utilities/Result.h"
27#include "pedigree/kernel/utilities/utility.h"
28
32namespace HashTableError {
33enum Error { HashTableEmpty, NotFound, IterationComplete };
34}
35
57template <class K, class V, class SiblingK = K, size_t InitialBuckets = 4,
58 bool QuadraticProbe = true, size_t GrowthFactor = 2>
59class HashTable {
60 private:
62
63 static_assert(InitialBuckets > 0, "At least one initial bucket must be available.");
64
65 struct bucket {
66 bucket() : key(), value(), set(false), parent(nullptr) {}
67
68 K key;
69 V value;
70 bool set;
71 HashTable* parent;
72
73 struct bucket* next() {
74 if (!parent) {
75 return nullptr;
76 }
77
78 struct bucket* node = this;
79 while (++node < (parent->m_Buckets + parent->m_nBuckets)) {
80 if (node->set) {
81 return node;
82 }
83 }
84
85 return nullptr;
86 }
87
88 struct bucket* previous() {
89 if (!parent) {
90 return nullptr;
91 }
92
93 struct bucket* node = this;
94 while (--node >= parent->m_Buckets) {
95 if (node->set) {
96 return node;
97 }
98 }
99
100 return nullptr;
101 }
102 };
103
104 public:
105 typedef ::Iterator<V, struct bucket> Iterator;
106 typedef typename Iterator::Const ConstIterator;
107
109 typedef Result<Pair<K, V>, HashTableError::Error> PairLookupResult;
110
111 HashTable() : m_Buckets(nullptr), m_Default(), m_nBuckets(0), m_nItems(0), m_nMask(0) {}
112
116 HashTable(const V& customDefault) : HashTable() {
117 m_Default = customDefault;
118 }
119
123 void clear() noexcept {
124 delete[] m_Buckets;
125 m_Buckets = nullptr;
126 m_nBuckets = 0;
127 m_nItems = 0;
128 m_nMask = 0;
129 }
130
131 ~HashTable() {
132 clear();
133 }
134
138 bool contains(const K& k) const {
139 if ((!m_Buckets) || (!m_nItems)) {
140 return false;
141 }
142
143 const uint32_t khash = k.hash();
144 size_t hash = khash & m_nMask;
145
146 const bucket* b = &m_Buckets[hash];
147 if (!b->set) {
148 return false;
149 }
150
151 if (b->key != k) {
152 b = findNextSet(hash, k, khash);
153 if (!b) {
154 return false;
155 }
156 }
157
158 return true;
159 }
160
168 LookupResult lookup(const K& k) const {
169 HashTableError::Error err;
170 const struct bucket* b = doLookup<K>(k, err);
171 if (b) {
172 return LookupResult::withValue(b->value);
173 } else {
174 return LookupResult::withError(err);
175 }
176 }
177
178 template <typename SK = SiblingK>
179 typename pedigree_std::enable_if<!pedigree_std::is_same<K, SK>::value, LookupResult>::type lookup(
180 const SK& k) const {
181 HashTableError::Error err;
182 const struct bucket* b = doLookup<SK>(k, err);
183 if (b) {
184 return LookupResult::withValue(b->value);
185 } else {
186 return LookupResult::withError(err);
187 }
188 }
189
197 PairLookupResult getNth(size_t n) const {
198 if (n < count()) {
199 size_t j = 0;
200 for (size_t i = 0; i < m_nBuckets; ++i) {
201 if (!m_Buckets[i].set) {
202 continue;
203 }
204
205 if (j++ == n) {
206 Pair<K, V> result(m_Buckets[i].key, m_Buckets[i].value);
207 return PairLookupResult::withValue(result);
208 }
209 }
210 }
211
212 return PairLookupResult::withError(HashTableError::IterationComplete);
213 }
214
218 bool insert(const K& k, const V& v) {
219 check();
220
221 // Ensure we have space for the new item
222 reserve(m_nItems + 1);
223
224 const uint32_t khash = k.hash();
225 size_t hash = khash & m_nMask;
226
227 // Do we need to chain?
228 bucket* b = &m_Buckets[hash];
229 if (b->set) {
230 // If key matches, this is more than just a hash collision.
231 if (b->key == k || findNextSet(hash, k, khash)) {
232 return false;
233 }
234
235 // Probe for an empty bucket.
236 b = findNextEmpty(hash);
237 if (!b) {
238 return false;
239 }
240 }
241
242 b->set = true;
243 b->key = k;
244 b->key.hash(); // precompute hash
245 b->value = v;
246 bool wasEmpty = m_nItems == 0;
247 ++m_nItems;
248
249 return true;
250 }
251
255 bool update(const K& k, const V& v) {
256 check();
257
258 HashTableError::Error err;
259 struct bucket* b = const_cast<struct bucket*>(doLookup(k, err));
260 if (!b) {
261 return false;
262 }
263
264 b->value = v;
265 return true;
266 }
267
271 void remove(const K& k) {
272 if (!m_Buckets) {
273 return;
274 }
275
276 const uint32_t khash = k.hash();
277 size_t hash = khash & m_nMask;
278
279 bucket* b = &m_Buckets[hash];
280 if (!b->set) {
281 return;
282 }
283
284 bool didRemove = false;
285
286 if (b->key == k) {
287 b->set = false;
288 b->value = m_Default;
289 didRemove = true;
290 } else {
291 b = findNextSet(hash, k, khash);
292 if (b) {
293 b->set = false;
294 b->value = m_Default;
295 didRemove = true;
296 }
297 }
298
299 if (didRemove) {
300 --m_nItems;
301
302 if (m_nItems) {
303 // Must rehash as we use linear probing for collision handling.
304 rehash();
305 }
306 }
307 }
308
312 void reserve(size_t numItems) {
313 check();
314
315 if (numItems < m_nBuckets) {
316 // No resize necessary, reserve is completely contained within the
317 // current bucket array.
318 return;
319 }
320
321 if (!numItems) {
322 ++numItems;
323 }
324
325 // Round up to next power of two (if not already)
326 size_t nextpow2 = 1ULL << (64 - __builtin_clzll(numItems));
327 size_t numItemsRounded = max(InitialBuckets, nextpow2);
328 if (m_nBuckets == numItemsRounded) {
329 // no need to resize here
330 return;
331 }
332
333 // Handle resize and associated rehash if the table is full.
334 size_t origCount = m_nBuckets;
335 m_nBuckets = numItemsRounded;
336 m_nMask = m_nBuckets - 1;
337 if (m_nItems) {
338 rehash(origCount);
339 } else {
340 // No items in the array, just recreate it here
341 delete[] m_Buckets;
342 m_Buckets = new bucket[m_nBuckets];
343 setDefaults(true);
344 }
345 }
346
347 size_t count() const {
348 return m_nItems;
349 }
350
351 Iterator begin() {
352 return m_nItems ? Iterator(getFirstSetBucket()) : end();
353 }
354
355 ConstIterator begin() const {
356 return m_nItems ? ConstIterator(getFirstSetBucket()) : end();
357 }
358
359 Iterator end() {
360 return Iterator(0);
361 }
362
363 ConstIterator end() const {
364 return ConstIterator(0);
365 }
366
371 struct bucket* node = at.__getNode();
372 if (node && node->set) {
373 node->set = false;
374 --m_nItems;
375 rehash();
376
377 // This is the only safe way to continue iterating - the rehash in
378 // remove makes everything else incorrect.
379 return begin();
380 } else {
381 return at;
382 }
383 }
384
391 HashTable(const SelfType& other) = delete;
392 SelfType& operator=(const SelfType& p) = delete;
393
400 void copyFrom(const SelfType& other) {
401 if (this == &other)
402 return;
403
404 clear();
405
406 m_Default = other.m_Default;
407 m_nBuckets = other.m_nBuckets;
408 m_nItems = other.m_nItems;
409 m_nMask = other.m_nMask;
410 if (m_nBuckets) {
411 m_Buckets = new bucket[m_nBuckets];
412 pedigree_std::copy(m_Buckets, other.m_Buckets, m_nBuckets);
413 resetParents();
414 }
415 }
416
417 SelfType& operator=(SelfType&& p) noexcept(noexcept(m_Default = static_cast<V&&>(p.m_Default))) {
418 if (this == &p)
419 return *this;
420
421 clear();
422
423 m_Default = static_cast<V&&>(p.m_Default);
424 m_nBuckets = p.m_nBuckets;
425 m_nItems = p.m_nItems;
426 m_nMask = p.m_nMask;
427 m_Buckets = p.m_Buckets;
428
429 p.m_Buckets = nullptr;
430 p.clear();
431
432 return *this;
433 }
434
435 private:
436 struct bucket* getFirstSetBucket() const {
437 struct bucket* result = m_Buckets;
438 while (result < (m_Buckets + m_nBuckets)) {
439 if (result->set) {
440 return result;
441 }
442
443 ++result;
444 }
445
446 return nullptr;
447 }
448
449 void check() {
450 if (m_Buckets == nullptr) {
451 m_Buckets = new bucket[InitialBuckets];
452 m_nBuckets = InitialBuckets;
453 m_nMask = InitialBuckets - 1;
454 setDefaults(true);
455 }
456 }
457
458 void rehash(size_t oldCount = 0) {
459 if (oldCount == 0) {
460 oldCount = m_nBuckets;
461 }
462
463 bucket* oldBuckets = m_Buckets;
464 m_Buckets = new bucket[m_nBuckets];
465 setDefaults(true);
466
467 if (m_nItems) {
468 // Performing a new insert, clear out the number of items as
469 // insert() will increment otherwise.
470 m_nItems = 0;
471
472 for (size_t i = 0; i < oldCount; ++i) {
473 if (oldBuckets[i].set) {
474 insert(oldBuckets[i].key, oldBuckets[i].value);
475 }
476 }
477 }
478 delete[] oldBuckets;
479 }
480
481 size_t nextIndex(size_t i, size_t& index, size_t& step) const {
482 if (QuadraticProbe) {
483 index = (index + step) & m_nMask;
484 ++step;
485 } else {
486 index = i;
487 }
488
489 return index;
490 }
491
492 bucket* findNextEmpty(size_t currentHash) {
493 size_t index = 0;
494 size_t step = 1;
495 for (size_t i = 0; i < m_nBuckets; ++i) {
496 size_t nextHash = (currentHash + nextIndex(i, index, step)) & m_nMask;
497 bucket* b = &m_Buckets[nextHash];
498 if (!b->set) {
499 return b;
500 }
501 }
502
503 return nullptr;
504 }
505
506 bucket* findNextSet(size_t currentHash, const K& k, uint32_t khash = 0) {
507 size_t index = 0;
508 size_t step = 1;
509 if (khash == 0) {
510 khash = k.hash();
511 }
512 for (size_t i = 0; i < m_nBuckets; ++i) {
513 size_t nextHash = (currentHash + nextIndex(i, index, step)) & m_nMask;
514 bucket* b = &m_Buckets[nextHash];
515
516 // Removal rehashes the table, so an empty bucket ends this probe chain.
517 if (!b->set) {
518 return nullptr;
519 }
520 // Hash comparison is likely to be faster than raw object
521 // comparison so we save the latter for when we have a candidate.
522 if (b->key.hash() == khash) {
523 if (b->key == k) {
524 return b;
525 }
526 }
527 }
528
529 return nullptr;
530 }
531
532 template <class FindK = K>
533 const bucket* findNextSet(size_t currentHash, const FindK& k, uint32_t khash = 0) const {
534 size_t index = 0;
535 size_t step = 1;
536 if (khash == 0) {
537 khash = k.hash();
538 }
539 for (size_t i = 0; i < m_nBuckets; ++i) {
540 size_t nextHash = (currentHash + nextIndex(i, index, step)) & m_nMask;
541 const bucket* b = &m_Buckets[nextHash];
542
543 // Removal rehashes the table, so an empty bucket ends this probe chain.
544 if (!b->set) {
545 return nullptr;
546 }
547 // Hash comparison is likely to be faster than raw object
548 // comparison so we save the latter for when we have a candidate.
549 if (b->key.hash() == khash) {
550 if (b->key == k) {
551 return b;
552 }
553 }
554 }
555
556 return nullptr;
557 }
558
559 template <class LookupK>
560 const struct bucket* doLookup(const LookupK& k, HashTableError::Error& err) const {
561 if ((!m_Buckets) || (!m_nItems)) {
562 err = HashTableError::HashTableEmpty;
563 return nullptr;
564 }
565
566 const uint32_t khash = k.hash();
567 size_t hash = khash & m_nMask;
568
569 const bucket* b = &m_Buckets[hash];
570 if (!b->set) {
571 err = HashTableError::NotFound;
572 return nullptr;
573 }
574
575 if (b->key != k) {
576 b = findNextSet(hash, k, khash);
577 if (!b) {
578 err = HashTableError::NotFound;
579 return nullptr;
580 }
581 }
582
583 return b;
584 }
585
587 void setDefaults(bool reparentToo = false) {
588 for (size_t i = 0; i < m_nBuckets; ++i) {
589 if (reparentToo) {
590 m_Buckets[i].parent = this;
591 }
592
593 if (m_Buckets[i].set) {
594 continue;
595 }
596
597 m_Buckets[i].value = m_Default;
598 }
599 }
600
601 void resetParents() {
602 for (size_t i = 0; i < m_nBuckets; ++i) {
603 m_Buckets[i].parent = this;
604 }
605 }
606
607 bucket* m_Buckets;
608 V m_Default;
609 size_t m_nBuckets;
610 size_t m_nItems;
611 size_t m_nMask;
612};
613
616#endif
LookupResult lookup(const K &k) const
Definition HashTable.h:168
void remove(const K &k)
Definition HashTable.h:271
PairLookupResult getNth(size_t n) const
Definition HashTable.h:197
void copyFrom(const SelfType &other)
Definition HashTable.h:400
HashTable(const V &customDefault)
Definition HashTable.h:116
bool update(const K &k, const V &v)
Definition HashTable.h:255
HashTable(const SelfType &other)=delete
bool insert(const K &k, const V &v)
Definition HashTable.h:218
void reserve(size_t numItems)
Definition HashTable.h:312
Iterator erase(Iterator &at)
Definition HashTable.h:370
void clear() noexcept
Definition HashTable.h:123
void setDefaults(bool reparentToo=false)
Definition HashTable.h:587
bool contains(const K &k) const
Definition HashTable.h:138
An iterator applicable for many data structures.
Definition Iterator.h:41
Struct * __getNode()
Definition Iterator.h:121
Definition Pair.h:30