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