Ocean
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 
15 namespace 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  */
24 template <typename TKey, typename T>
25 class HashMap
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 
194 template <typename TKey, typename T>
196  mapElements(hashMap.mapElements),
197  mapSize(hashMap.mapSize),
198  mapFunction(hashMap.mapFunction)
199 {
200  // nothing to do here
201 }
202 
203 template <typename TKey, typename T>
204 inline 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 
212 template <typename TKey, typename T>
213 HashMap<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 
221 template <typename TKey, typename T>
222 HashMap<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 
244 template <typename TKey, typename T>
245 bool 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 
311 template <typename TKey, typename T>
312 bool 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 
378 template <typename TKey, typename T>
379 bool 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 
485 template <typename TKey, typename T>
486 bool 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 
512 template <typename TKey, typename T>
513 bool 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 
542 template <typename TKey, typename T>
543 bool 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 
572 template <typename TKey, typename T>
573 const 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 
600 template <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 
613 template <typename TKey, typename T>
614 inline size_t HashMap<TKey, T>::size() const
615 {
616  return mapSize;
617 }
618 
619 template <typename TKey, typename T>
620 inline size_t HashMap<TKey, T>::capacity() const
621 {
622  return mapElements.size();
623 }
624 
625 template <typename TKey, typename T>
626 inline bool HashMap<TKey, T>::isEmpty() const
627 {
628  return mapSize == 0;
629 }
630 
631 template <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 
644 template <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 
659 template <typename TKey, typename T>
660 inline size_t HashMap<TKey, T>::defaultHashFunction(const TKey& key)
661 {
662  return size_t(key);
663 }
664 
665 template <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