Ocean
Loading...
Searching...
No Matches
HashMap.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#ifndef META_OCEAN_BASE_HASH_MAP_H
9#define META_OCEAN_BASE_HASH_MAP_H
10
11#include "ocean/base/Base.h"
12
13namespace Ocean
14{
15
16/**
17 * This class implements a hash map.
18 * @tparam TKey The data type of the key that is associated with the data element
19 * @tparam T The data type of the data elements that are stored in the hash map
20 * @ingroup base
21 */
22template <typename TKey, typename T>
24{
25 protected:
26
27 /**
28 * Definition of a pair combining a counter states and an object.
29 */
30 using Element = typename std::pair<std::pair<size_t, size_t>, std::pair<TKey, T>>;
31
32 /**
33 * Definition of a vector holding the map objects.
34 */
35 using Elements = std::vector<Element>;
36
37 /**
38 * Definition of a function pointer returning a hash map value.
39 */
40 using ValueFunction = size_t (*)(const TKey& key);
41
42 public:
43
44 /**
45 * Copy constructor.
46 * @param hashMap The hash map to copy
47 */
48 inline HashMap(const HashMap<TKey, T>& hashMap);
49
50 /**
51 * Move constructor.
52 * @param hashMap The hash map to move
53 */
54 inline HashMap(HashMap<TKey, T>&& hashMap) noexcept;
55
56 /**
57 * Creates a new hash map object by a given capacity.
58 * @param capacity Maximal capacity the hash map will support
59 * @param function Hash function to be used
60 */
61 explicit HashMap(const size_t capacity, const ValueFunction& function = defaultHashFunction);
62
63 /**
64 * Adds a new element to this hash map.
65 * @param key Key to be added
66 * @param element Element to be added
67 * @param oneOnly True, to add the element only if it does not exist already
68 * @param extendCapacity True, to extend the capacity if necessary
69 * @return True, if the element has been added
70 */
71 bool insert(const TKey& key, const T& element, const bool oneOnly = true, const bool extendCapacity = true);
72
73 /**
74 * Adds (moves) a new element to this hash map.
75 * @param key Key to be moved
76 * @param element Element to be moved
77 * @param oneOnly True, to add the element only if it does not exist already
78 * @param extendCapacity True, to extend the capacity if necessary
79 * @return True, if the element has been added
80 */
81 bool insert(TKey&& key, T&& element, const bool oneOnly = true, const bool extendCapacity = true);
82
83 /**
84 * Removes an element from this hash map.
85 * @param key Key to be removed
86 * @return True, if succeeded
87 */
88 bool remove(const TKey& key);
89
90 /**
91 * Returns whether this hash map holds a given element.
92 * @param key Key to be found
93 * @return True, if succeeded
94 */
95 bool find(const TKey& key) const;
96
97 /**
98 * Returns whether this hash map holds a given element.
99 * @param key Key to be found
100 * @param element Resulting element corresponding to the requested key
101 * @return True, if succeeded
102 */
103 bool find(const TKey& key, const T*& element) const;
104
105 /**
106 * Returns whether this hash map holds a given element.
107 * @param key Key to be found
108 * @param element Resulting element corresponding to the requested key
109 * @return True, if succeeded
110 */
111 bool find(const TKey& key, T*& element);
112
113 /**
114 * Returns a specific element of this map.
115 * Beware: The element must exist!
116 * @param key The key which belongs to the element
117 * @return The element of the defined key
118 */
119 const T& element(const TKey& key) const;
120
121 /**
122 * Removes all elements from this has map.
123 */
124 void clear();
125
126 /**
127 * Returns the number of elements this hash map currently holds.
128 * @return Number of elements
129 */
130 inline size_t size() const;
131
132 /**
133 * Returns the capacity of this hash map.
134 * @return Maximal capacity this hash map supports
135 */
136 inline size_t capacity() const;
137
138 /**
139 * Returns whether this hash map is empty.
140 * @return True, if so
141 */
142 inline bool isEmpty() const;
143
144 /**
145 * Assign operator.
146 * @param hashMap The hash map to assign
147 * @return The reference to this object
148 */
149 inline HashMap<TKey, T>& operator=(const HashMap<TKey, T>& hashMap);
150
151 /**
152 * Move operator.
153 * @param hashMap The hash map to move
154 * @return The reference to this object
155 */
156 inline HashMap<TKey, T>& operator=(HashMap<TKey, T>&& hashMap) noexcept;
157
158 protected:
159
160 /**
161 * Creates a new hash map by a given hash map.
162 * @param capacity The capacity of the new has map, with range [hashMap.size(), infinity)
163 * @param hashMap The hash map which defines the initial values of this hash map, will be moved
164 */
165 HashMap(const size_t capacity, HashMap<TKey, T>&& hashMap);
166
167 /**
168 * Default hash function for elements supporting an cast size_t cast.
169 * @param key Key to return the hash value for
170 * @return Resulting hash value
171 */
172 static inline size_t defaultHashFunction(const TKey& key);
173
174 /**
175 * Returns whether this hash map is still consistent.
176 * @return True, if so
177 */
178 bool isConsistent() const;
179
180 protected:
181
182 /// Hash map elements.
184
185 /// Number of elements this has map holds.
186 size_t size_ = 0;
187
188 /// Value function.
190};
191
192template <typename TKey, typename T>
194 elements_(hashMap.elements_),
195 size_(hashMap.size_),
196 function_(hashMap.function_)
197{
198 // nothing to do here
199}
200
201template <typename TKey, typename T>
202inline HashMap<TKey, T>::HashMap(HashMap<TKey, T>&& hashMap) noexcept :
203 elements_(std::move(hashMap.elements_)),
204 size_(hashMap.size_),
205 function_(hashMap.function_)
206{
207 hashMap.size_ = 0;
208}
209
210template <typename TKey, typename T>
211HashMap<TKey, T>::HashMap(const size_t capacity, const ValueFunction& function) :
212 elements_(capacity),
213 size_(0),
214 function_(function)
215{
216 ocean_assert(isConsistent());
217}
218
219template <typename TKey, typename T>
220HashMap<TKey, T>::HashMap(const size_t capacity, HashMap<TKey, T>&& hashMap) :
221 elements_(capacity),
222 size_(0),
223 function_(hashMap.function_)
224{
225 ocean_assert(capacity >= hashMap.size());
226
227 for (Element& hashMapElement : hashMap.elements_)
228 {
229 if (hashMapElement.first.first != 0)
230 {
231 TKey& key = hashMapElement.second.first;
232 T& element = hashMapElement.second.second;
233
234 // duplicate keys are preserved, and this map is already large enough for all elements
235 insert(std::move(key), std::move(element), false /*oneOnly*/, false /*extendCapacity*/);
236 }
237 }
238
239 ocean_assert(size() == hashMap.size());
240 ocean_assert(isConsistent());
241
242 hashMap.clear();
243}
244
245template <typename TKey, typename T>
246bool HashMap<TKey, T>::insert(const TKey& key, const T& element, const bool oneOnly, const bool extendCapacity)
247{
248 ocean_assert(size_ <= elements_.size());
249 ocean_assert(isConsistent());
250
251 // check whether we have to extend the capacity of this hash map (we extend the map if more than 80% is occupied)
252 if (extendCapacity && size_ >= elements_.size() * 80 / 100)
253 {
254 *this = HashMap<TKey, T>(max(size_t(32), elements_.size() * 2), std::move(*this));
255 ocean_assert(size_ < elements_.size() * 80 / 100);
256 }
257
258 if (size_ == elements_.size())
259 {
260 return false;
261 }
262
263 if (oneOnly)
264 {
265 // linear search
266 for (size_t n = 0; n < elements_.size(); ++n)
267 {
268 const size_t value = (function_(key) + n) % elements_.size();
269
270 // check whether the place is free
271 if (elements_[value].first.first == 0)
272 {
273 elements_[value].first.first = 1;
274 elements_[value].first.second = n;
275 elements_[value].second.first = key;
276 elements_[value].second.second = element;
277
278 ++size_;
279 return true;
280 }
281 else if (elements_[value].second.first == key)
282 {
283 // undo the counts added while probing towards the already existing key
284 const size_t startIndex = function_(key);
285
286 for (size_t i = 0; i < n; ++i)
287 {
288 elements_[(startIndex + i) % elements_.size()].first.first--;
289 }
290
291 return false;
292 }
293 else
294 {
295 elements_[value].first.first++;
296 }
297 }
298 }
299 else
300 {
301 // linear search
302 for (size_t n = 0; n < elements_.size(); ++n)
303 {
304 const size_t value = (function_(key) + n) % elements_.size();
305
306 // check whether the place is free
307 if (elements_[value].first.first == 0)
308 {
309 elements_[value].first.first = 1;
310 elements_[value].first.second = n;
311 elements_[value].second.first = key;
312 elements_[value].second.second = element;
313
314 ++size_;
315 return true;
316 }
317 else
318 {
319 elements_[value].first.first++;
320 }
321 }
322 }
323
324 ocean_assert(false && "This must never happen!");
325 return false;
326}
327
328template <typename TKey, typename T>
329bool HashMap<TKey, T>::insert(TKey&& key, T&& element, const bool oneOnly, const bool extendCapacity)
330{
331 ocean_assert(size_ <= elements_.size());
332 ocean_assert(isConsistent());
333
334 // check whether we have to extend the capacity of this hash map (we extend the map if more than 80% is occupied)
335 if (extendCapacity && size_ >= elements_.size() * 80 / 100)
336 {
337 *this = HashMap<TKey, T>(max(size_t(32), elements_.size() * 2), std::move(*this));
338 ocean_assert(size_ < elements_.size() * 80 / 100);
339 }
340
341 if (size_ == elements_.size())
342 {
343 return false;
344 }
345
346 if (oneOnly)
347 {
348 // linear search
349 for (size_t n = 0; n < elements_.size(); ++n)
350 {
351 const size_t value = (function_(key) + n) % elements_.size();
352
353 // check whether the place is free
354 if (elements_[value].first.first == 0)
355 {
356 elements_[value].first.first = 1;
357 elements_[value].first.second = n;
358 elements_[value].second.first = std::move(key);
359 elements_[value].second.second = std::move(element);
360
361 ++size_;
362 return true;
363 }
364 else if (elements_[value].second.first == key)
365 {
366 // undo the counts added while probing towards the already existing key
367 const size_t startIndex = function_(key);
368
369 for (size_t i = 0; i < n; ++i)
370 {
371 elements_[(startIndex + i) % elements_.size()].first.first--;
372 }
373
374 return false;
375 }
376 else
377 {
378 elements_[value].first.first++;
379 }
380 }
381 }
382 else
383 {
384 // linear search
385 for (size_t n = 0; n < elements_.size(); ++n)
386 {
387 const size_t value = (function_(key) + n) % elements_.size();
388
389 // check whether the place is free
390 if (elements_[value].first.first == 0)
391 {
392 elements_[value].first.first = 1;
393 elements_[value].first.second = n;
394 elements_[value].second.first = std::move(key);
395 elements_[value].second.second = std::move(element);
396
397 ++size_;
398 return true;
399 }
400 else
401 {
402 elements_[value].first.first++;
403 }
404 }
405 }
406
407 ocean_assert(false && "This must never happen!");
408 return false;
409}
410
411template <typename TKey, typename T>
412bool HashMap<TKey, T>::remove(const TKey& key)
413{
414 ocean_assert(size_ <= elements_.size());
415 ocean_assert(isConsistent());
416
417 // linear search
418 for (size_t n = 0; n < elements_.size(); ++n)
419 {
420 const size_t value = (function_(key) + n) % elements_.size();
421
422 // check whether this place is free
423 if (elements_[value].first.first == 0)
424 {
425 return false;
426 }
427
428 // check whether this place has no shift problem
429 if (elements_[value].first.first == 1)
430 {
431 if (elements_[value].second.first == key)
432 {
433 // the slots this element was probed past no longer carry it
434 const size_t startIndex = function_(key);
435
436 for (size_t i = 0; i < n; ++i)
437 {
438 elements_[(startIndex + i) % elements_.size()].first.first--;
439 }
440
441 elements_[value].first.first = 0;
442 elements_[value].second.first = TKey();
443 elements_[value].second.second = T();
444 --size_;
445
446 ocean_assert(isConsistent());
447
448 return true;
449 }
450
451 // the element is not the element to be removed, but also there is no other position for this element
452 return false;
453 }
454
455 ocean_assert(elements_[value].first.first > 1);
456
457 size_t elementOffset = 0u;
458
459 if (elements_[value].second.first == key)
460 {
461 // the element exists however, the following elements needs a special handling
462
463 size_t localValue = value;
464 size_t endLocation = elements_.size();
465
466 while (true)
467 {
468 size_t lastOffset = 0;
469
470 // find last element to swap
471 for (size_t i = 1; i < endLocation; ++i)
472 {
473 const size_t testValue = (localValue + i) % elements_.size();
474
475 if (elements_[testValue].first.first >= 1)
476 {
477 if (elements_[testValue].first.second >= i)
478 {
479 lastOffset = i;
480 }
481 }
482
483 if (elements_[testValue].first.first <= 1)
484 {
485 break;
486 }
487 }
488
489 if (lastOffset == 0)
490 {
491 break;
492 }
493
494 ocean_assert(endLocation >= lastOffset);
495 endLocation -= lastOffset;
496
497 elementOffset += lastOffset;
498
499 const size_t lastValue = (localValue + lastOffset) % elements_.size();
500
501 // move the found element
502
503 // elements_[localValue].first.first stays constant
504 elements_[localValue].first.second = elements_[lastValue].first.second - lastOffset;
505 elements_[localValue].second = elements_[lastValue].second;
506
507 localValue = lastValue;
508
509 if (elements_[lastValue].first.first == 1)
510 {
511 break;
512 }
513 }
514
515 // decrease the used counter
516 const size_t startIndex = function_(key);
517
518 for (size_t i = 0u; i < elementOffset + n; ++i)
519 {
520 elements_[(startIndex + i) % elements_.size()].first.first--;
521 }
522
523 elements_[(value + elementOffset) % elements_.size()].first.first = 0;
524 elements_[(value + elementOffset) % elements_.size()].second.first = TKey();
525 elements_[(value + elementOffset) % elements_.size()].second.second = T();
526 --size_;
527
528 ocean_assert(isConsistent());
529
530 return true;
531 }
532 }
533
534 ocean_assert(false && "This must never happen!");
535 return false;
536}
537
538template <typename TKey, typename T>
539bool HashMap<TKey, T>::find(const TKey& key) const
540{
541 ocean_assert(size_ <= elements_.size());
542 ocean_assert(isConsistent());
543
544 // linear search
545 for (size_t n = 0; n < elements_.size(); ++n)
546 {
547 const size_t value = (function_(key) + n) % elements_.size();
548
549 // check whether this place is free
550 if (elements_[value].first.first == 0)
551 {
552 return false;
553 }
554
555 // check whether this element is equal to the given one
556 if (elements_[value].second.first == key)
557 {
558 return true;
559 }
560
561 // check whether this place is not free but unique
562 if (elements_[value].first.first == 1)
563 {
564 return false;
565 }
566 }
567
568 return false;
569}
570
571template <typename TKey, typename T>
572bool HashMap<TKey, T>::find(const TKey& key, const T*& element) const
573{
574 ocean_assert(size_ <= elements_.size());
575 ocean_assert(isConsistent());
576
577 // linear search
578 for (size_t n = 0; n < elements_.size(); ++n)
579 {
580 const size_t value = (function_(key) + n) % elements_.size();
581
582 // check whether this place is free
583 if (elements_[value].first.first == 0)
584 {
585 return false;
586 }
587
588 // check whether this element is equal to the given one
589 if (elements_[value].second.first == key)
590 {
591 element = &elements_[value].second.second;
592 return true;
593 }
594
595 // check whether this place is not free but unique
596 if (elements_[value].first.first == 1)
597 {
598 return false;
599 }
600 }
601
602 return false;
603}
604
605template <typename TKey, typename T>
606bool HashMap<TKey, T>::find(const TKey& key, T*& element)
607{
608 ocean_assert(size_ <= elements_.size());
609 ocean_assert(isConsistent());
610
611 // linear search
612 for (size_t n = 0; n < elements_.size(); ++n)
613 {
614 const size_t value = (function_(key) + n) % elements_.size();
615
616 // check whether this place is free
617 if (elements_[value].first.first == 0)
618 {
619 return false;
620 }
621
622 // check whether this element is equal to the given one
623 if (elements_[value].second.first == key)
624 {
625 element = &elements_[value].second.second;
626 return true;
627 }
628
629 // check whether this place is not free but unique
630 if (elements_[value].first.first == 1)
631 {
632 return false;
633 }
634 }
635
636 return false;
637}
638
639template <typename TKey, typename T>
640const T& HashMap<TKey, T>::element(const TKey& key) const
641{
642 ocean_assert(size_ <= elements_.size());
643 ocean_assert(isConsistent());
644
645 // linear search
646 for (size_t n = 0; n < elements_.size(); ++n)
647 {
648 const size_t value = (function_(key) + n) % elements_.size();
649
650 // check whether this place is free
651 if (elements_[value].first.first == 0)
652 {
653 break;
654 }
655
656 // check whether this element is equal to the given one
657 if (elements_[value].second.first == key)
658 {
659 return elements_[value].second.second;
660 }
661
662 // check whether this place is not free but unique
663 if (elements_[value].first.first == 1)
664 {
665 break;
666 }
667 }
668
669 ocean_assert(false && "Invalid key!");
670 return elements_.cbegin()->second.second;
671}
672
673template <typename TKey, typename T>
675{
676 ocean_assert(isConsistent());
677
678 for (Element& element : elements_)
679 {
680 element.first.first = 0;
681 }
682
683 size_ = 0;
684
685 ocean_assert(isConsistent());
686}
687
688template <typename TKey, typename T>
689inline size_t HashMap<TKey, T>::size() const
690{
691 return size_;
692}
693
694template <typename TKey, typename T>
695inline size_t HashMap<TKey, T>::capacity() const
696{
697 return elements_.size();
698}
699
700template <typename TKey, typename T>
701inline bool HashMap<TKey, T>::isEmpty() const
702{
703 return size_ == 0;
704}
705
706template <typename TKey, typename T>
708{
709 if (this != &hashMap)
710 {
711 elements_ = hashMap.elements_;
712 size_ = hashMap.size_;
713 function_ = hashMap.function_;
714 }
715
716 return *this;
717}
718
719template <typename TKey, typename T>
721{
722 if (this != &hashMap)
723 {
724 elements_ = std::move(hashMap.elements_);
725 size_ = hashMap.size_;
726 function_ = hashMap.function_;
727
728 hashMap.size_ = 0;
729 }
730
731 return *this;
732}
733
734template <typename TKey, typename T>
735inline size_t HashMap<TKey, T>::defaultHashFunction(const TKey& key)
736{
737 return size_t(key);
738}
739
740template <typename TKey, typename T>
742{
743 size_t count = 0;
744
745 // every element contributes one count to each slot of its probe sequence, so both sums must match
746 size_t slotCounters = 0;
747 size_t probeSequenceLengths = 0;
748
749 for (const Element& element : elements_)
750 {
751 slotCounters += element.first.first;
752
753 if (element.first.first != 0)
754 {
755 ++count;
756 probeSequenceLengths += element.first.second + 1;
757 }
758 }
759
760 return count == size_ && slotCounters == probeSequenceLengths;
761}
762
763}
764
765#endif // META_OCEAN_BASE_HASH_MAP_H
This class implements a hash map.
Definition HashMap.h:24
size_t capacity() const
Returns the capacity of this hash map.
Definition HashMap.h:695
bool isConsistent() const
Returns whether this hash map is still consistent.
Definition HashMap.h:741
typename std::pair< std::pair< size_t, size_t >, std::pair< TKey, T > > Element
Definition of a pair combining a counter states and an object.
Definition HashMap.h:30
void clear()
Removes all elements from this has map.
Definition HashMap.h:674
size_t size_
Number of elements this has map holds.
Definition HashMap.h:186
bool isEmpty() const
Returns whether this hash map is empty.
Definition HashMap.h:701
HashMap< TKey, T > & operator=(const HashMap< TKey, T > &hashMap)
Assign operator.
Definition HashMap.h:707
size_t size() const
Returns the number of elements this hash map currently holds.
Definition HashMap.h:689
std::vector< Element > Elements
Definition of a vector holding the map objects.
Definition HashMap.h:35
bool find(const TKey &key) const
Returns whether this hash map holds a given element.
Definition HashMap.h:539
ValueFunction function_
Value function.
Definition HashMap.h:189
const T & element(const TKey &key) const
Returns a specific element of this map.
Definition HashMap.h:640
bool remove(const TKey &key)
Removes an element from this hash map.
Definition HashMap.h:412
static size_t defaultHashFunction(const TKey &key)
Default hash function for elements supporting an cast size_t cast.
Definition HashMap.h:735
bool insert(const TKey &key, const T &element, const bool oneOnly=true, const bool extendCapacity=true)
Adds a new element to this hash map.
Definition HashMap.h:246
size_t(*)(const TKey &key) ValueFunction
Definition of a function pointer returning a hash map value.
Definition HashMap.h:40
HashMap(const HashMap< TKey, T > &hashMap)
Copy constructor.
Definition HashMap.h:193
Elements elements_
Hash map elements.
Definition HashMap.h:183
The namespace covering the entire Ocean framework.
Definition Accessor.h:15