nfx-containers 0.6.0
Modern C++20 header-only library providing high-performance hash containers with Robin Hood and perfect hashing
Loading...
Searching...
No Matches
OrderedHashMap.h
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) 2026 nfx
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
32
33#pragma once
34
35#include "nfx/detail/containers/CompilerSupport.h"
36
37#include <nfx/Hashing.h>
38
39#include <algorithm>
40#include <concepts>
41#include <cstddef>
42#include <cstdint>
43#include <functional>
44#include <initializer_list>
45#include <iterator>
46#include <stdexcept>
47#include <string>
48#include <string_view>
49#include <type_traits>
50#include <utility>
51#include <vector>
52
53namespace nfx::containers
54{
55 //=====================================================================
56 // OrderedHashMap class
57 //=====================================================================
58
72 template <typename TKey,
73 typename TValue,
74 hashing::Hash32or64 HashType = uint32_t,
75 HashType Seed = ( sizeof( HashType ) == 4 ? hashing::constants::FNV_OFFSET_BASIS_32 : hashing::constants::FNV_OFFSET_BASIS_64 ),
76 typename THasher = hashing::Hasher<HashType, Seed>,
77 typename KeyEqual = std::equal_to<>>
78 class OrderedHashMap final
79 {
80 //----------------------------------------------
81 // Compile-time type constraints
82 //----------------------------------------------
83
84 static_assert( std::is_same_v<HashType, uint32_t> || std::is_same_v<HashType, uint64_t>,
85 "HashType must be uint32_t or uint64_t" );
86
87 static_assert( std::is_invocable_r_v<HashType, THasher, TKey>,
88 "THasher must be callable with TKey and return HashType" );
89
90 static_assert( std::is_invocable_r_v<bool, KeyEqual, TKey, TKey>,
91 "KeyEqual must be callable with two TKey arguments and return bool" );
92
93 public:
94 //----------------------------------------------
95 // Forward declarations for iterator support
96 //----------------------------------------------
97
98 class Iterator;
99 class ConstIterator;
100
101 //----------------------------------------------
102 // STL-compatible type aliases
103 //----------------------------------------------
104
106 using key_type = TKey;
107
109 using mapped_type = TValue;
110
112 using value_type = std::pair<const TKey, TValue>;
113
115 using hasher = THasher;
116
118 using key_equal = KeyEqual;
119
121 using hash_type = HashType;
122
124 using size_type = size_t;
125
127 using difference_type = std::ptrdiff_t;
128
131
134
135 //----------------------------------------------
136 // Construction
137 //----------------------------------------------
138
143
148 inline OrderedHashMap( std::initializer_list<std::pair<TKey, TValue>> init );
149
156 template <typename InputIt>
157 inline OrderedHashMap( InputIt first, InputIt last );
158
163 inline explicit OrderedHashMap( size_t initialCapacity );
164
169 OrderedHashMap( OrderedHashMap&& other ) noexcept;
170
177
183
190
195
196 //----------------------------------------------
197 // Core operations
198 //----------------------------------------------
199
206 template <typename KeyType = TKey>
207 [[nodiscard]] inline TValue* find( const KeyType& key ) noexcept;
208
215 template <typename KeyType = TKey>
216 [[nodiscard]] inline const TValue* find( const KeyType& key ) const noexcept;
217
225 template <typename KeyType = TKey>
226 [[nodiscard]] inline bool contains( const KeyType& key ) const noexcept;
227
234 inline TValue& at( const TKey& key );
235
242 inline const TValue& at( const TKey& key ) const;
243
252 inline TValue& operator[]( const TKey& key );
253
262 inline TValue& operator[]( TKey&& key );
263
271 template <typename KeyType = TKey>
272 inline TValue& at( const KeyType& key );
273
281 template <typename KeyType = TKey>
282 inline const TValue& at( const KeyType& key ) const;
283
284 //----------------------------------------------
285 // Insertion
286 //----------------------------------------------
287
296 inline bool insert( const TKey& key, const TValue& value );
297
306 inline bool insert( const TKey& key, TValue&& value );
307
316 inline bool insert( TKey&& key, TValue&& value );
317
325 inline void insertOrAssign( const TKey& key, TValue&& value );
326
334 inline void insertOrAssign( const TKey& key, const TValue& value );
335
343 inline void insertOrAssign( TKey&& key, TValue&& value );
344
345 //----------------------------------------------
346 // Emplace operations
347 //----------------------------------------------
348
356 template <typename... Args>
357 inline void emplace( const TKey& key, Args&&... args );
358
366 template <typename... Args>
367 inline void emplace( TKey&& key, Args&&... args );
368
378 template <typename... Args>
379 inline std::pair<Iterator, bool> tryEmplace( const TKey& key, Args&&... args );
380
390 template <typename... Args>
391 inline std::pair<Iterator, bool> tryEmplace( TKey&& key, Args&&... args );
392
393 //----------------------------------------------
394 // Capacity and memory management
395 //----------------------------------------------
396
402 inline void reserve( size_t minCapacity );
403
411 template <typename KeyType = TKey>
412 inline bool erase( const KeyType& key ) noexcept;
413
420 inline Iterator erase( ConstIterator pos ) noexcept;
421
428 inline Iterator erase( ConstIterator first, ConstIterator last ) noexcept;
429
433 inline void clear() noexcept;
434
445 template <typename KeyType = TKey>
446 [[nodiscard]] inline std::optional<std::pair<TKey, TValue>> extract( const KeyType& key );
447
456 inline void merge( OrderedHashMap& other );
457
462 inline void merge( OrderedHashMap&& other );
463
464 //----------------------------------------------
465 // State inspection
466 //----------------------------------------------
467
472 [[nodiscard]] inline size_t size() const noexcept;
473
478 [[nodiscard]] inline size_t capacity() const noexcept;
479
484 [[nodiscard]] inline bool isEmpty() const noexcept;
485
491 inline void swap( OrderedHashMap& other ) noexcept;
492
493 //----------------------------------------------
494 // STL-compatible iteration support (insertion order)
495 //----------------------------------------------
496
502 [[nodiscard]] Iterator begin() noexcept;
503
509 [[nodiscard]] ConstIterator begin() const noexcept;
510
516 [[nodiscard]] Iterator end() noexcept;
517
523 [[nodiscard]] ConstIterator end() const noexcept;
524
530 [[nodiscard]] ConstIterator cbegin() const noexcept;
531
537 [[nodiscard]] ConstIterator cend() const noexcept;
538
545 [[nodiscard]] bool operator==( const OrderedHashMap& other ) const noexcept;
546
547 private:
548 //----------------------------------------------
549 // Node structure for doubly-linked list
550 //----------------------------------------------
551
555 struct Node
556 {
557 std::pair<TKey, TValue> data;
558 Node* prev{};
559 Node* next{};
560 HashType hash{};
561 };
562
563 //----------------------------------------------
564 // Robin Hood Hashing bucket structure
565 //----------------------------------------------
566
570 struct Bucket
571 {
572 Node* node{};
573 HashType hash{};
574 uint32_t distance{};
575 bool occupied{};
576 };
577
581 static constexpr size_t INITIAL_CAPACITY = 32;
582
586 static constexpr size_t MAX_LOAD_FACTOR_PERCENT = 75;
587
591 std::vector<Bucket> m_buckets;
592
593 Node* m_head{};
594 Node* m_tail{};
595 size_t m_size{};
596 size_t m_capacity{ INITIAL_CAPACITY };
597 size_t m_mask{ INITIAL_CAPACITY - 1 };
598
602 NFX_CONTAINERS_NO_UNIQUE_ADDRESS hasher m_hasher;
603
607 NFX_CONTAINERS_NO_UNIQUE_ADDRESS KeyEqual m_keyEqual;
608
609 //----------------------------------------------
610 // Internal implementation
611 //----------------------------------------------
612
619 template <typename ValueType>
620 inline void insertOrAssignInternal( const TKey& key, ValueType&& value );
621
628 template <typename ValueType>
629 inline void insertOrAssignInternal( TKey&& key, ValueType&& value );
630
635 inline bool shouldResize() const noexcept;
636
641 inline void resize();
642
647 inline void eraseAtPosition( size_t pos ) noexcept;
648
653 inline void unlinkNode( Node* node ) noexcept;
654
659 inline void appendNode( Node* node ) noexcept;
660
669 template <typename KeyType1, typename KeyType2>
670 inline bool keysEqual( const KeyType1& k1, const KeyType2& k2 ) const noexcept;
671
672 public:
673 //----------------------------------------------
674 // OrderedHashMap::Iterator class
675 //----------------------------------------------
676
681 {
682 friend class ConstIterator;
683 friend class OrderedHashMap;
684
685 public:
687 using iterator_category = std::bidirectional_iterator_tag;
688
690 using value_type = std::pair<const TKey, TValue>;
691
693 using difference_type = std::ptrdiff_t;
694
697
700
701 //---------------------------
702 // Construction
703 //---------------------------
704
708 Iterator() = default;
709
714 inline explicit Iterator( Node* node );
715
716 //---------------------------
717 // Operations
718 //---------------------------
719
724 inline reference operator*() const;
725
730 inline pointer operator->() const;
731
737
742 inline Iterator operator++( int );
743
749
754 inline Iterator operator--( int );
755
756 //---------------------------
757 // Comparison
758 //---------------------------
759
765 inline bool operator==( const Iterator& other ) const;
766
772 inline bool operator!=( const Iterator& other ) const;
773
774 private:
775 //---------------------------
776 // Private members
777 //---------------------------
778
784 inline Iterator( Node* node, const OrderedHashMap* container );
785
786 Node* m_node = nullptr;
787 const OrderedHashMap* m_container = nullptr;
788 };
789
790 //----------------------------------------------
791 // OrderedHashMap::ConstIterator class
792 //----------------------------------------------
793
798 {
799 friend class OrderedHashMap;
800
801 public:
803 using iterator_category = std::bidirectional_iterator_tag;
804
806 using value_type = std::pair<const TKey, TValue>;
807
809 using difference_type = std::ptrdiff_t;
810
812 using pointer = const value_type*;
813
815 using reference = const value_type&;
816
817 //---------------------------
818 // Construction
819 //---------------------------
820
824 ConstIterator() = default;
825
830 inline explicit ConstIterator( const Node* node );
831
836 inline ConstIterator( const Iterator& it );
837
838 //---------------------------
839 // Operations
840 //---------------------------
841
846 inline reference operator*() const;
847
852 inline pointer operator->() const;
853
859
865
871
877
878 //---------------------------
879 // Comparison
880 //---------------------------
881
887 inline bool operator==( const ConstIterator& other ) const;
888
894 inline bool operator!=( const ConstIterator& other ) const;
895
896 private:
897 //---------------------------
898 // Private members
899 //---------------------------
900
906 inline ConstIterator( const Node* node, const OrderedHashMap* container );
907
908 const Node* m_node = nullptr;
909 const OrderedHashMap* m_container = nullptr;
910 };
911 };
912} // namespace nfx::containers
913
914#include "nfx/detail/containers/OrderedHashMap.inl"
const TValue * find(const KeyType &key) const noexcept
Fast const lookup with heterogeneous key types (C++ idiom: pointer return).
ConstIterator cbegin() const noexcept
Get const iterator to first element in insertion order (explicit const).
std::pair< const TKey, TValue > value_type
Type alias for key-value pair type.
KeyEqual key_equal
Type alias for key equality comparator.
const TValue & at(const TKey &key) const
Checked const element access with bounds checking.
void swap(OrderedHashMap &other) noexcept
Swap contents with another map.
TValue & operator[](TKey &&key)
STL-compatible subscript operator with move semantics.
void reserve(size_t minCapacity)
Reserve capacity for at least the specified number of elements.
THasher hasher
Type alias for hasher type.
OrderedHashMap(size_t initialCapacity)
Constructor with specified initial capacity.
Iterator end() noexcept
Get iterator to end (past last element in insertion order).
bool contains(const KeyType &key) const noexcept
Check if a key exists in the map.
OrderedHashMap(std::initializer_list< std::pair< TKey, TValue > > init)
Construct map from initializer_list.
std::pair< Iterator, bool > tryEmplace(const TKey &key, Args &&... args)
Try to emplace a value if key doesn't exist.
OrderedHashMap & operator=(OrderedHashMap &&other) noexcept
Move assignment operator.
TValue * find(const KeyType &key) noexcept
Fast lookup with heterogeneous key types (C++ idiom: pointer return).
Iterator iterator
Type alias for iterator.
TValue mapped_type
Type alias for mapped value type.
bool insert(const TKey &key, TValue &&value)
Insert a key-value pair only if key doesn't exist (move semantics).
TKey key_type
Type alias for key type.
std::ptrdiff_t difference_type
Type alias for difference type.
void merge(OrderedHashMap &other)
Merge elements from another map into this map.
OrderedHashMap(InputIt first, InputIt last)
Construct map from iterator range.
void emplace(const TKey &key, Args &&... args)
Emplace a value in-place for the given key.
void emplace(TKey &&key, Args &&... args)
Emplace a value in-place for the given key (move key).
void insertOrAssign(TKey &&key, TValue &&value)
Insert or update a key-value pair (perfect forwarding for both key and value).
void insertOrAssign(const TKey &key, const TValue &value)
Insert or update a key-value pair (copy semantics).
void clear() noexcept
Clear all elements from the map.
Iterator erase(ConstIterator first, ConstIterator last) noexcept
Erase range of elements.
bool isEmpty() const noexcept
Check if the map is empty.
Iterator erase(ConstIterator pos) noexcept
Erase element at iterator position.
OrderedHashMap()
Default constructor with initial capacity of 32 elements.
TValue & at(const TKey &key)
Checked element access with bounds checking.
Iterator begin() noexcept
Get iterator to first element in insertion order.
TValue & at(const KeyType &key)
Checked element access with bounds checking.
bool erase(const KeyType &key) noexcept
Remove a key-value pair from the map.
size_t size() const noexcept
Get the number of elements in the map.
OrderedHashMap & operator=(const OrderedHashMap &other)
Copy assignment operator.
ConstIterator const_iterator
Type alias for const iterator.
OrderedHashMap(OrderedHashMap &&other) noexcept
Move constructor.
ConstIterator cend() const noexcept
Get const iterator to end (explicit const).
bool insert(TKey &&key, TValue &&value)
Insert a key-value pair only if key doesn't exist (perfect forwarding).
const TValue & at(const KeyType &key) const
Checked const element access with bounds checking.
HashType hash_type
Type alias for hash type (uint32_t or uint64_t).
void insertOrAssign(const TKey &key, TValue &&value)
Insert or update a key-value pair (move semantics).
std::pair< Iterator, bool > tryEmplace(TKey &&key, Args &&... args)
Try to emplace a value if key doesn't exist.
std::optional< std::pair< TKey, TValue > > extract(const KeyType &key)
Extract and remove a key-value pair from the map.
TValue & operator[](const TKey &key)
STL-compatible subscript operator (insert-if-missing).
size_t size_type
Type alias for size type.
OrderedHashMap(const OrderedHashMap &other)
Copy constructor.
bool insert(const TKey &key, const TValue &value)
Insert a key-value pair only if key doesn't exist (copy semantics).
size_t capacity() const noexcept
Get the current capacity of the hash table.
Iterator for OrderedHashMap that follows insertion order.
value_type * pointer
STL iterator pointer type.
Iterator & operator++()
Pre-increment operator to advance to next element in insertion order.
Iterator & operator--()
Pre-decrement operator to move to previous element in insertion order.
Iterator operator++(int)
Post-increment operator to advance to next element in insertion order.
value_type & reference
STL iterator reference type.
std::bidirectional_iterator_tag iterator_category
STL iterator category (bidirectional iterator).
std::pair< const TKey, TValue > value_type
STL iterator value type (key-value pair).
reference operator*() const
Dereference operator to access key-value pair.
Iterator()=default
Default constructor creates an invalid iterator.
Iterator operator--(int)
Post-decrement operator to move to previous element in insertion order.
std::ptrdiff_t difference_type
STL iterator difference type.
Iterator(Node *node)
Construct iterator from node.
pointer operator->() const
Arrow operator to access key-value pair members.
bool operator!=(const Iterator &other) const
Inequality comparison operator.
bool operator==(const Iterator &other) const
Equality comparison operator.
Const iterator for OrderedHashMap that follows insertion order.
reference operator*() const
Dereference operator to access key-value pair.
ConstIterator(const Node *node)
Construct const iterator from node.
ConstIterator()=default
Default constructor creates an invalid iterator.
ConstIterator & operator--()
Pre-decrement operator to move to previous element in insertion order.
std::bidirectional_iterator_tag iterator_category
STL iterator category (bidirectional iterator).
std::ptrdiff_t difference_type
STL iterator difference type.
ConstIterator & operator++()
Pre-increment operator to advance to next element in insertion order.
const value_type & reference
STL iterator reference type.
std::pair< const TKey, TValue > value_type
STL iterator value type (const key-value pair).
ConstIterator operator++(int)
Post-increment operator to advance to next element in insertion order.
bool operator==(const ConstIterator &other) const
Equality comparison operator.
ConstIterator operator--(int)
Post-decrement operator to move to previous element in insertion order.
bool operator!=(const ConstIterator &other) const
Inequality comparison operator.
const value_type * pointer
STL iterator pointer type.
ConstIterator(const Iterator &it)
Convert from non-const iterator.
pointer operator->() const
Arrow operator to access key-value pair members.