/src/ninja/src/third_party/emhash/hash_table8.hpp
Line | Count | Source |
1 | | // version 1.7.4 |
2 | | // https://github.com/ktprime/emhash/blob/master/hash_table8.hpp |
3 | | // |
4 | | // Licensed under the MIT License <http://opensource.org/licenses/MIT>. |
5 | | // SPDX-License-Identifier: MIT |
6 | | // Copyright (c) 2021-2026 Huang Yuanbing & bailuzhou AT 163.com |
7 | | // |
8 | | // Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | | // of this software and associated documentation files (the "Software"), to deal |
10 | | // in the Software without restriction, including without limitation the rights |
11 | | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
12 | | // copies of the Software, and to permit persons to whom the Software is |
13 | | // furnished to do so, subject to the following conditions: |
14 | | // |
15 | | // The above copyright notice and this permission notice shall be included in all |
16 | | // copies or substantial portions of the Software. |
17 | | // |
18 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21 | | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
23 | | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
24 | | // SOFTWARE |
25 | | |
26 | | /// @file hash_table8.hpp |
27 | | /// @brief Split-index + dense-pairs open addressing hash map (emhash8) |
28 | | /// @version 1.7.4 |
29 | | /// @copyright Copyright (c) 2021-2026 Huang Yuanbing |
30 | | |
31 | | #pragma once |
32 | | |
33 | | #include <cstring> |
34 | | #include <string> |
35 | | #include <cstdlib> |
36 | | #include <type_traits> |
37 | | #include <cassert> |
38 | | #include <utility> |
39 | | #include <cstdint> |
40 | | #include <functional> |
41 | | #include <iterator> |
42 | | #include <algorithm> |
43 | | #include <memory> |
44 | | |
45 | | #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) |
46 | | #include <xmmintrin.h> |
47 | | #elif _WIN32 && defined(_M_ARM64) |
48 | | #include <intrin.h> |
49 | | #endif |
50 | | |
51 | | #undef EMH_NEW |
52 | | #undef EMH_EMPTY |
53 | | #undef EMH_EQHASH |
54 | | |
55 | | // likely/unlikely |
56 | | #if defined(__GNUC__) && (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1) || defined(__clang__) |
57 | 1.05M | #define EMH_LIKELY(condition) __builtin_expect(!!(condition), 1) |
58 | 204k | #define EMH_UNLIKELY(condition) __builtin_expect(!!(condition), 0) |
59 | | #elif defined(_MSC_VER) && (_MSC_VER >= 1920) |
60 | | #define EMH_LIKELY(condition) ((condition) ? ((void)__assume(condition), 1) : 0) |
61 | | #define EMH_UNLIKELY(condition) ((condition) ? 1 : ((void)__assume(!(condition)), 0)) |
62 | | #else |
63 | | #define EMH_LIKELY(condition) (condition) |
64 | | #define EMH_UNLIKELY(condition) (condition) |
65 | | #endif |
66 | | |
67 | 1.81M | #define EMH_EMPTY(n) (0 > (int)(_index[n].next)) |
68 | 1.41M | #define EMH_EQHASH(n, key_hash) (((size_type)(key_hash) & ~_mask) == (_index[n].slot & ~_mask)) |
69 | | //#define EMH_EQHASH(n, key_hash) ((size_type)(key_hash - _index[n].slot) & ~_mask) == 0 |
70 | | #define EMH_NEW(key, val, bucket, key_hash) \ |
71 | 347k | new(_pairs + _num_filled) value_type(key, val); \ |
72 | 347k | _etail = bucket; \ |
73 | 347k | _index[bucket] = {bucket, _num_filled++ | ((size_type)(key_hash) & ~_mask)} |
74 | | |
75 | | namespace emhash8 { |
76 | | |
77 | | struct DefaultPolicy { |
78 | | static constexpr float load_factor = 0.80f; |
79 | | static constexpr float min_load_factor = 0.20f; |
80 | | static constexpr size_t cacheline_size = 64U; |
81 | | }; |
82 | | |
83 | | /// @brief High-performance hash map with split index + dense pairs layout. |
84 | | /// |
85 | | /// emhash8 uses a two-array design: |
86 | | /// - `_index[]` maps buckets to slots via linked-bucket chains |
87 | | /// - `_pairs[]` stores key-value pairs in a dense, packed array |
88 | | /// |
89 | | /// This layout provides extremely fast iteration (sequential scan of `_pairs`) |
90 | | /// and is ideal for complex/large key or value types. |
91 | | /// |
92 | | /// @tparam KeyT Key type |
93 | | /// @tparam ValueT Mapped value type |
94 | | /// @tparam HashT Hash functor (default: std::hash<KeyT>) |
95 | | /// @tparam EqT Key equality functor (default: std::equal_to<KeyT>) |
96 | | /// @tparam AllocT Allocator type (default: std::allocator<std::pair<KeyT, ValueT>>) |
97 | | /// @tparam Policy Configuration policy (default: DefaultPolicy) |
98 | | /// |
99 | | /// @note Header-only: just `#include "emhash/hash_table8.hpp"` and use `emhash8::HashMap`. |
100 | | /// @note Not thread-safe. Concurrent read-only access is safe. |
101 | | template<typename KeyT, typename ValueT, |
102 | | typename HashT = std::hash<KeyT>, |
103 | | typename EqT = std::equal_to<KeyT>, |
104 | | typename AllocT = std::allocator<std::pair<KeyT, ValueT>>, |
105 | | typename Policy = DefaultPolicy> |
106 | | class HashMap |
107 | | { |
108 | | static_assert(std::is_copy_constructible<KeyT>::value || std::is_move_constructible<KeyT>::value, |
109 | | "KeyT must be copy-constructible or move-constructible"); |
110 | | static_assert(std::is_copy_constructible<ValueT>::value || std::is_move_constructible<ValueT>::value, |
111 | | "ValueT must be copy-constructible or move-constructible"); |
112 | | |
113 | | #ifndef EMH_DEFAULT_LOAD_FACTOR |
114 | | constexpr static float EMH_DEFAULT_LOAD_FACTOR = 0.80f; |
115 | | #endif |
116 | | constexpr static float EMH_MIN_LOAD_FACTOR = 0.25f; //< 0.5 |
117 | | #ifndef EMH_CACHE_LINE_SIZE |
118 | | constexpr static uint32_t EMH_CACHE_LINE_SIZE = 64; |
119 | | #endif |
120 | | |
121 | | public: |
122 | | using htype = HashMap<KeyT, ValueT, HashT, EqT, AllocT, Policy>; |
123 | | using value_type = std::pair<KeyT, ValueT>; //TODO set to const KeyT |
124 | | // using value_type = std::pair<const KeyT, ValueT>; //TODO set to const KeyT |
125 | | using key_type = const KeyT; |
126 | | using mapped_type = ValueT; |
127 | | //using dPolicy = Policy; |
128 | | |
129 | | #ifdef EMH_SMALL_TYPE |
130 | | using size_type = uint16_t; |
131 | | #elif EMH_SIZE_TYPE == 0 |
132 | | using size_type = uint32_t; |
133 | | #else |
134 | | using size_type = uint64_t; |
135 | | #endif |
136 | | |
137 | | using hasher = HashT; |
138 | | using key_equal = EqT; |
139 | | using allocator_type = AllocT; |
140 | | |
141 | | constexpr static size_type INACTIVE = size_type(-1); |
142 | | constexpr static size_type EAD = 2; |
143 | | |
144 | | struct Index |
145 | | { |
146 | | size_type next; |
147 | | size_type slot; |
148 | | }; |
149 | | |
150 | | template <bool IsConst, typename HashMapType> |
151 | | class hashmap_iterator |
152 | | { |
153 | | public: |
154 | | using iterator_category = std::bidirectional_iterator_tag; |
155 | | using difference_type = std::ptrdiff_t; |
156 | | using value_type = typename HashMapType::value_type; |
157 | | using reference = std::conditional_t<IsConst, const value_type&, value_type&>; |
158 | | using pointer = std::conditional_t<IsConst, const value_type*, value_type*>; |
159 | | using hash_map_type = std::conditional_t<IsConst, const HashMapType, HashMapType>; |
160 | | |
161 | | constexpr hashmap_iterator() noexcept : kv_(nullptr) {} |
162 | | constexpr hashmap_iterator(pointer kv) noexcept : kv_(kv) {} |
163 | | constexpr hashmap_iterator(hash_map_type* hash_map, size_type bucket) noexcept |
164 | 2.11M | : kv_(hash_map->_pairs + bucket) {}emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::hashmap_iterator(emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> const*, unsigned int) Line | Count | Source | 164 | 2.11M | : kv_(hash_map->_pairs + bucket) {} |
Unexecuted instantiation: emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::hashmap_iterator(emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>*, unsigned int) Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::hashmap_iterator(emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>*, unsigned int) Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::hashmap_iterator(emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> const*, unsigned int) |
165 | | |
166 | | // Non-const to const iterator conversion (not needed when already const) |
167 | | #if defined(__clang__) |
168 | | #pragma clang diagnostic push |
169 | | #pragma clang diagnostic ignored "-Wclass-conversion" |
170 | | #endif |
171 | | template <bool OtherIsConst = IsConst, std::enable_if_t<!OtherIsConst, int> = 0> |
172 | | constexpr operator hashmap_iterator<true, HashMapType>() const noexcept { |
173 | | return hashmap_iterator<true, HashMapType>(kv_); |
174 | | } |
175 | | #if defined(__clang__) |
176 | | #pragma clang diagnostic pop |
177 | | #endif |
178 | | |
179 | | constexpr hashmap_iterator& operator++() noexcept |
180 | 0 | { |
181 | 0 | ++kv_; |
182 | 0 | return *this; |
183 | 0 | } Unexecuted instantiation: emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::operator++() Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::operator++() |
184 | | |
185 | | constexpr hashmap_iterator operator++(int) noexcept |
186 | | { |
187 | | auto copy = *this; |
188 | | ++(*this); |
189 | | return copy; |
190 | | } |
191 | | |
192 | | constexpr hashmap_iterator& operator--() noexcept |
193 | | { |
194 | | --kv_; |
195 | | return *this; |
196 | | } |
197 | | |
198 | | constexpr hashmap_iterator operator--(int) noexcept |
199 | | { |
200 | | auto copy = *this; |
201 | | --(*this); |
202 | | return copy; |
203 | | } |
204 | | |
205 | | constexpr hashmap_iterator operator+(difference_type diff) const noexcept |
206 | | { |
207 | | auto copy = *this; |
208 | | copy.kv_ += diff; |
209 | | return copy; |
210 | | } |
211 | | |
212 | 0 | constexpr reference operator*() const noexcept { return *kv_; } |
213 | 708k | constexpr pointer operator->() const noexcept { return kv_; }emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::operator->() const Line | Count | Source | 213 | 708k | constexpr pointer operator->() const noexcept { return kv_; } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::operator->() const Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::operator->() const Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::operator->() const |
214 | | |
215 | | template <bool OtherIsConst> |
216 | | constexpr bool operator==(const hashmap_iterator<OtherIsConst, HashMapType>& rhs) const noexcept |
217 | 1.05M | { |
218 | 1.05M | return kv_ == rhs.kv_; |
219 | 1.05M | } bool emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::operator==<true>(emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> > const&) const Line | Count | Source | 217 | 1.05M | { | 218 | 1.05M | return kv_ == rhs.kv_; | 219 | 1.05M | } |
Unexecuted instantiation: bool emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::operator==<false>(emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> > const&) const Unexecuted instantiation: bool emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::operator==<false>(emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> > const&) const Unexecuted instantiation: bool emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::operator==<true>(emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> > const&) const |
220 | | |
221 | | template <bool OtherIsConst> |
222 | | constexpr bool operator!=(const hashmap_iterator<OtherIsConst, HashMapType>& rhs) const noexcept |
223 | 1.05M | { |
224 | 1.05M | return !(*this == rhs); |
225 | 1.05M | } bool emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::operator!=<true>(emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> > const&) const Line | Count | Source | 223 | 1.05M | { | 224 | 1.05M | return !(*this == rhs); | 225 | 1.05M | } |
Unexecuted instantiation: bool emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> >::operator!=<false>(emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> > const&) const Unexecuted instantiation: bool emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::operator!=<false>(emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<false, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> > const&) const Unexecuted instantiation: bool emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> >::operator!=<true>(emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> > const&) const |
226 | | |
227 | | private: |
228 | | pointer kv_; |
229 | | template <bool, typename> friend class hashmap_iterator; |
230 | | friend HashMapType; |
231 | | }; |
232 | | |
233 | | using iterator = hashmap_iterator<false, htype>; |
234 | | using const_iterator = hashmap_iterator<true, htype>; |
235 | | |
236 | | void init(size_type bucket, float mlf = EMH_DEFAULT_LOAD_FACTOR) |
237 | 617 | { |
238 | 617 | _pairs = nullptr; |
239 | 617 | _index = nullptr; |
240 | 617 | _mask = _num_buckets = 0; |
241 | 617 | _num_filled = 0; |
242 | 617 | _pairs_capacity = 0; |
243 | 617 | _mlf = (uint32_t)((1 << 28) / EMH_DEFAULT_LOAD_FACTOR); |
244 | 617 | max_load_factor(mlf); |
245 | 617 | rehash(bucket); |
246 | 617 | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::init(unsigned int, float) Line | Count | Source | 237 | 617 | { | 238 | 617 | _pairs = nullptr; | 239 | 617 | _index = nullptr; | 240 | 617 | _mask = _num_buckets = 0; | 241 | 617 | _num_filled = 0; | 242 | 617 | _pairs_capacity = 0; | 243 | 617 | _mlf = (uint32_t)((1 << 28) / EMH_DEFAULT_LOAD_FACTOR); | 244 | 617 | max_load_factor(mlf); | 245 | 617 | rehash(bucket); | 246 | 617 | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::init(unsigned int, float) |
247 | | |
248 | | HashMap(size_type bucket = 2, float mlf = EMH_DEFAULT_LOAD_FACTOR) |
249 | 617 | { |
250 | 617 | init(bucket, mlf); |
251 | 617 | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::HashMap(unsigned int, float) Line | Count | Source | 249 | 617 | { | 250 | 617 | init(bucket, mlf); | 251 | 617 | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::HashMap(unsigned int, float) |
252 | | |
253 | | HashMap(const HashMap& rhs) |
254 | | : _pair_allocator(PairAllocTraits::select_on_container_copy_construction(rhs._pair_allocator)) |
255 | | , _index_allocator(IndexAllocTraits::select_on_container_copy_construction(rhs._index_allocator)) |
256 | | { |
257 | | if (rhs.load_factor() > EMH_MIN_LOAD_FACTOR) { |
258 | | _pairs_capacity = (size_type)((float)rhs._num_buckets * rhs.max_load_factor()) + 4; |
259 | | _pairs = alloc_bucket(_pairs_capacity); |
260 | | _index = alloc_index(rhs._num_buckets); |
261 | | clone(rhs); |
262 | | } else { |
263 | | init(rhs._num_filled + 2, rhs.max_load_factor()); |
264 | | for (auto it = rhs.begin(); it != rhs.end(); ++it) |
265 | | insert_unique(it->first, it->second); |
266 | | } |
267 | | } |
268 | | |
269 | | HashMap(HashMap&& rhs) noexcept |
270 | | : _pair_allocator(std::move(rhs._pair_allocator)) |
271 | | , _index_allocator(std::move(rhs._index_allocator)) |
272 | | { |
273 | | init(0); |
274 | | *this = std::move(rhs); |
275 | | } |
276 | | |
277 | | HashMap(std::initializer_list<value_type> ilist) |
278 | | { |
279 | | init((size_type)ilist.size()); |
280 | | for (auto it = ilist.begin(); it != ilist.end(); ++it) |
281 | | do_insert(*it); |
282 | | } |
283 | | |
284 | | template<class InputIt> |
285 | | HashMap(InputIt first, InputIt last, size_type bucket_count=4) |
286 | | { |
287 | | init((size_type)std::distance(first, last) + bucket_count); |
288 | | for (; first != last; ++first) |
289 | | emplace(*first); |
290 | | } |
291 | | |
292 | | explicit HashMap(const allocator_type& alloc) |
293 | | : _pair_allocator(alloc) |
294 | | , _index_allocator(alloc) |
295 | | { |
296 | | init(2); |
297 | | } |
298 | | |
299 | | HashMap(size_type bucket, float mlf, const allocator_type& alloc) |
300 | | : _pair_allocator(alloc) |
301 | | , _index_allocator(alloc) |
302 | | { |
303 | | init(bucket, mlf); |
304 | | } |
305 | | |
306 | | HashMap(const HashMap& rhs, const allocator_type& alloc) |
307 | | : _pair_allocator(alloc) |
308 | | , _index_allocator(alloc) |
309 | | { |
310 | | if (rhs.load_factor() > EMH_MIN_LOAD_FACTOR) { |
311 | | _pairs_capacity = (size_type)((float)rhs._num_buckets * rhs.max_load_factor()) + 4; |
312 | | _pairs = alloc_bucket(_pairs_capacity); |
313 | | _index = alloc_index(rhs._num_buckets); |
314 | | clone(rhs); |
315 | | } else { |
316 | | init(rhs._num_filled + 2, rhs.max_load_factor()); |
317 | | for (auto it = rhs.begin(); it != rhs.end(); ++it) |
318 | | insert_unique(it->first, it->second); |
319 | | } |
320 | | } |
321 | | |
322 | | HashMap(HashMap&& rhs, const allocator_type& alloc) noexcept |
323 | | : _pair_allocator(alloc) |
324 | | , _index_allocator(alloc) |
325 | | { |
326 | | init(0); |
327 | | *this = std::move(rhs); |
328 | | } |
329 | | |
330 | | HashMap& operator=(const HashMap& rhs) |
331 | | { |
332 | | if (this == &rhs) |
333 | | return *this; |
334 | | |
335 | | if constexpr (PairAllocTraits::propagate_on_container_copy_assignment::value) { |
336 | | _pair_allocator = rhs._pair_allocator; |
337 | | _index_allocator = rhs._index_allocator; |
338 | | } |
339 | | |
340 | | if (rhs.load_factor() < EMH_MIN_LOAD_FACTOR) { |
341 | | clear(); dealloc_bucket(_pairs, _pairs_capacity); _pairs = nullptr; _pairs_capacity = 0; |
342 | | rehash(rhs._num_filled + 2); |
343 | | for (auto it = rhs.begin(); it != rhs.end(); ++it) |
344 | | insert_unique(it->first, it->second); |
345 | | return *this; |
346 | | } |
347 | | |
348 | | clearkv(); |
349 | | |
350 | | if (_num_buckets != rhs._num_buckets) { |
351 | | dealloc_bucket(_pairs, _pairs_capacity); dealloc_index(_index, _num_buckets); |
352 | | _index = alloc_index(rhs._num_buckets); |
353 | | _pairs_capacity = (size_type)((float)rhs._num_buckets * rhs.max_load_factor()) + 4; |
354 | | _pairs = alloc_bucket(_pairs_capacity); |
355 | | } |
356 | | |
357 | | clone(rhs); |
358 | | return *this; |
359 | | } |
360 | | |
361 | | HashMap& operator=(HashMap&& rhs) noexcept |
362 | | { |
363 | | if (this != &rhs) { |
364 | | swap(rhs); |
365 | | rhs.clear(); |
366 | | } |
367 | | return *this; |
368 | | } |
369 | | |
370 | | template<typename Con> |
371 | | bool operator == (const Con& rhs) const |
372 | | { |
373 | | if (size() != rhs.size()) |
374 | | return false; |
375 | | |
376 | | for (auto it = begin(), last = end(); it != last; ++it) { |
377 | | auto oi = rhs.find(it->first); |
378 | | if (oi == rhs.end() || it->second != oi->second) |
379 | | return false; |
380 | | } |
381 | | return true; |
382 | | } |
383 | | |
384 | | template<typename Con> |
385 | | bool operator != (const Con& rhs) const { return !(*this == rhs); } |
386 | | |
387 | | ~HashMap() noexcept |
388 | 617 | { |
389 | 617 | clearkv(); |
390 | 617 | dealloc_bucket(_pairs, _pairs_capacity); |
391 | 617 | dealloc_index(_index, _num_buckets); |
392 | 617 | _num_filled = 0; |
393 | 617 | _index = nullptr; |
394 | 617 | _pairs = nullptr; |
395 | 617 | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::~HashMap() Line | Count | Source | 388 | 617 | { | 389 | 617 | clearkv(); | 390 | 617 | dealloc_bucket(_pairs, _pairs_capacity); | 391 | 617 | dealloc_index(_index, _num_buckets); | 392 | 617 | _num_filled = 0; | 393 | 617 | _index = nullptr; | 394 | 617 | _pairs = nullptr; | 395 | 617 | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::~HashMap() |
396 | | |
397 | | void clone(const HashMap& rhs) |
398 | | { |
399 | | _hasher = rhs._hasher; |
400 | | // _eq = rhs._eq; |
401 | | _num_buckets = rhs._num_buckets; |
402 | | _num_filled = rhs._num_filled; |
403 | | _pairs_capacity = rhs._pairs_capacity; |
404 | | _mlf = rhs._mlf; |
405 | | _last = rhs._last; |
406 | | _mask = rhs._mask; |
407 | | #if EMH_HIGH_LOAD |
408 | | _ehead = rhs._ehead; |
409 | | #endif |
410 | | _etail = rhs._etail; |
411 | | |
412 | | auto opairs = rhs._pairs; |
413 | | memcpy((char*)_index, (char*)rhs._index, (_num_buckets + EAD) * sizeof(Index)); |
414 | | |
415 | | if (is_trivially_copyable()) { |
416 | | memcpy((char*)_pairs, (char*)opairs, _num_filled * sizeof(value_type)); |
417 | | } else { |
418 | | for (size_type slot = 0; slot < _num_filled; slot++) |
419 | | new(_pairs + slot) value_type(opairs[slot]); |
420 | | } |
421 | | } |
422 | | |
423 | | void swap(HashMap& rhs) |
424 | | { |
425 | | // std::swap(_eq, rhs._eq); |
426 | | std::swap(_hasher, rhs._hasher); |
427 | | std::swap(_pairs, rhs._pairs); |
428 | | std::swap(_index, rhs._index); |
429 | | std::swap(_num_buckets, rhs._num_buckets); |
430 | | std::swap(_num_filled, rhs._num_filled); |
431 | | std::swap(_pairs_capacity, rhs._pairs_capacity); |
432 | | std::swap(_mask, rhs._mask); |
433 | | std::swap(_mlf, rhs._mlf); |
434 | | std::swap(_last, rhs._last); |
435 | | #if EMH_HIGH_LOAD |
436 | | std::swap(_ehead, rhs._ehead); |
437 | | #endif |
438 | | std::swap(_etail, rhs._etail); |
439 | | std::swap(_pair_allocator, rhs._pair_allocator); |
440 | | std::swap(_index_allocator, rhs._index_allocator); |
441 | | } |
442 | | |
443 | | // ------------------------------------------------------------- |
444 | 0 | iterator first() { return iterator{this, 0}; }Unexecuted instantiation: emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::first() Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::first() |
445 | | iterator last() { return iterator{this, _num_filled - 1}; } |
446 | | const_iterator first() const { return const_iterator{ this, 0 }; } |
447 | | const_iterator last() const { return const_iterator{ this, _num_filled - 1 }; } |
448 | | |
449 | | //no exception if empty |
450 | | value_type& front() { return _pairs[0]; } |
451 | | const value_type& front() const { return _pairs[0]; } |
452 | | value_type& back() { return _pairs[_num_filled - 1]; } |
453 | | const value_type& back() const { return _pairs[_num_filled - 1]; } |
454 | | |
455 | | void pop_front() { erase(begin()); } //TODO. only erase first without move last |
456 | | void pop_back() { erase(last()); } |
457 | | |
458 | 0 | constexpr iterator begin() { return first(); }Unexecuted instantiation: emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::begin() Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::begin() |
459 | | constexpr const_iterator cbegin() const { return first(); } |
460 | | constexpr const_iterator begin() const { return first(); } |
461 | | |
462 | 0 | constexpr iterator end() { return { this, _num_filled }; }Unexecuted instantiation: emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::end() Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::end() |
463 | 1.05M | constexpr const_iterator cend() const { return { this, _num_filled }; }emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::cend() const Line | Count | Source | 463 | 1.05M | constexpr const_iterator cend() const { return { this, _num_filled }; } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::cend() const |
464 | 1.05M | constexpr const_iterator end() const { return cend(); }emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::end() const Line | Count | Source | 464 | 1.05M | constexpr const_iterator end() const { return cend(); } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::end() const |
465 | | |
466 | | const value_type* values() const { return _pairs; } |
467 | | const Index* index() const { return _index; } |
468 | | |
469 | | size_type size() const { return _num_filled; } |
470 | | bool empty() const { return _num_filled == 0; } |
471 | | size_type bucket_count() const { return _num_buckets; } |
472 | | float load_factor() const { return static_cast<float>(_num_filled) / ((float)_mask + 1.0f); } |
473 | | |
474 | | const HashT& hash_function() const { return _hasher; } |
475 | | const EqT& key_eq() const { return _eq; } |
476 | | allocator_type get_allocator() const { return allocator_type(_pair_allocator); } |
477 | | |
478 | | void max_load_factor(float mlf) |
479 | 617 | { |
480 | 617 | if (mlf <= 0.999 && mlf > EMH_MIN_LOAD_FACTOR) { |
481 | 617 | _mlf = (uint32_t)((1 << 28) / mlf); |
482 | | //if (_num_buckets > 0) rehash(_num_buckets); |
483 | 617 | } |
484 | 617 | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::max_load_factor(float) Line | Count | Source | 479 | 617 | { | 480 | 617 | if (mlf <= 0.999 && mlf > EMH_MIN_LOAD_FACTOR) { | 481 | 617 | _mlf = (uint32_t)((1 << 28) / mlf); | 482 | | //if (_num_buckets > 0) rehash(_num_buckets); | 483 | 617 | } | 484 | 617 | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::max_load_factor(float) |
485 | | |
486 | 2.73k | constexpr float max_load_factor() const { return (1 << 28) / (float)_mlf; }emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::max_load_factor() const Line | Count | Source | 486 | 2.73k | constexpr float max_load_factor() const { return (1 << 28) / (float)_mlf; } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::max_load_factor() const |
487 | 2.73k | constexpr uint64_t max_size() const { return 1ull << (sizeof(_num_buckets) * 8 - 1); }emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::max_size() const Line | Count | Source | 487 | 2.73k | constexpr uint64_t max_size() const { return 1ull << (sizeof(_num_buckets) * 8 - 1); } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::max_size() const |
488 | | constexpr uint64_t max_bucket_count() const { return max_size(); } |
489 | | |
490 | | #if EMH_STATIS |
491 | | //Returns the bucket number where the element with key k is located. |
492 | | size_type bucket(const KeyT& key) const |
493 | | { |
494 | | const auto bucket = hash_bucket(key); |
495 | | const auto next_bucket = _index[bucket].next; |
496 | | if ((int)next_bucket < 0) |
497 | | return 0; |
498 | | else if (bucket == next_bucket) |
499 | | return bucket + 1; |
500 | | |
501 | | return hash_main(bucket) + 1; |
502 | | } |
503 | | |
504 | | //Returns the number of collision elements in pos bucket. |
505 | | size_type bucket_size(const size_type bucket) const |
506 | | { |
507 | | auto next_bucket = _index[bucket].next; |
508 | | if ((int)next_bucket < 0) |
509 | | return 0; |
510 | | |
511 | | next_bucket = hash_main(bucket); |
512 | | size_type ibucket_size = 1; |
513 | | |
514 | | while (true) { |
515 | | const auto nbucket = _index[next_bucket].next; |
516 | | if (nbucket == next_bucket) { |
517 | | break; |
518 | | } |
519 | | ibucket_size ++; |
520 | | next_bucket = nbucket; |
521 | | } |
522 | | return ibucket_size; |
523 | | } |
524 | | |
525 | | size_type get_main_bucket(const size_type bucket) const |
526 | | { |
527 | | auto next_bucket = _index[bucket].next; |
528 | | if ((int)next_bucket < 0) |
529 | | return INACTIVE; |
530 | | |
531 | | return hash_main(bucket); |
532 | | } |
533 | | |
534 | | size_type get_diss(size_type bucket, size_type next_bucket, const size_type slots) const |
535 | | { |
536 | | auto pbucket = reinterpret_cast<uint64_t>(&_pairs[bucket]); |
537 | | auto pnext = reinterpret_cast<uint64_t>(&_pairs[next_bucket]); |
538 | | if (pbucket / EMH_CACHE_LINE_SIZE == pnext / EMH_CACHE_LINE_SIZE) |
539 | | return 0; |
540 | | size_type diff = pbucket > pnext ? (pbucket - pnext) : (pnext - pbucket); |
541 | | if (diff / EMH_CACHE_LINE_SIZE < slots - 1) |
542 | | return diff / EMH_CACHE_LINE_SIZE + 1; |
543 | | return slots - 1; |
544 | | } |
545 | | |
546 | | int get_bucket_info(const size_type bucket, size_type steps[], const size_type slots) const |
547 | | { |
548 | | auto next_bucket = _index[bucket].next; |
549 | | if ((int)next_bucket < 0) |
550 | | return -1; |
551 | | |
552 | | const auto main_bucket = hash_main(bucket); |
553 | | if (next_bucket == main_bucket) |
554 | | return 1; |
555 | | else if (main_bucket != bucket) |
556 | | return 0; |
557 | | |
558 | | steps[get_diss(bucket, next_bucket, slots)] ++; |
559 | | size_type ibucket_size = 2; |
560 | | //find a empty and linked it to tail |
561 | | while (true) { |
562 | | const auto nbucket = _index[next_bucket].next; |
563 | | if (nbucket == next_bucket) |
564 | | break; |
565 | | |
566 | | steps[get_diss(nbucket, next_bucket, slots)] ++; |
567 | | ibucket_size ++; |
568 | | next_bucket = nbucket; |
569 | | } |
570 | | return (int)ibucket_size; |
571 | | } |
572 | | |
573 | | void dump_statics() const |
574 | | { |
575 | | const size_type slots = 128; |
576 | | size_type buckets[slots + 1] = {0}; |
577 | | size_type steps[slots + 1] = {0}; |
578 | | for (size_type bucket = 0; bucket < _num_buckets; ++bucket) { |
579 | | auto bsize = get_bucket_info(bucket, steps, slots); |
580 | | if (bsize > 0) |
581 | | buckets[bsize] ++; |
582 | | } |
583 | | |
584 | | size_type sumb = 0, collision = 0, sumc = 0, finds = 0, sumn = 0; |
585 | | puts("============== buckets size ration ========="); |
586 | | for (size_type i = 0; i < sizeof(buckets) / sizeof(buckets[0]); i++) { |
587 | | const auto bucketsi = buckets[i]; |
588 | | if (bucketsi == 0) |
589 | | continue; |
590 | | sumb += bucketsi; |
591 | | sumn += bucketsi * i; |
592 | | collision += bucketsi * (i - 1); |
593 | | finds += bucketsi * i * (i + 1) / 2; |
594 | | printf(" %2u %8u %2.2lf| %.2lf\n", i, bucketsi, bucketsi * 100.0 * i / _num_filled, sumn * 100.0 / _num_filled); |
595 | | } |
596 | | |
597 | | puts("========== collision miss ration ==========="); |
598 | | for (size_type i = 0; i < sizeof(steps) / sizeof(steps[0]); i++) { |
599 | | sumc += steps[i]; |
600 | | if (steps[i] <= 2) |
601 | | continue; |
602 | | printf(" %2u %8u %.2lf %.2lf\n", i, steps[i], steps[i] * 100.0 / collision, sumc * 100.0 / collision); |
603 | | } |
604 | | |
605 | | if (sumb == 0) return; |
606 | | printf(" _num_filled/bucket_size/packed collision/cache_miss/hit_find = %u/%.2lf/%zd/ %.2lf%%/%.2lf%%/%.2lf\n", |
607 | | _num_filled, _num_filled * 1.0 / sumb, sizeof(value_type), (collision * 100.0 / _num_filled), (collision - steps[0]) * 100.0 / _num_filled, finds * 1.0 / _num_filled); |
608 | | assert(sumn == _num_filled); |
609 | | assert(sumc == collision); |
610 | | puts("============== buckets size end ============="); |
611 | | } |
612 | | #endif |
613 | | |
614 | | //only useful for at function if not find key then return zero |
615 | | void pack_zero(ValueT zero) |
616 | | { |
617 | | _pairs[_num_filled] = { KeyT(), zero }; |
618 | | } |
619 | | |
620 | | // ------------------------------------------------------------ |
621 | | template<typename K=KeyT> |
622 | | iterator find(const K& key) noexcept |
623 | 0 | { |
624 | 0 | return {this, find_filled_slot(key)}; |
625 | 0 | } |
626 | | |
627 | | template<typename K=KeyT> |
628 | | const_iterator find(const K& key) const noexcept |
629 | 1.05M | { |
630 | 1.05M | return {this, find_filled_slot(key)}; |
631 | 1.05M | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy> > emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::find<StringPiece>(StringPiece const&) const Line | Count | Source | 629 | 1.05M | { | 630 | 1.05M | return {this, find_filled_slot(key)}; | 631 | 1.05M | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hashmap_iterator<true, emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy> > emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::find<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const |
632 | | |
633 | | //it key is not found, it will return value at end() |
634 | | template<typename K=KeyT> |
635 | | ValueT& at(const K& key) noexcept |
636 | | { |
637 | | const auto slot = find_filled_slot(key); |
638 | | return _pairs[slot].second; |
639 | | } |
640 | | |
641 | | template<typename K=KeyT> |
642 | | const ValueT& at(const K& key) const noexcept |
643 | | { |
644 | | const auto slot = find_filled_slot(key); |
645 | | return _pairs[slot].second; |
646 | | } |
647 | | |
648 | | const ValueT& index(const uint32_t index) const noexcept |
649 | | { |
650 | | return _pairs[index].second; |
651 | | } |
652 | | |
653 | | ValueT& index(const uint32_t index) noexcept |
654 | | { |
655 | | return _pairs[index].second; |
656 | | } |
657 | | |
658 | | /// @brief Check if a key exists in the map. |
659 | | /// @param key The key to search for. |
660 | | /// @return true if the key exists, false otherwise. |
661 | | /// @note Faster than count() > 0 for existence checks. |
662 | | template<typename K=KeyT> |
663 | | bool contains(const K& key) const noexcept |
664 | | { |
665 | | return find_filled_slot(key) != _num_filled; |
666 | | } |
667 | | |
668 | | template<typename K=KeyT> |
669 | | size_type count(const K& key) const noexcept |
670 | | { |
671 | | return find_filled_slot(key) == _num_filled ? 0 : 1; |
672 | | //return find_sorted_bucket(key) == END ? 0 : 1; |
673 | | //return find_hash_bucket(key) == END ? 0 : 1; |
674 | | } |
675 | | |
676 | | template<typename K=KeyT> |
677 | | std::pair<iterator, iterator> equal_range(const K& key) |
678 | | { |
679 | | const auto found = find(key); |
680 | | if (found.second == _num_filled) |
681 | | return { found, found }; |
682 | | else |
683 | | return { found, std::next(found) }; |
684 | | } |
685 | | |
686 | | void merge(HashMap& rhs) |
687 | | { |
688 | | if (empty()) { |
689 | | *this = std::move(rhs); |
690 | | return; |
691 | | } |
692 | | |
693 | | for (auto rit = rhs.begin(); rit != rhs.end(); ) { |
694 | | auto fit = find(rit->first); |
695 | | if (fit == end()) { |
696 | | insert_unique(rit->first, std::move(rit->second)); |
697 | | rit = rhs.erase(rit); |
698 | | } else { |
699 | | ++rit; |
700 | | } |
701 | | } |
702 | | } |
703 | | |
704 | | /// Returns the matching ValueT or nullptr if k isn't found. |
705 | | bool try_get(const KeyT& key, ValueT& val) const noexcept |
706 | | { |
707 | | const auto slot = find_filled_slot(key); |
708 | | const auto found = slot != _num_filled; |
709 | | if (found) { |
710 | | val = _pairs[slot].second; |
711 | | } |
712 | | return found; |
713 | | } |
714 | | |
715 | | /// @brief Get a pointer to the value for a key, or nullptr if not found. |
716 | | /// @param key The key to look up. |
717 | | /// @return Pointer to the value if found, nullptr otherwise. |
718 | | /// @note More efficient than find() + iterator dereference. |
719 | | /// @code |
720 | | /// if (auto* pval = map.try_get(key)) { use(*pval); } |
721 | | /// @endcode |
722 | | ValueT* try_get(const KeyT& key) noexcept |
723 | | { |
724 | | const auto slot = find_filled_slot(key); |
725 | | return slot != _num_filled ? &_pairs[slot].second : nullptr; |
726 | | } |
727 | | |
728 | | /// @brief Const version of try_get(). |
729 | | ValueT* try_get(const KeyT& key) const noexcept |
730 | | { |
731 | | const auto slot = find_filled_slot(key); |
732 | | return slot != _num_filled ? &_pairs[slot].second : nullptr; |
733 | | } |
734 | | |
735 | | /// @brief Set value if key exists, do nothing if it doesn't. |
736 | | /// @param key The key to look up. |
737 | | /// @param val The new value to set. |
738 | | /// @return true if the key existed and value was updated, false if key not found. |
739 | | /// @note Only available in emhash5/8. |
740 | | bool try_set(const KeyT& key, const ValueT& val) noexcept |
741 | | { |
742 | | const auto slot = find_filled_slot(key); |
743 | | if (slot == _num_filled) |
744 | | return false; |
745 | | |
746 | | _pairs[slot].second = val; |
747 | | return true; |
748 | | } |
749 | | |
750 | | /// set value if key exist |
751 | | bool try_set(const KeyT& key, ValueT&& val) noexcept |
752 | | { |
753 | | const auto slot = find_filled_slot(key); |
754 | | if (slot == _num_filled) |
755 | | return false; |
756 | | |
757 | | _pairs[slot].second = std::move(val); |
758 | | return true; |
759 | | } |
760 | | |
761 | | /// Convenience function. |
762 | | ValueT get_or_return_default(const KeyT& key) const noexcept |
763 | | { |
764 | | const auto slot = find_filled_slot(key); |
765 | | return slot == _num_filled ? ValueT() : _pairs[slot].second; |
766 | | } |
767 | | |
768 | | // ----------------------------------------------------- |
769 | | std::pair<iterator, bool> do_insert(const value_type& value) noexcept |
770 | | { |
771 | | const auto key_hash = hash_key(value.first); |
772 | | const auto bucket = find_or_allocate(value.first, key_hash); |
773 | | const auto bempty = EMH_EMPTY(bucket); |
774 | | if (bempty) { |
775 | | EMH_NEW(value.first, value.second, bucket, key_hash); |
776 | | } |
777 | | |
778 | | const auto slot = _index[bucket].slot & _mask; |
779 | | return { {this, slot}, bempty }; |
780 | | } |
781 | | |
782 | | std::pair<iterator, bool> do_insert(value_type&& value) noexcept |
783 | | { |
784 | | const auto key_hash = hash_key(value.first); |
785 | | const auto bucket = find_or_allocate(value.first, key_hash); |
786 | | const auto bempty = EMH_EMPTY(bucket); |
787 | | if (bempty) { |
788 | | EMH_NEW(std::move(value.first), std::move(value.second), bucket, key_hash); |
789 | | } |
790 | | |
791 | | const auto slot = _index[bucket].slot & _mask; |
792 | | return { {this, slot}, bempty }; |
793 | | } |
794 | | |
795 | | template<typename K, typename V> |
796 | | std::pair<iterator, bool> do_insert(K&& key, V&& val) noexcept |
797 | 0 | { |
798 | 0 | const auto key_hash = hash_key(key); |
799 | 0 | const auto bucket = find_or_allocate(key, key_hash); |
800 | 0 | const auto bempty = EMH_EMPTY(bucket); |
801 | 0 | if (bempty) { |
802 | 0 | EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash); |
803 | 0 | } |
804 | |
|
805 | 0 | const auto slot = _index[bucket].slot & _mask; |
806 | 0 | return { {this, slot}, bempty }; |
807 | 0 | } |
808 | | |
809 | | template<typename K, typename V> |
810 | | std::pair<iterator, bool> do_assign(K&& key, V&& val) noexcept |
811 | | { |
812 | | check_expand_need(); |
813 | | const auto key_hash = hash_key(key); |
814 | | const auto bucket = find_or_allocate(key, key_hash); |
815 | | const auto bempty = EMH_EMPTY(bucket); |
816 | | if (bempty) { |
817 | | EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash); |
818 | | } else { |
819 | | _pairs[_index[bucket].slot & _mask].second = std::move(val); |
820 | | } |
821 | | |
822 | | const auto slot = _index[bucket].slot & _mask; |
823 | | return { {this, slot}, bempty }; |
824 | | } |
825 | | |
826 | | std::pair<iterator, bool> insert(const value_type& p) |
827 | | { |
828 | | check_expand_need(); |
829 | | return do_insert(p); |
830 | | } |
831 | | |
832 | | std::pair<iterator, bool> insert(value_type && p) |
833 | | { |
834 | | check_expand_need(); |
835 | | return do_insert(std::move(p)); |
836 | | } |
837 | | |
838 | | template <typename Iter> |
839 | | void insert(Iter first, Iter last) |
840 | | { |
841 | | reserve(std::distance(first, last) + _num_filled, false); |
842 | | for (; first != last; ++first) |
843 | | do_insert(first->first, first->second); |
844 | | } |
845 | | |
846 | | void insert(std::initializer_list<value_type> ilist) |
847 | | { |
848 | | reserve(ilist.size() + _num_filled, false); |
849 | | for (auto it = ilist.begin(); it != ilist.end(); ++it) |
850 | | do_insert(*it); |
851 | | } |
852 | | |
853 | | #if 0 |
854 | | template <typename Iter> |
855 | | void insert_unique(Iter begin, Iter end) |
856 | | { |
857 | | reserve(std::distance(begin, end) + _num_filled, false); |
858 | | for (; begin != end; ++begin) { |
859 | | insert_unique(*begin); |
860 | | } |
861 | | } |
862 | | #endif |
863 | | |
864 | | /// @brief Insert a key-value pair without checking for duplicates. |
865 | | /// @param key The key to insert. |
866 | | /// @param val The value to insert. |
867 | | /// @return The bucket index where the element was inserted. |
868 | | /// @pre The key must NOT already exist in the map. |
869 | | /// @note 20-40% faster than insert() when uniqueness is guaranteed. |
870 | | /// @warning Inserting a duplicate key causes undefined behavior. |
871 | | template<typename K, typename V> |
872 | | size_type insert_unique(K&& key, V&& val) |
873 | | { |
874 | | check_expand_need(); |
875 | | const auto key_hash = hash_key(key); |
876 | | auto bucket = find_unique_bucket(key_hash); |
877 | | EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash); |
878 | | return bucket; |
879 | | } |
880 | | |
881 | | size_type insert_unique(value_type&& value) |
882 | | { |
883 | | return insert_unique(std::move(value.first), std::move(value.second)); |
884 | | } |
885 | | |
886 | | size_type insert_unique(const value_type& value) |
887 | | { |
888 | | return insert_unique(value.first, value.second); |
889 | | } |
890 | | |
891 | | template <class... Args> |
892 | | std::pair<iterator, bool> emplace(Args&&... args) noexcept |
893 | 0 | { |
894 | 0 | check_expand_need(); |
895 | 0 | return do_insert(std::forward<Args>(args)...); |
896 | 0 | } |
897 | | |
898 | | //no any optimize for position |
899 | | template <class... Args> |
900 | | iterator emplace_hint(const_iterator hint, Args&&... args) |
901 | | { |
902 | | (void)hint; |
903 | | check_expand_need(); |
904 | | return do_insert(std::forward<Args>(args)...).first; |
905 | | } |
906 | | |
907 | | template<class... Args> |
908 | | std::pair<iterator, bool> try_emplace(const KeyT& k, Args&&... args) |
909 | | { |
910 | | check_expand_need(); |
911 | | return do_insert(k, std::forward<Args>(args)...); |
912 | | } |
913 | | |
914 | | template<class... Args> |
915 | | std::pair<iterator, bool> try_emplace(KeyT&& k, Args&&... args) |
916 | | { |
917 | | check_expand_need(); |
918 | | return do_insert(std::move(k), std::forward<Args>(args)...); |
919 | | } |
920 | | |
921 | | template <class... Args> |
922 | | size_type emplace_unique(Args&&... args) |
923 | | { |
924 | | return insert_unique(std::forward<Args>(args)...); |
925 | | } |
926 | | |
927 | | std::pair<iterator, bool> insert_or_assign(const KeyT& key, ValueT&& val) |
928 | | { |
929 | | return do_assign(key, std::forward<ValueT>(val)); |
930 | | } |
931 | | |
932 | | std::pair<iterator, bool> insert_or_assign(KeyT&& key, ValueT&& val) |
933 | | { |
934 | | return do_assign(std::move(key), std::forward<ValueT>(val)); |
935 | | } |
936 | | |
937 | | /// Return the old value or ValueT() if it didn't exist. |
938 | | ValueT set_get(const KeyT& key, const ValueT& val) |
939 | | { |
940 | | check_expand_need(); |
941 | | const auto key_hash = hash_key(key); |
942 | | const auto bucket = find_or_allocate(key, key_hash); |
943 | | if (EMH_EMPTY(bucket)) { |
944 | | EMH_NEW(key, val, bucket, key_hash); |
945 | | return ValueT(); |
946 | | } else { |
947 | | const auto slot = _index[bucket].slot & _mask; |
948 | | ValueT old_value(val); |
949 | | std::swap(_pairs[slot].second, old_value); |
950 | | return old_value; |
951 | | } |
952 | | } |
953 | | |
954 | | /// Like std::map<KeyT, ValueT>::operator[]. |
955 | | ValueT& operator[](const KeyT& key) noexcept |
956 | | { |
957 | | check_expand_need(); |
958 | | const auto key_hash = hash_key(key); |
959 | | const auto bucket = find_or_allocate(key, key_hash); |
960 | | if (EMH_EMPTY(bucket)) { |
961 | | /* Check if inserting a value rather than overwriting an old entry */ |
962 | | EMH_NEW(key, std::move(ValueT()), bucket, key_hash); |
963 | | } |
964 | | |
965 | | const auto slot = _index[bucket].slot & _mask; |
966 | | return _pairs[slot].second; |
967 | | } |
968 | | |
969 | | ValueT& operator[](KeyT&& key) noexcept |
970 | 347k | { |
971 | 347k | check_expand_need(); |
972 | 347k | const auto key_hash = hash_key(key); |
973 | 347k | const auto bucket = find_or_allocate(key, key_hash); |
974 | 347k | if (EMH_EMPTY(bucket)) { |
975 | 347k | EMH_NEW(std::move(key), std::move(ValueT()), bucket, key_hash); |
976 | 347k | } |
977 | | |
978 | 347k | const auto slot = _index[bucket].slot & _mask; |
979 | 347k | return _pairs[slot].second; |
980 | 347k | } |
981 | | |
982 | | /// @brief Erase an element by key. |
983 | | /// @param key The key of the element to erase. |
984 | | /// @return 1 if the element was erased, 0 if the key was not found. |
985 | | /// Erase an element from the hash table. |
986 | | /// return 0 if element was not found |
987 | | size_type erase(const KeyT& key) noexcept |
988 | 0 | { |
989 | 0 | const auto key_hash = hash_key(key); |
990 | 0 | const auto sbucket = find_filled_bucket(key, key_hash); |
991 | 0 | if (sbucket == INACTIVE) |
992 | 0 | return 0; |
993 | | |
994 | 0 | const auto main_bucket = key_hash & _mask; |
995 | 0 | erase_slot(sbucket, (size_type)main_bucket); |
996 | 0 | return 1; |
997 | 0 | } |
998 | | |
999 | | //iterator erase(const_iterator begin_it, const_iterator end_it) |
1000 | | iterator erase(const const_iterator& cit) noexcept |
1001 | | { |
1002 | | const auto slot = (size_type)(cit.kv_ - _pairs); |
1003 | | size_type main_bucket; |
1004 | | const auto sbucket = find_slot_bucket(slot, main_bucket); //TODO |
1005 | | erase_slot(sbucket, main_bucket); |
1006 | | return {this, slot}; |
1007 | | } |
1008 | | |
1009 | | //only last >= first |
1010 | | iterator erase(const_iterator first, const_iterator last) noexcept |
1011 | | { |
1012 | | auto esize = long(last.kv_ - first.kv_); |
1013 | | auto tsize = long((_pairs + _num_filled) - last.kv_); //last to tail size |
1014 | | auto next = first; |
1015 | | while (tsize -- > 0) { |
1016 | | if (esize-- <= 0) |
1017 | | break; |
1018 | | next = ++erase(next); |
1019 | | } |
1020 | | |
1021 | | //fast erase from last |
1022 | | next = this->last(); |
1023 | | while (esize -- > 0) |
1024 | | next = --erase(next); |
1025 | | |
1026 | | return {this, size_type(next.kv_ - _pairs)}; |
1027 | | } |
1028 | | |
1029 | | template<typename Pred> |
1030 | | size_type erase_if(Pred pred) |
1031 | | { |
1032 | | auto old_size = size(); |
1033 | | for (auto it = begin(); it != end();) { |
1034 | | if (pred(*it)) |
1035 | | it = erase(it); |
1036 | | else |
1037 | | ++it; |
1038 | | } |
1039 | | return old_size - size(); |
1040 | | } |
1041 | | |
1042 | | static constexpr bool is_trivially_destructible() |
1043 | 0 | { |
1044 | 0 | #if __cplusplus >= 201402L || _MSC_VER > 1600 |
1045 | 0 | return (std::is_trivially_destructible<KeyT>::value && std::is_trivially_destructible<ValueT>::value); |
1046 | 0 | #else |
1047 | 0 | return (std::is_pod<KeyT>::value && std::is_pod<ValueT>::value); |
1048 | 0 | #endif |
1049 | 0 | } Unexecuted instantiation: emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::is_trivially_destructible() Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::is_trivially_destructible() |
1050 | | |
1051 | | static constexpr bool is_trivially_copyable() |
1052 | 0 | { |
1053 | 0 | #if __cplusplus >= 201103L || _MSC_VER > 1600 |
1054 | 0 | return (std::is_trivially_copyable<KeyT>::value && std::is_trivially_copyable<ValueT>::value); |
1055 | 0 | #else |
1056 | 0 | return (std::is_pod<KeyT>::value && std::is_pod<ValueT>::value); |
1057 | 0 | #endif |
1058 | 0 | } Unexecuted instantiation: emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::is_trivially_copyable() Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::is_trivially_copyable() |
1059 | | |
1060 | | void clearkv() |
1061 | 617 | { |
1062 | 617 | if (!is_trivially_destructible()) { |
1063 | 0 | while (_num_filled --) |
1064 | 0 | _pairs[_num_filled].~value_type(); |
1065 | 0 | } |
1066 | 617 | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::clearkv() Line | Count | Source | 1061 | 617 | { | 1062 | 617 | if (!is_trivially_destructible()) { | 1063 | 0 | while (_num_filled --) | 1064 | 0 | _pairs[_num_filled].~value_type(); | 1065 | 0 | } | 1066 | 617 | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::clearkv() |
1067 | | |
1068 | | /// Remove all elements, keeping full capacity. |
1069 | | void clear() noexcept |
1070 | | { |
1071 | | clearkv(); |
1072 | | |
1073 | | if (_num_filled > 0) |
1074 | | memset((char*)_index, (int)INACTIVE, sizeof(_index[0]) * _num_buckets); |
1075 | | |
1076 | | _last = _num_filled = 0; |
1077 | | _etail = INACTIVE; |
1078 | | |
1079 | | #if EMH_HIGH_LOAD |
1080 | | _ehead = 0; |
1081 | | #endif |
1082 | | } |
1083 | | |
1084 | | void shrink_to_fit(const float min_factor = EMH_DEFAULT_LOAD_FACTOR / 4) |
1085 | | { |
1086 | | if (load_factor() < min_factor && bucket_count() > 10) //safe guard |
1087 | | rehash(_num_filled + 1); |
1088 | | } |
1089 | | |
1090 | | #if EMH_HIGH_LOAD |
1091 | | #ifdef EMH_PREVET |
1092 | | #undef EMH_PREVET |
1093 | | #endif |
1094 | | #define EMH_PREVET(i, n) i[n].slot |
1095 | | void set_empty() |
1096 | | { |
1097 | | auto prev = 0; |
1098 | | for (int32_t bucket = 1; bucket < _num_buckets; ++bucket) { |
1099 | | if (EMH_EMPTY(bucket)) { |
1100 | | if (prev != 0) { |
1101 | | EMH_PREVET(_index, bucket) = prev; |
1102 | | _index[prev].next = -bucket; |
1103 | | } |
1104 | | else |
1105 | | _ehead = bucket; |
1106 | | prev = bucket; |
1107 | | } |
1108 | | } |
1109 | | |
1110 | | if (prev == 0) { _ehead = 0; return; } //no empty bucket |
1111 | | EMH_PREVET(_index, _ehead) = prev; |
1112 | | _index[prev].next = 0-_ehead; |
1113 | | _ehead = 0-_index[_ehead].next; |
1114 | | } |
1115 | | |
1116 | | void clear_empty() |
1117 | | { |
1118 | | auto prev = EMH_PREVET(_index, _ehead); |
1119 | | while (prev != _ehead) { |
1120 | | _index[prev].next = INACTIVE; |
1121 | | prev = EMH_PREVET(_index, prev); |
1122 | | } |
1123 | | _index[_ehead].next = INACTIVE; |
1124 | | _ehead = 0; |
1125 | | } |
1126 | | |
1127 | | //prev-ehead->next |
1128 | | size_type pop_empty(const size_type bucket) |
1129 | | { |
1130 | | const auto prev_bucket = EMH_PREVET(_index, bucket); |
1131 | | const int next_bucket = 0-_index[bucket].next; |
1132 | | |
1133 | | EMH_PREVET(_index, next_bucket) = prev_bucket; |
1134 | | _index[prev_bucket].next = -next_bucket; |
1135 | | |
1136 | | _ehead = next_bucket; |
1137 | | return bucket; |
1138 | | } |
1139 | | |
1140 | | //ehead->bucket->next |
1141 | | void push_empty(const int32_t bucket) |
1142 | | { |
1143 | | const int next_bucket = 0-_index[_ehead].next; |
1144 | | assert(next_bucket > 0); |
1145 | | |
1146 | | EMH_PREVET(_index, bucket) = _ehead; |
1147 | | _index[bucket].next = -next_bucket; |
1148 | | |
1149 | | EMH_PREVET(_index, next_bucket) = bucket; |
1150 | | _index[_ehead].next = -bucket; |
1151 | | // _ehead = bucket; |
1152 | | } |
1153 | | #endif |
1154 | | |
1155 | | /// Make room for this many elements |
1156 | | bool reserve(uint64_t num_elems, bool force) |
1157 | 347k | { |
1158 | 347k | (void)force; |
1159 | 347k | #if EMH_HIGH_LOAD == 0 |
1160 | 347k | const auto required_buckets = num_elems * _mlf >> 28; |
1161 | 347k | if (EMH_LIKELY(required_buckets < _num_buckets)) // && !force |
1162 | 345k | return false; |
1163 | | #else |
1164 | | const auto required_buckets = num_elems + num_elems * 1 / 9; |
1165 | | if (EMH_LIKELY(required_buckets < _mask)) |
1166 | | return false; |
1167 | | |
1168 | | else if (_num_buckets < 16 && _num_filled < _num_buckets) |
1169 | | return false; |
1170 | | |
1171 | | else if (_num_buckets > EMH_HIGH_LOAD) { |
1172 | | if (_ehead == 0) { |
1173 | | set_empty(); |
1174 | | return false; |
1175 | | } else if (/*_num_filled + 100 < _num_buckets && */_index[_ehead].next != 0-_ehead) { |
1176 | | return false; |
1177 | | } |
1178 | | } |
1179 | | #endif |
1180 | | #if EMH_STATIS |
1181 | | if (_num_filled > EMH_STATIS) dump_statics(); |
1182 | | #endif |
1183 | | |
1184 | | //assert(required_buckets < max_size()); |
1185 | 2.12k | rehash(required_buckets + 2); |
1186 | 2.12k | return true; |
1187 | 347k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::reserve(unsigned long, bool) Line | Count | Source | 1157 | 347k | { | 1158 | 347k | (void)force; | 1159 | 347k | #if EMH_HIGH_LOAD == 0 | 1160 | 347k | const auto required_buckets = num_elems * _mlf >> 28; | 1161 | 347k | if (EMH_LIKELY(required_buckets < _num_buckets)) // && !force | 1162 | 345k | return false; | 1163 | | #else | 1164 | | const auto required_buckets = num_elems + num_elems * 1 / 9; | 1165 | | if (EMH_LIKELY(required_buckets < _mask)) | 1166 | | return false; | 1167 | | | 1168 | | else if (_num_buckets < 16 && _num_filled < _num_buckets) | 1169 | | return false; | 1170 | | | 1171 | | else if (_num_buckets > EMH_HIGH_LOAD) { | 1172 | | if (_ehead == 0) { | 1173 | | set_empty(); | 1174 | | return false; | 1175 | | } else if (/*_num_filled + 100 < _num_buckets && */_index[_ehead].next != 0-_ehead) { | 1176 | | return false; | 1177 | | } | 1178 | | } | 1179 | | #endif | 1180 | | #if EMH_STATIS | 1181 | | if (_num_filled > EMH_STATIS) dump_statics(); | 1182 | | #endif | 1183 | | | 1184 | | //assert(required_buckets < max_size()); | 1185 | 2.12k | rehash(required_buckets + 2); | 1186 | 2.12k | return true; | 1187 | 347k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::reserve(unsigned long, bool) |
1188 | | |
1189 | | value_type* alloc_bucket(size_type num_buckets) |
1190 | 2.73k | { |
1191 | 2.73k | return PairAllocTraits::allocate(_pair_allocator, num_buckets); |
1192 | 2.73k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::alloc_bucket(unsigned int) Line | Count | Source | 1190 | 2.73k | { | 1191 | 2.73k | return PairAllocTraits::allocate(_pair_allocator, num_buckets); | 1192 | 2.73k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::alloc_bucket(unsigned int) |
1193 | | |
1194 | | void dealloc_bucket(value_type* ptr, size_type num_buckets) |
1195 | 3.35k | { |
1196 | 3.35k | if (ptr) |
1197 | 2.73k | PairAllocTraits::deallocate(_pair_allocator, ptr, num_buckets); |
1198 | 3.35k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::dealloc_bucket(std::__1::pair<StringPiece, Node*>*, unsigned int) Line | Count | Source | 1195 | 3.35k | { | 1196 | 3.35k | if (ptr) | 1197 | 2.73k | PairAllocTraits::deallocate(_pair_allocator, ptr, num_buckets); | 1198 | 3.35k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::dealloc_bucket(std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > >*, unsigned int) |
1199 | | |
1200 | | Index* alloc_index(size_type num_buckets) |
1201 | 2.73k | { |
1202 | 2.73k | return IndexAllocTraits::allocate(_index_allocator, num_buckets + EAD); |
1203 | 2.73k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::alloc_index(unsigned int) Line | Count | Source | 1201 | 2.73k | { | 1202 | 2.73k | return IndexAllocTraits::allocate(_index_allocator, num_buckets + EAD); | 1203 | 2.73k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::alloc_index(unsigned int) |
1204 | | |
1205 | | void dealloc_index(Index* ptr, size_type num_buckets) |
1206 | 3.35k | { |
1207 | 3.35k | if (ptr) |
1208 | 2.73k | IndexAllocTraits::deallocate(_index_allocator, ptr, num_buckets + EAD); |
1209 | 3.35k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::dealloc_index(emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::Index*, unsigned int) Line | Count | Source | 1206 | 3.35k | { | 1207 | 3.35k | if (ptr) | 1208 | 2.73k | IndexAllocTraits::deallocate(_index_allocator, ptr, num_buckets + EAD); | 1209 | 3.35k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::dealloc_index(emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::Index*, unsigned int) |
1210 | | |
1211 | | bool reserve(size_type required_buckets) noexcept |
1212 | | { |
1213 | | if (_num_filled != required_buckets) |
1214 | | return reserve(required_buckets, true); |
1215 | | |
1216 | | _last = 0; |
1217 | | #if EMH_HIGH_LOAD |
1218 | | _ehead = 0; |
1219 | | #endif |
1220 | | |
1221 | | memset((char*)_index, (int)INACTIVE, sizeof(_index[0]) * _num_buckets); |
1222 | | for (size_type slot = 0; slot < _num_filled; ++slot) { |
1223 | | const auto& key = _pairs[slot].first; |
1224 | | const auto key_hash = hash_key(key); |
1225 | | const auto bucket = find_unique_bucket(key_hash); |
1226 | | _index[bucket] = { bucket, slot | ((size_type)(key_hash) & ~_mask) }; |
1227 | | } |
1228 | | return true; |
1229 | | } |
1230 | | |
1231 | | void rebuild(size_type num_buckets, size_type required_buckets, size_type old_num_buckets) noexcept |
1232 | 2.73k | { |
1233 | 2.73k | dealloc_index(_index, old_num_buckets); |
1234 | 2.73k | const auto need_size = std::max((size_type)((double)num_buckets * max_load_factor()) + 4, required_buckets + 2); |
1235 | 2.73k | auto new_pairs = alloc_bucket(need_size); |
1236 | 2.73k | if (is_trivially_copyable()) { |
1237 | 2.73k | if (_pairs) |
1238 | 2.12k | memcpy((char*)new_pairs, (char*)_pairs, _num_filled * sizeof(value_type)); |
1239 | 2.73k | } else { |
1240 | 0 | for (size_type slot = 0; slot < _num_filled; slot++) { |
1241 | 0 | new(new_pairs + slot) value_type(std::move(_pairs[slot])); |
1242 | 0 | if (!is_trivially_destructible()) |
1243 | 0 | _pairs[slot].~value_type(); |
1244 | 0 | } |
1245 | 0 | } |
1246 | 2.73k | dealloc_bucket(_pairs, _pairs_capacity); |
1247 | 2.73k | _pairs = new_pairs; |
1248 | 2.73k | _pairs_capacity = need_size; |
1249 | 2.73k | _index = alloc_index(num_buckets); |
1250 | | |
1251 | 2.73k | memset((char*)_index, (int)INACTIVE, sizeof(_index[0]) * num_buckets); |
1252 | 2.73k | memset((char*)(_index + num_buckets), 0, sizeof(_index[0]) * EAD); |
1253 | 2.73k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::rebuild(unsigned int, unsigned int, unsigned int) Line | Count | Source | 1232 | 2.73k | { | 1233 | 2.73k | dealloc_index(_index, old_num_buckets); | 1234 | 2.73k | const auto need_size = std::max((size_type)((double)num_buckets * max_load_factor()) + 4, required_buckets + 2); | 1235 | 2.73k | auto new_pairs = alloc_bucket(need_size); | 1236 | 2.73k | if (is_trivially_copyable()) { | 1237 | 2.73k | if (_pairs) | 1238 | 2.12k | memcpy((char*)new_pairs, (char*)_pairs, _num_filled * sizeof(value_type)); | 1239 | 2.73k | } else { | 1240 | 0 | for (size_type slot = 0; slot < _num_filled; slot++) { | 1241 | 0 | new(new_pairs + slot) value_type(std::move(_pairs[slot])); | 1242 | 0 | if (!is_trivially_destructible()) | 1243 | 0 | _pairs[slot].~value_type(); | 1244 | 0 | } | 1245 | 0 | } | 1246 | 2.73k | dealloc_bucket(_pairs, _pairs_capacity); | 1247 | 2.73k | _pairs = new_pairs; | 1248 | 2.73k | _pairs_capacity = need_size; | 1249 | 2.73k | _index = alloc_index(num_buckets); | 1250 | | | 1251 | 2.73k | memset((char*)_index, (int)INACTIVE, sizeof(_index[0]) * num_buckets); | 1252 | 2.73k | memset((char*)(_index + num_buckets), 0, sizeof(_index[0]) * EAD); | 1253 | 2.73k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::rebuild(unsigned int, unsigned int, unsigned int) |
1254 | | |
1255 | | void rehash(uint64_t required_buckets) |
1256 | 2.73k | { |
1257 | 2.73k | if (required_buckets < _num_filled) |
1258 | 0 | return; |
1259 | | |
1260 | 2.73k | uint64_t buckets = _num_filled > (1u << 16) ? (1u << 16) : 4u; |
1261 | 12.2k | while (buckets < required_buckets) { buckets *= 2; } |
1262 | 2.73k | if (buckets > (uint64_t)max_size() || buckets < _num_filled) |
1263 | 0 | std::abort(); //throw std::length_error("too large size"); |
1264 | | |
1265 | | #if EMH_SAVE_MEM |
1266 | | if (sizeof(KeyT) < sizeof(size_type) && buckets >= (1ul << (2 * 8))) |
1267 | | buckets = 2ul << (sizeof(KeyT) * 8); |
1268 | | #endif |
1269 | | |
1270 | 2.73k | auto num_buckets = (size_type)buckets; |
1271 | | |
1272 | | #if EMH_REHASH_LOG |
1273 | | auto last = _last; |
1274 | | size_type collision = 0; |
1275 | | #endif |
1276 | | |
1277 | | #if EMH_HIGH_LOAD |
1278 | | _ehead = 0; |
1279 | | #endif |
1280 | 2.73k | _last = 0; |
1281 | | |
1282 | 2.73k | _mask = num_buckets - 1; |
1283 | | #if EMH_PACK_TAIL > 1 |
1284 | | _last = _mask; |
1285 | | num_buckets += num_buckets * EMH_PACK_TAIL / 100; //add more 5-10% |
1286 | | #endif |
1287 | 2.73k | auto old_num_buckets = _num_buckets; |
1288 | 2.73k | _num_buckets = num_buckets; |
1289 | | |
1290 | 2.73k | rebuild(num_buckets, (size_type)required_buckets, old_num_buckets); |
1291 | | |
1292 | | #ifdef EMH_SORT |
1293 | | std::sort(_pairs, _pairs + _num_filled, [this](const value_type & l, const value_type & r) { |
1294 | | const auto hashl = hash_key(l.first), hashr = hash_key(r.first); |
1295 | | auto diff = int64_t((hashl & _mask) - (hashr & _mask)); |
1296 | | if (diff != 0) |
1297 | | return diff < 0; |
1298 | | return hashl < hashr; |
1299 | | }); |
1300 | | #endif |
1301 | | |
1302 | 2.73k | _etail = INACTIVE; |
1303 | 526k | for (size_type slot = 0; slot < _num_filled; ++slot) { |
1304 | 523k | const auto& key = _pairs[slot].first; |
1305 | 523k | const auto key_hash = hash_key(key); |
1306 | 523k | const auto bucket = find_unique_bucket(key_hash); |
1307 | 523k | _index[bucket] = { bucket, slot | ((size_type)(key_hash) & ~_mask) }; |
1308 | | |
1309 | | #if EMH_REHASH_LOG |
1310 | | if (bucket != hash_main(bucket)) |
1311 | | collision ++; |
1312 | | #endif |
1313 | 523k | } |
1314 | | |
1315 | | #if EMH_REHASH_LOG |
1316 | | if (_num_filled > EMH_REHASH_LOG) { |
1317 | | auto mbucket = _num_filled - collision; |
1318 | | char buff[255] = {0}; |
1319 | | sprintf(buff, " _num_filled/aver_size/K.V/pack/collision|last = %u/%.2lf/%s.%s/%zd|%.2lf%%,%.2lf%%", |
1320 | | _num_filled, double (_num_filled) / mbucket, typeid(KeyT).name(), typeid(ValueT).name(), sizeof(_pairs[0]), collision * 100.0 / _num_filled, last * 100.0 / _num_buckets); |
1321 | | #ifdef EMH_LOG |
1322 | | static uint32_t ihashs = 0; EMH_LOG() << "hash_nums = " << ihashs ++ << "|" <<__FUNCTION__ << "|" << buff << endl; |
1323 | | #else |
1324 | | puts(buff); |
1325 | | #endif |
1326 | | } |
1327 | | #endif |
1328 | 2.73k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::rehash(unsigned long) Line | Count | Source | 1256 | 2.73k | { | 1257 | 2.73k | if (required_buckets < _num_filled) | 1258 | 0 | return; | 1259 | | | 1260 | 2.73k | uint64_t buckets = _num_filled > (1u << 16) ? (1u << 16) : 4u; | 1261 | 12.2k | while (buckets < required_buckets) { buckets *= 2; } | 1262 | 2.73k | if (buckets > (uint64_t)max_size() || buckets < _num_filled) | 1263 | 0 | std::abort(); //throw std::length_error("too large size"); | 1264 | | | 1265 | | #if EMH_SAVE_MEM | 1266 | | if (sizeof(KeyT) < sizeof(size_type) && buckets >= (1ul << (2 * 8))) | 1267 | | buckets = 2ul << (sizeof(KeyT) * 8); | 1268 | | #endif | 1269 | | | 1270 | 2.73k | auto num_buckets = (size_type)buckets; | 1271 | | | 1272 | | #if EMH_REHASH_LOG | 1273 | | auto last = _last; | 1274 | | size_type collision = 0; | 1275 | | #endif | 1276 | | | 1277 | | #if EMH_HIGH_LOAD | 1278 | | _ehead = 0; | 1279 | | #endif | 1280 | 2.73k | _last = 0; | 1281 | | | 1282 | 2.73k | _mask = num_buckets - 1; | 1283 | | #if EMH_PACK_TAIL > 1 | 1284 | | _last = _mask; | 1285 | | num_buckets += num_buckets * EMH_PACK_TAIL / 100; //add more 5-10% | 1286 | | #endif | 1287 | 2.73k | auto old_num_buckets = _num_buckets; | 1288 | 2.73k | _num_buckets = num_buckets; | 1289 | | | 1290 | 2.73k | rebuild(num_buckets, (size_type)required_buckets, old_num_buckets); | 1291 | | | 1292 | | #ifdef EMH_SORT | 1293 | | std::sort(_pairs, _pairs + _num_filled, [this](const value_type & l, const value_type & r) { | 1294 | | const auto hashl = hash_key(l.first), hashr = hash_key(r.first); | 1295 | | auto diff = int64_t((hashl & _mask) - (hashr & _mask)); | 1296 | | if (diff != 0) | 1297 | | return diff < 0; | 1298 | | return hashl < hashr; | 1299 | | }); | 1300 | | #endif | 1301 | | | 1302 | 2.73k | _etail = INACTIVE; | 1303 | 526k | for (size_type slot = 0; slot < _num_filled; ++slot) { | 1304 | 523k | const auto& key = _pairs[slot].first; | 1305 | 523k | const auto key_hash = hash_key(key); | 1306 | 523k | const auto bucket = find_unique_bucket(key_hash); | 1307 | 523k | _index[bucket] = { bucket, slot | ((size_type)(key_hash) & ~_mask) }; | 1308 | | | 1309 | | #if EMH_REHASH_LOG | 1310 | | if (bucket != hash_main(bucket)) | 1311 | | collision ++; | 1312 | | #endif | 1313 | 523k | } | 1314 | | | 1315 | | #if EMH_REHASH_LOG | 1316 | | if (_num_filled > EMH_REHASH_LOG) { | 1317 | | auto mbucket = _num_filled - collision; | 1318 | | char buff[255] = {0}; | 1319 | | sprintf(buff, " _num_filled/aver_size/K.V/pack/collision|last = %u/%.2lf/%s.%s/%zd|%.2lf%%,%.2lf%%", | 1320 | | _num_filled, double (_num_filled) / mbucket, typeid(KeyT).name(), typeid(ValueT).name(), sizeof(_pairs[0]), collision * 100.0 / _num_filled, last * 100.0 / _num_buckets); | 1321 | | #ifdef EMH_LOG | 1322 | | static uint32_t ihashs = 0; EMH_LOG() << "hash_nums = " << ihashs ++ << "|" <<__FUNCTION__ << "|" << buff << endl; | 1323 | | #else | 1324 | | puts(buff); | 1325 | | #endif | 1326 | | } | 1327 | | #endif | 1328 | 2.73k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::rehash(unsigned long) |
1329 | | |
1330 | | private: |
1331 | | // Can we fit another element? |
1332 | | bool check_expand_need() |
1333 | 347k | { |
1334 | 347k | return reserve(_num_filled, false); |
1335 | 347k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::check_expand_need() Line | Count | Source | 1333 | 347k | { | 1334 | 347k | return reserve(_num_filled, false); | 1335 | 347k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::check_expand_need() |
1336 | | |
1337 | | static void prefetch_heap_block(char* ctrl) |
1338 | 1.32M | { |
1339 | | // Prefetch the heap-allocated memory region to resolve potential TLB |
1340 | | // misses. This is intended to overlap with execution of calculating the hash for a key. |
1341 | 1.32M | #if defined(__GNUC__) || defined(__clang__) |
1342 | 1.32M | __builtin_prefetch(static_cast<const void*>(ctrl), 0, 1); |
1343 | | #elif _WIN32 && defined(_M_ARM64) |
1344 | | __prefetch((const char*)ctrl); |
1345 | | #elif _WIN32 |
1346 | | _mm_prefetch((const char*)ctrl, _MM_HINT_T0); |
1347 | | #endif |
1348 | 1.32M | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::prefetch_heap_block(char*) Line | Count | Source | 1338 | 1.32M | { | 1339 | | // Prefetch the heap-allocated memory region to resolve potential TLB | 1340 | | // misses. This is intended to overlap with execution of calculating the hash for a key. | 1341 | 1.32M | #if defined(__GNUC__) || defined(__clang__) | 1342 | 1.32M | __builtin_prefetch(static_cast<const void*>(ctrl), 0, 1); | 1343 | | #elif _WIN32 && defined(_M_ARM64) | 1344 | | __prefetch((const char*)ctrl); | 1345 | | #elif _WIN32 | 1346 | | _mm_prefetch((const char*)ctrl, _MM_HINT_T0); | 1347 | | #endif | 1348 | 1.32M | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::prefetch_heap_block(char*) |
1349 | | |
1350 | | size_type slot_to_bucket(const size_type slot) const noexcept |
1351 | 0 | { |
1352 | 0 | size_type main_bucket; |
1353 | 0 | return find_slot_bucket(slot, main_bucket); //TODO |
1354 | 0 | } |
1355 | | |
1356 | | //very slow |
1357 | | void erase_slot(const size_type sbucket, const size_type main_bucket) noexcept |
1358 | 0 | { |
1359 | 0 | const auto slot = _index[sbucket].slot & _mask; |
1360 | 0 | const auto last_slot = _num_filled - 1; |
1361 | | // Find last_slot's bucket BEFORE erase_bucket modifies the chain |
1362 | 0 | const auto last_bucket = (slot == last_slot) ? sbucket : slot_to_bucket(last_slot); |
1363 | |
|
1364 | 0 | const auto ebucket = erase_bucket(sbucket, main_bucket); |
1365 | 0 | --_num_filled; |
1366 | 0 | if (EMH_LIKELY(slot != last_slot)) { |
1367 | | // When sbucket == main_bucket, erase_bucket promotes next_bucket to |
1368 | | // main_bucket. If last_bucket was next_bucket (== ebucket), the data |
1369 | | // is now at main_bucket, so update main_bucket instead. |
1370 | 0 | const auto update_bucket = (last_bucket == ebucket && sbucket == main_bucket) |
1371 | 0 | ? main_bucket : last_bucket; |
1372 | |
|
1373 | 0 | _pairs[slot] = std::move(_pairs[last_slot]); |
1374 | 0 | _index[update_bucket].slot = slot | (_index[update_bucket].slot & ~_mask); |
1375 | 0 | } |
1376 | |
|
1377 | 0 | if (!is_trivially_destructible()) |
1378 | 0 | _pairs[last_slot].~value_type(); |
1379 | |
|
1380 | 0 | _etail = INACTIVE; |
1381 | 0 | _index[ebucket] = {INACTIVE, 0}; |
1382 | | #if EMH_HIGH_LOAD |
1383 | | if (_ehead) { |
1384 | | if (10 * _num_filled < 8 * _num_buckets) |
1385 | | clear_empty(); |
1386 | | else if (ebucket) |
1387 | | push_empty(ebucket); |
1388 | | } |
1389 | | #endif |
1390 | 0 | } |
1391 | | |
1392 | | size_type erase_bucket(const size_type bucket, const size_type main_bucket) noexcept |
1393 | 0 | { |
1394 | 0 | const auto next_bucket = _index[bucket].next; |
1395 | 0 | if (bucket == main_bucket) { |
1396 | 0 | if (main_bucket != next_bucket) { |
1397 | 0 | const auto nbucket = _index[next_bucket].next; |
1398 | 0 | _index[main_bucket] = { |
1399 | 0 | (nbucket == next_bucket) ? main_bucket : nbucket, |
1400 | 0 | _index[next_bucket].slot |
1401 | 0 | }; |
1402 | 0 | } |
1403 | 0 | return next_bucket; |
1404 | 0 | } |
1405 | | |
1406 | 0 | const auto prev_bucket = find_prev_bucket(main_bucket, bucket); |
1407 | 0 | _index[prev_bucket].next = (bucket == next_bucket) ? prev_bucket : next_bucket; |
1408 | 0 | return bucket; |
1409 | 0 | } |
1410 | | |
1411 | | // Find the slot with this key, or return bucket size |
1412 | | size_type find_slot_bucket(const size_type slot, size_type& main_bucket) const |
1413 | 0 | { |
1414 | 0 | const auto key_hash = hash_key(_pairs[slot].first); |
1415 | 0 | const auto bucket = main_bucket = size_type(key_hash & _mask); |
1416 | 0 | if (slot == (_index[bucket].slot & _mask)) |
1417 | 0 | return bucket; |
1418 | | |
1419 | 0 | auto next_bucket = _index[bucket].next; |
1420 | 0 | while (true) { |
1421 | 0 | if (EMH_LIKELY(slot == (_index[next_bucket].slot & _mask))) |
1422 | 0 | return next_bucket; |
1423 | 0 | next_bucket = _index[next_bucket].next; |
1424 | 0 | } |
1425 | | |
1426 | 0 | return INACTIVE; |
1427 | 0 | } |
1428 | | |
1429 | | // Find the slot with this key, or return bucket size |
1430 | | size_type find_filled_bucket(const KeyT& key, uint64_t key_hash) const noexcept |
1431 | 0 | { |
1432 | 0 | const auto bucket = size_type(key_hash & _mask); |
1433 | 0 | const auto& idx = _index[bucket]; |
1434 | 0 | auto next_bucket = idx.next; |
1435 | 0 | if (EMH_UNLIKELY((int)next_bucket < 0)) |
1436 | 0 | return INACTIVE; |
1437 | | |
1438 | 0 | const auto slot = idx.slot & _mask; |
1439 | 0 | prefetch_heap_block((char*)&_pairs[slot]); |
1440 | 0 | if (EMH_EQHASH(bucket, key_hash)) { |
1441 | 0 | if (EMH_LIKELY(_eq(key, _pairs[slot].first))) |
1442 | 0 | return bucket; |
1443 | 0 | } |
1444 | 0 | if (next_bucket == bucket) |
1445 | 0 | return INACTIVE; |
1446 | | |
1447 | 0 | while (true) { |
1448 | 0 | if (EMH_EQHASH(next_bucket, key_hash)) { |
1449 | 0 | const auto eslot = _index[next_bucket].slot & _mask; |
1450 | 0 | if (EMH_LIKELY(_eq(key, _pairs[eslot].first))) |
1451 | 0 | return next_bucket; |
1452 | 0 | } |
1453 | | |
1454 | 0 | const auto nbucket = _index[next_bucket].next; |
1455 | 0 | if (nbucket == next_bucket) |
1456 | 0 | return INACTIVE; |
1457 | 0 | next_bucket = nbucket; |
1458 | 0 | } |
1459 | | |
1460 | 0 | return INACTIVE; |
1461 | 0 | } |
1462 | | |
1463 | | // Find the slot with this key, or return bucket size |
1464 | | template<typename K=KeyT> |
1465 | | size_type find_filled_slot(const K& key) const noexcept |
1466 | 1.05M | { |
1467 | 1.05M | const auto key_hash = hash_key(key); |
1468 | 1.05M | const auto bucket = size_type(key_hash & _mask); |
1469 | 1.05M | const auto& idx = _index[bucket]; |
1470 | 1.05M | auto next_bucket = idx.next; |
1471 | 1.05M | if ((int)next_bucket < 0) |
1472 | 142k | return _num_filled; |
1473 | | |
1474 | 913k | const auto slot = idx.slot & _mask; |
1475 | 913k | prefetch_heap_block((char*)&_pairs[slot]); |
1476 | 913k | if (EMH_EQHASH(bucket, key_hash)) { |
1477 | 573k | if (EMH_LIKELY(_eq(key, _pairs[slot].first))) |
1478 | 573k | return slot; |
1479 | 573k | } |
1480 | 340k | if (next_bucket == bucket) |
1481 | 151k | return _num_filled; |
1482 | | |
1483 | 242k | while (true) { |
1484 | 242k | if (EMH_EQHASH(next_bucket, key_hash)) { |
1485 | 136k | const auto eslot = _index[next_bucket].slot & _mask; |
1486 | 136k | if (EMH_LIKELY(_eq(key, _pairs[eslot].first))) |
1487 | 135k | return eslot; |
1488 | 136k | } |
1489 | | |
1490 | 106k | const auto nbucket = _index[next_bucket].next; |
1491 | 106k | if (nbucket == next_bucket) |
1492 | 52.8k | return _num_filled; |
1493 | 53.8k | next_bucket = nbucket; |
1494 | 53.8k | } |
1495 | | |
1496 | 0 | return _num_filled; |
1497 | 188k | } unsigned int emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::find_filled_slot<StringPiece>(StringPiece const&) const Line | Count | Source | 1466 | 1.05M | { | 1467 | 1.05M | const auto key_hash = hash_key(key); | 1468 | 1.05M | const auto bucket = size_type(key_hash & _mask); | 1469 | 1.05M | const auto& idx = _index[bucket]; | 1470 | 1.05M | auto next_bucket = idx.next; | 1471 | 1.05M | if ((int)next_bucket < 0) | 1472 | 142k | return _num_filled; | 1473 | | | 1474 | 913k | const auto slot = idx.slot & _mask; | 1475 | 913k | prefetch_heap_block((char*)&_pairs[slot]); | 1476 | 913k | if (EMH_EQHASH(bucket, key_hash)) { | 1477 | 573k | if (EMH_LIKELY(_eq(key, _pairs[slot].first))) | 1478 | 573k | return slot; | 1479 | 573k | } | 1480 | 340k | if (next_bucket == bucket) | 1481 | 151k | return _num_filled; | 1482 | | | 1483 | 242k | while (true) { | 1484 | 242k | if (EMH_EQHASH(next_bucket, key_hash)) { | 1485 | 136k | const auto eslot = _index[next_bucket].slot & _mask; | 1486 | 136k | if (EMH_LIKELY(_eq(key, _pairs[eslot].first))) | 1487 | 135k | return eslot; | 1488 | 136k | } | 1489 | | | 1490 | 106k | const auto nbucket = _index[next_bucket].next; | 1491 | 106k | if (nbucket == next_bucket) | 1492 | 52.8k | return _num_filled; | 1493 | 53.8k | next_bucket = nbucket; | 1494 | 53.8k | } | 1495 | | | 1496 | 0 | return _num_filled; | 1497 | 188k | } |
Unexecuted instantiation: unsigned int emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::find_filled_slot<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const |
1498 | | |
1499 | | #if EMH_SORT |
1500 | | size_type find_hash_bucket(const KeyT& key) const noexcept |
1501 | | { |
1502 | | const auto key_hash = hash_key(key); |
1503 | | const auto bucket = size_type(key_hash & _mask); |
1504 | | const auto next_bucket = _index[bucket].next; |
1505 | | if ((int)next_bucket < 0) |
1506 | | return END; |
1507 | | |
1508 | | auto slot = _index[bucket].slot & _mask; |
1509 | | if (_eq(key, _pairs[slot++].first)) |
1510 | | return slot; |
1511 | | else if (next_bucket == bucket) |
1512 | | return END; |
1513 | | |
1514 | | while (true) { |
1515 | | const auto& okey = _pairs[slot++].first; |
1516 | | if (_eq(key, okey)) |
1517 | | return slot; |
1518 | | |
1519 | | const auto hasho = hash_key(okey); |
1520 | | if ((hasho & _mask) != bucket) |
1521 | | break; |
1522 | | else if (hasho > key_hash) |
1523 | | break; |
1524 | | else if (EMH_UNLIKELY(slot >= _num_filled)) |
1525 | | break; |
1526 | | } |
1527 | | |
1528 | | return END; |
1529 | | } |
1530 | | |
1531 | | //only for find/can not insert |
1532 | | size_type find_sorted_bucket(const KeyT& key) const noexcept |
1533 | | { |
1534 | | const auto key_hash = hash_key(key); |
1535 | | const auto bucket = size_type(key_hash & _mask); |
1536 | | const auto slots = (int)(_index[bucket].next); //TODO |
1537 | | if (slots < 0 /**|| key < _pairs[slot].first*/) |
1538 | | return END; |
1539 | | |
1540 | | const auto slot = _index[bucket].slot & _mask; |
1541 | | auto ormask = _index[bucket].slot & ~_mask; |
1542 | | auto hmask = (size_type)(key_hash) & ~_mask; |
1543 | | if ((hmask | ormask) != ormask) |
1544 | | return END; |
1545 | | |
1546 | | if (_eq(key, _pairs[slot].first)) |
1547 | | return slot; |
1548 | | else if (slots == 1 || key < _pairs[slot].first) |
1549 | | return END; |
1550 | | |
1551 | | #if EMH_SORT |
1552 | | if (key < _pairs[slot].first || key > _pairs[slots + slot - 1].first) |
1553 | | return END; |
1554 | | #endif |
1555 | | |
1556 | | for (size_type i = 1; i < slots; ++i) { |
1557 | | const auto& okey = _pairs[slot + i].first; |
1558 | | if (_eq(key, okey)) |
1559 | | return slot + i; |
1560 | | // else if (okey > key) |
1561 | | // return END; |
1562 | | } |
1563 | | |
1564 | | return END; |
1565 | | } |
1566 | | #endif |
1567 | | |
1568 | | //kick out bucket and find empty to occupy |
1569 | | //it will break the original link and relink again. |
1570 | | //before: main_bucket --> prev_bucket --> bucket --> next_bucket(maybe none exist) |
1571 | | //after : main_bucket --> prev_bucket (kickout) next_bucket <-- new_bucket(bucket) |
1572 | | // \|/ ^ |
1573 | | // -|------------------------------------------| |
1574 | | size_type kickout_bucket(const size_type kmain, const size_type bucket) noexcept |
1575 | 65.8k | { |
1576 | 65.8k | const auto next_bucket = _index[bucket].next; |
1577 | 65.8k | const auto new_bucket = find_empty_bucket(next_bucket, 2); |
1578 | 65.8k | const auto prev_bucket = find_prev_bucket(kmain, bucket); |
1579 | | |
1580 | 65.8k | const auto last = next_bucket == bucket ? new_bucket : next_bucket; |
1581 | 65.8k | _index[new_bucket] = {last, _index[bucket].slot}; |
1582 | | |
1583 | 65.8k | _index[prev_bucket].next = new_bucket; |
1584 | 65.8k | _index[bucket].next = INACTIVE; |
1585 | | |
1586 | 65.8k | return bucket; |
1587 | 65.8k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::kickout_bucket(unsigned int, unsigned int) Line | Count | Source | 1575 | 65.8k | { | 1576 | 65.8k | const auto next_bucket = _index[bucket].next; | 1577 | 65.8k | const auto new_bucket = find_empty_bucket(next_bucket, 2); | 1578 | 65.8k | const auto prev_bucket = find_prev_bucket(kmain, bucket); | 1579 | | | 1580 | 65.8k | const auto last = next_bucket == bucket ? new_bucket : next_bucket; | 1581 | 65.8k | _index[new_bucket] = {last, _index[bucket].slot}; | 1582 | | | 1583 | 65.8k | _index[prev_bucket].next = new_bucket; | 1584 | 65.8k | _index[bucket].next = INACTIVE; | 1585 | | | 1586 | 65.8k | return bucket; | 1587 | 65.8k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::kickout_bucket(unsigned int, unsigned int) |
1588 | | |
1589 | | /* |
1590 | | ** inserts a new key into a hash table; first, check whether key's main |
1591 | | ** bucket/position is free. If not, check whether colliding node/bucket is in its main |
1592 | | ** position or not: if it is not, move colliding bucket to an empty place and |
1593 | | ** put new key in its main position; otherwise (colliding bucket is in its main |
1594 | | ** position), new key goes to an empty position. |
1595 | | */ |
1596 | | template<typename K=KeyT> |
1597 | | size_type find_or_allocate(const K& key, uint64_t key_hash) noexcept |
1598 | 347k | { |
1599 | 347k | const auto bucket = size_type(key_hash & _mask); |
1600 | 347k | const auto& idx = _index[bucket]; |
1601 | 347k | auto next_bucket = idx.next; |
1602 | 347k | prefetch_heap_block((char*)&_pairs[bucket]); |
1603 | 347k | if ((int)next_bucket < 0) { |
1604 | | #if EMH_HIGH_LOAD |
1605 | | if (next_bucket != INACTIVE) |
1606 | | pop_empty(bucket); |
1607 | | #endif |
1608 | 143k | return bucket; |
1609 | 143k | } |
1610 | | |
1611 | 203k | const auto slot = idx.slot & _mask; |
1612 | 203k | if (EMH_EQHASH(bucket, key_hash)) |
1613 | 12 | if (EMH_LIKELY(_eq(key, _pairs[slot].first))) |
1614 | 0 | return bucket; |
1615 | | |
1616 | | //check current bucket_key is in main bucket or not |
1617 | 203k | const auto kmain = hash_bucket(_pairs[slot].first); |
1618 | 203k | if (kmain != bucket) |
1619 | 51.2k | return kickout_bucket(kmain, bucket); |
1620 | 152k | else if (next_bucket == bucket) |
1621 | 110k | return _index[next_bucket].next = find_empty_bucket(next_bucket, 1); |
1622 | | |
1623 | 42.1k | uint32_t csize = 1; |
1624 | | //find next linked bucket and check key |
1625 | 53.3k | while (true) { |
1626 | 53.3k | const auto eslot = _index[next_bucket].slot & _mask; |
1627 | 53.3k | if (EMH_EQHASH(next_bucket, key_hash)) { |
1628 | 12 | if (EMH_LIKELY(_eq(key, _pairs[eslot].first))) |
1629 | 0 | return next_bucket; |
1630 | 12 | } |
1631 | | |
1632 | 53.3k | csize += 1; |
1633 | 53.3k | const auto nbucket = _index[next_bucket].next; |
1634 | 53.3k | if (nbucket == next_bucket) |
1635 | 42.1k | break; |
1636 | 11.2k | next_bucket = nbucket; |
1637 | 11.2k | } |
1638 | | |
1639 | | //find a empty and link it to tail |
1640 | 42.1k | const auto new_bucket = find_empty_bucket(next_bucket, csize); |
1641 | 42.1k | prefetch_heap_block((char*)&_pairs[new_bucket]); |
1642 | 42.1k | return _index[next_bucket].next = new_bucket; |
1643 | 42.1k | } unsigned int emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::find_or_allocate<StringPiece>(StringPiece const&, unsigned long) Line | Count | Source | 1598 | 347k | { | 1599 | 347k | const auto bucket = size_type(key_hash & _mask); | 1600 | 347k | const auto& idx = _index[bucket]; | 1601 | 347k | auto next_bucket = idx.next; | 1602 | 347k | prefetch_heap_block((char*)&_pairs[bucket]); | 1603 | 347k | if ((int)next_bucket < 0) { | 1604 | | #if EMH_HIGH_LOAD | 1605 | | if (next_bucket != INACTIVE) | 1606 | | pop_empty(bucket); | 1607 | | #endif | 1608 | 143k | return bucket; | 1609 | 143k | } | 1610 | | | 1611 | 203k | const auto slot = idx.slot & _mask; | 1612 | 203k | if (EMH_EQHASH(bucket, key_hash)) | 1613 | 12 | if (EMH_LIKELY(_eq(key, _pairs[slot].first))) | 1614 | 0 | return bucket; | 1615 | | | 1616 | | //check current bucket_key is in main bucket or not | 1617 | 203k | const auto kmain = hash_bucket(_pairs[slot].first); | 1618 | 203k | if (kmain != bucket) | 1619 | 51.2k | return kickout_bucket(kmain, bucket); | 1620 | 152k | else if (next_bucket == bucket) | 1621 | 110k | return _index[next_bucket].next = find_empty_bucket(next_bucket, 1); | 1622 | | | 1623 | 42.1k | uint32_t csize = 1; | 1624 | | //find next linked bucket and check key | 1625 | 53.3k | while (true) { | 1626 | 53.3k | const auto eslot = _index[next_bucket].slot & _mask; | 1627 | 53.3k | if (EMH_EQHASH(next_bucket, key_hash)) { | 1628 | 12 | if (EMH_LIKELY(_eq(key, _pairs[eslot].first))) | 1629 | 0 | return next_bucket; | 1630 | 12 | } | 1631 | | | 1632 | 53.3k | csize += 1; | 1633 | 53.3k | const auto nbucket = _index[next_bucket].next; | 1634 | 53.3k | if (nbucket == next_bucket) | 1635 | 42.1k | break; | 1636 | 11.2k | next_bucket = nbucket; | 1637 | 11.2k | } | 1638 | | | 1639 | | //find a empty and link it to tail | 1640 | 42.1k | const auto new_bucket = find_empty_bucket(next_bucket, csize); | 1641 | 42.1k | prefetch_heap_block((char*)&_pairs[new_bucket]); | 1642 | 42.1k | return _index[next_bucket].next = new_bucket; | 1643 | 42.1k | } |
Unexecuted instantiation: unsigned int emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::find_or_allocate<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long) |
1644 | | |
1645 | | size_type find_unique_bucket(uint64_t key_hash) noexcept |
1646 | 523k | { |
1647 | 523k | const auto bucket = size_type(key_hash & _mask); |
1648 | 523k | auto next_bucket = _index[bucket].next; |
1649 | 523k | if ((int)next_bucket < 0) { |
1650 | | #if EMH_HIGH_LOAD |
1651 | | if (next_bucket != INACTIVE) |
1652 | | pop_empty(bucket); |
1653 | | #endif |
1654 | 414k | return bucket; |
1655 | 414k | } |
1656 | | |
1657 | | //check current bucket_key is in main bucket or not |
1658 | 109k | const auto kmain = hash_main(bucket); |
1659 | 109k | if (EMH_UNLIKELY(kmain != bucket)) |
1660 | 14.6k | return kickout_bucket(kmain, bucket); |
1661 | 95.1k | else if (EMH_UNLIKELY(next_bucket != bucket)) |
1662 | 13.0k | next_bucket = find_last_bucket(next_bucket); |
1663 | | |
1664 | 95.1k | return _index[next_bucket].next = find_empty_bucket(next_bucket, 2); |
1665 | 109k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::find_unique_bucket(unsigned long) Line | Count | Source | 1646 | 523k | { | 1647 | 523k | const auto bucket = size_type(key_hash & _mask); | 1648 | 523k | auto next_bucket = _index[bucket].next; | 1649 | 523k | if ((int)next_bucket < 0) { | 1650 | | #if EMH_HIGH_LOAD | 1651 | | if (next_bucket != INACTIVE) | 1652 | | pop_empty(bucket); | 1653 | | #endif | 1654 | 414k | return bucket; | 1655 | 414k | } | 1656 | | | 1657 | | //check current bucket_key is in main bucket or not | 1658 | 109k | const auto kmain = hash_main(bucket); | 1659 | 109k | if (EMH_UNLIKELY(kmain != bucket)) | 1660 | 14.6k | return kickout_bucket(kmain, bucket); | 1661 | 95.1k | else if (EMH_UNLIKELY(next_bucket != bucket)) | 1662 | 13.0k | next_bucket = find_last_bucket(next_bucket); | 1663 | | | 1664 | 95.1k | return _index[next_bucket].next = find_empty_bucket(next_bucket, 2); | 1665 | 109k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::find_unique_bucket(unsigned long) |
1666 | | |
1667 | | /*** |
1668 | | Different probing techniques usually provide a trade-off between memory locality and avoidance of clustering. |
1669 | | Since Robin Hood hashing is relatively resilient to clustering (both primary and secondary), linear probing is the most cache friendly alternativeis typically used. |
1670 | | |
1671 | | It's the core algorithm of this hash map with highly optimization/benchmark. |
1672 | | normally linear probing is inefficient with high load factor, it use a new 3-way linear |
1673 | | probing strategy to search empty slot. from benchmark even the load factor > 0.9, it's more 2-3 timer fast than |
1674 | | one-way search strategy. |
1675 | | |
1676 | | 1. linear or quadratic probing a few cache line for less cache miss from input slot "bucket_from". |
1677 | | 2. the first search slot from member variant "_last", init with 0 with linear probe |
1678 | | 3. the second search slot from calculated pos "(_num_filled + _last) & _mask", it's like a rand value |
1679 | | ****/ |
1680 | | // key is not in this slot. Find a place to put it. |
1681 | | size_type find_empty_bucket(const size_type bucket_from, uint32_t csize) noexcept |
1682 | 313k | { |
1683 | | #if EMH_HIGH_LOAD |
1684 | | if (_ehead) |
1685 | | return pop_empty(_ehead); |
1686 | | #endif |
1687 | | |
1688 | 313k | auto bucket = bucket_from; |
1689 | 313k | if (EMH_EMPTY(++bucket & _mask) || EMH_EMPTY(++bucket & _mask)) |
1690 | 211k | return bucket & _mask; |
1691 | | |
1692 | | #ifdef EMH_QUADRATIC |
1693 | | constexpr size_type linear_probe_length = 2 + 2 * EMH_CACHE_LINE_SIZE / sizeof(Index); |
1694 | | for (size_type offset = csize + 2, step = 4; offset <= linear_probe_length; ) { |
1695 | | bucket = (bucket_from + offset) & _mask; |
1696 | | if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket)) |
1697 | | return bucket; |
1698 | | offset += step; //7/8. 12. 16 |
1699 | | } |
1700 | | #else |
1701 | 101k | constexpr size_type quadratic_probe_length = 6u; |
1702 | 235k | for (size_type offset = 1 + csize, step = 3u; step < quadratic_probe_length; ) { |
1703 | 208k | bucket = (bucket_from + offset) & _mask; |
1704 | 208k | if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket)) |
1705 | 75.3k | return bucket; |
1706 | 133k | offset += step++; |
1707 | 133k | } |
1708 | 26.6k | #endif |
1709 | | |
1710 | 26.6k | prefetch_heap_block((char*)&_index[_last]); |
1711 | | |
1712 | 58.2k | for (;;) { |
1713 | | #if EMH_PACK_TAIL |
1714 | | //find empty bucket and skip next |
1715 | | if (EMH_EMPTY(_last++))// || EMH_EMPTY(_last++)) |
1716 | | return _last++ - 1; |
1717 | | |
1718 | | if (EMH_UNLIKELY(_last >= _num_buckets)) |
1719 | | _last = 0; |
1720 | | |
1721 | | auto medium = (_mask / 4 + _last++) & _mask; |
1722 | | if (EMH_EMPTY(medium)) |
1723 | | return medium; |
1724 | | #else |
1725 | 58.2k | _last &= _mask; |
1726 | 58.2k | if (EMH_EMPTY(++_last))// || EMH_EMPTY(++_last)) |
1727 | 14.0k | return _last; |
1728 | | |
1729 | 44.1k | auto medium = (_num_buckets / 2 + _last) & _mask; |
1730 | 44.1k | if (EMH_EMPTY(medium))// || EMH_EMPTY(++medium)) |
1731 | 12.6k | return medium; |
1732 | 44.1k | #endif |
1733 | 44.1k | } |
1734 | | |
1735 | 26.6k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::find_empty_bucket(unsigned int, unsigned int) Line | Count | Source | 1682 | 313k | { | 1683 | | #if EMH_HIGH_LOAD | 1684 | | if (_ehead) | 1685 | | return pop_empty(_ehead); | 1686 | | #endif | 1687 | | | 1688 | 313k | auto bucket = bucket_from; | 1689 | 313k | if (EMH_EMPTY(++bucket & _mask) || EMH_EMPTY(++bucket & _mask)) | 1690 | 211k | return bucket & _mask; | 1691 | | | 1692 | | #ifdef EMH_QUADRATIC | 1693 | | constexpr size_type linear_probe_length = 2 + 2 * EMH_CACHE_LINE_SIZE / sizeof(Index); | 1694 | | for (size_type offset = csize + 2, step = 4; offset <= linear_probe_length; ) { | 1695 | | bucket = (bucket_from + offset) & _mask; | 1696 | | if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket)) | 1697 | | return bucket; | 1698 | | offset += step; //7/8. 12. 16 | 1699 | | } | 1700 | | #else | 1701 | 101k | constexpr size_type quadratic_probe_length = 6u; | 1702 | 235k | for (size_type offset = 1 + csize, step = 3u; step < quadratic_probe_length; ) { | 1703 | 208k | bucket = (bucket_from + offset) & _mask; | 1704 | 208k | if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket)) | 1705 | 75.3k | return bucket; | 1706 | 133k | offset += step++; | 1707 | 133k | } | 1708 | 26.6k | #endif | 1709 | | | 1710 | 26.6k | prefetch_heap_block((char*)&_index[_last]); | 1711 | | | 1712 | 58.2k | for (;;) { | 1713 | | #if EMH_PACK_TAIL | 1714 | | //find empty bucket and skip next | 1715 | | if (EMH_EMPTY(_last++))// || EMH_EMPTY(_last++)) | 1716 | | return _last++ - 1; | 1717 | | | 1718 | | if (EMH_UNLIKELY(_last >= _num_buckets)) | 1719 | | _last = 0; | 1720 | | | 1721 | | auto medium = (_mask / 4 + _last++) & _mask; | 1722 | | if (EMH_EMPTY(medium)) | 1723 | | return medium; | 1724 | | #else | 1725 | 58.2k | _last &= _mask; | 1726 | 58.2k | if (EMH_EMPTY(++_last))// || EMH_EMPTY(++_last)) | 1727 | 14.0k | return _last; | 1728 | | | 1729 | 44.1k | auto medium = (_num_buckets / 2 + _last) & _mask; | 1730 | 44.1k | if (EMH_EMPTY(medium))// || EMH_EMPTY(++medium)) | 1731 | 12.6k | return medium; | 1732 | 44.1k | #endif | 1733 | 44.1k | } | 1734 | | | 1735 | 26.6k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::find_empty_bucket(unsigned int, unsigned int) |
1736 | | |
1737 | | size_type find_last_bucket(size_type main_bucket) const |
1738 | 13.0k | { |
1739 | 13.0k | auto next_bucket = _index[main_bucket].next; |
1740 | 13.0k | if (next_bucket == main_bucket) |
1741 | 11.2k | return main_bucket; |
1742 | | |
1743 | 2.29k | while (true) { |
1744 | 2.29k | const auto nbucket = _index[next_bucket].next; |
1745 | 2.29k | if (nbucket == next_bucket) |
1746 | 1.76k | return next_bucket; |
1747 | 531 | next_bucket = nbucket; |
1748 | 531 | } |
1749 | 1.76k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::find_last_bucket(unsigned int) const Line | Count | Source | 1738 | 13.0k | { | 1739 | 13.0k | auto next_bucket = _index[main_bucket].next; | 1740 | 13.0k | if (next_bucket == main_bucket) | 1741 | 11.2k | return main_bucket; | 1742 | | | 1743 | 2.29k | while (true) { | 1744 | 2.29k | const auto nbucket = _index[next_bucket].next; | 1745 | 2.29k | if (nbucket == next_bucket) | 1746 | 1.76k | return next_bucket; | 1747 | 531 | next_bucket = nbucket; | 1748 | 531 | } | 1749 | 1.76k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::find_last_bucket(unsigned int) const |
1750 | | |
1751 | | size_type find_prev_bucket(const size_type main_bucket, const size_type bucket) const |
1752 | 65.8k | { |
1753 | 65.8k | auto next_bucket = _index[main_bucket].next; |
1754 | 65.8k | if (next_bucket == bucket) |
1755 | 54.1k | return main_bucket; |
1756 | | |
1757 | 14.3k | while (true) { |
1758 | 14.3k | const auto nbucket = _index[next_bucket].next; |
1759 | 14.3k | if (nbucket == bucket) |
1760 | 11.7k | return next_bucket; |
1761 | 2.62k | next_bucket = nbucket; |
1762 | 2.62k | } |
1763 | 11.7k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::find_prev_bucket(unsigned int, unsigned int) const Line | Count | Source | 1752 | 65.8k | { | 1753 | 65.8k | auto next_bucket = _index[main_bucket].next; | 1754 | 65.8k | if (next_bucket == bucket) | 1755 | 54.1k | return main_bucket; | 1756 | | | 1757 | 14.3k | while (true) { | 1758 | 14.3k | const auto nbucket = _index[next_bucket].next; | 1759 | 14.3k | if (nbucket == bucket) | 1760 | 11.7k | return next_bucket; | 1761 | 2.62k | next_bucket = nbucket; | 1762 | 2.62k | } | 1763 | 11.7k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::find_prev_bucket(unsigned int, unsigned int) const |
1764 | | |
1765 | | size_type hash_bucket(const KeyT& key) const noexcept |
1766 | 203k | { |
1767 | 203k | return (size_type)hash_key(key) & _mask; |
1768 | 203k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hash_bucket(StringPiece const&) const Line | Count | Source | 1766 | 203k | { | 1767 | 203k | return (size_type)hash_key(key) & _mask; | 1768 | 203k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hash_bucket(StringPiece const&) const |
1769 | | |
1770 | | size_type hash_main(const size_type bucket) const noexcept |
1771 | 109k | { |
1772 | 109k | const auto slot = _index[bucket].slot & _mask; |
1773 | 109k | return (size_type)hash_key(_pairs[slot].first) & _mask; |
1774 | 109k | } emhash8::HashMap<StringPiece, Node*, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, Node*> >, emhash8::DefaultPolicy>::hash_main(unsigned int) const Line | Count | Source | 1771 | 109k | { | 1772 | 109k | const auto slot = _index[bucket].slot & _mask; | 1773 | 109k | return (size_type)hash_key(_pairs[slot].first) & _mask; | 1774 | 109k | } |
Unexecuted instantiation: emhash8::HashMap<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> >, std::__1::hash<StringPiece>, std::__1::equal_to<StringPiece>, std::__1::allocator<std::__1::pair<StringPiece, std::__1::unique_ptr<BuildLog::LogEntry, std::__1::default_delete<BuildLog::LogEntry> > > >, emhash8::DefaultPolicy>::hash_main(unsigned int) const |
1775 | | |
1776 | | #if EMH_INT_HASH |
1777 | | static constexpr uint64_t KC = UINT64_C(11400714819323198485); |
1778 | | static uint64_t hash64(uint64_t key) |
1779 | | { |
1780 | | #if __SIZEOF_INT128__ && EMH_INT_HASH == 1 |
1781 | | __uint128_t r = key; r *= KC; |
1782 | | return (uint64_t)(r >> 64) + (uint64_t)r; |
1783 | | #elif EMH_INT_HASH == 2 |
1784 | | //MurmurHash3Mixer |
1785 | | uint64_t h = key; |
1786 | | h ^= h >> 33; |
1787 | | h *= 0xff51afd7ed558ccd; |
1788 | | h ^= h >> 33; |
1789 | | h *= 0xc4ceb9fe1a85ec53; |
1790 | | h ^= h >> 33; |
1791 | | return h; |
1792 | | #elif _WIN64 && EMH_INT_HASH == 1 |
1793 | | uint64_t high; |
1794 | | return _umul128(key, KC, &high) + high; |
1795 | | #elif EMH_INT_HASH == 3 |
1796 | | auto ror = (key >> 32) | (key << 32); |
1797 | | auto low = key * 0xA24BAED4963EE407ull; |
1798 | | auto high = ror * 0x9FB21C651E98DF25ull; |
1799 | | auto mix = low + high; |
1800 | | return mix; |
1801 | | #elif EMH_INT_HASH == 1 |
1802 | | uint64_t r = key * UINT64_C(0xca4bcaa75ec3f625); |
1803 | | return (r >> 32) + r; |
1804 | | #elif EMH_WYHASH64 |
1805 | | return wyhash64(key, KC); |
1806 | | #else |
1807 | | uint64_t x = key; |
1808 | | x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); |
1809 | | x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); |
1810 | | x = x ^ (x >> 31); |
1811 | | return x; |
1812 | | #endif |
1813 | | } |
1814 | | #endif |
1815 | | |
1816 | | #if EMH_WYHASH_HASH |
1817 | | //#define WYHASH_CONDOM 1 |
1818 | | static uint64_t wymix(uint64_t A, uint64_t B) |
1819 | | { |
1820 | | #if defined(__SIZEOF_INT128__) |
1821 | | __uint128_t r = A; r *= B; |
1822 | | #if WYHASH_CONDOM2 |
1823 | | A ^= (uint64_t)r; B ^= (uint64_t)(r >> 64); |
1824 | | #else |
1825 | | A = (uint64_t)r; B = (uint64_t)(r >> 64); |
1826 | | #endif |
1827 | | |
1828 | | #elif defined(_MSC_VER) && defined(_M_X64) |
1829 | | #if WYHASH_CONDOM2 |
1830 | | uint64_t a, b; |
1831 | | a = _umul128(A, B, &b); |
1832 | | A ^= a; B ^= b; |
1833 | | #else |
1834 | | A = _umul128(A, B, &B); |
1835 | | #endif |
1836 | | #else |
1837 | | uint64_t ha = A >> 32, hb = B >> 32, la = (uint32_t)A, lb = (uint32_t)B, hi, lo; |
1838 | | uint64_t rh = ha * hb, rm0 = ha * lb, rm1 = hb * la, rl = la * lb, t = rl + (rm0 << 32), c = t < rl; |
1839 | | lo = t + (rm1 << 32); c += lo < t; hi = rh + (rm0 >> 32) + (rm1 >> 32) + c; |
1840 | | #if WYHASH_CONDOM2 |
1841 | | A ^= lo; B ^= hi; |
1842 | | #else |
1843 | | A = lo; B = hi; |
1844 | | #endif |
1845 | | #endif |
1846 | | return A ^ B; |
1847 | | } |
1848 | | |
1849 | | //multiply and xor mix function, aka MUM |
1850 | | static inline uint64_t wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v; } |
1851 | | static inline uint64_t wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v; } |
1852 | | static inline uint64_t wyr3(const uint8_t *p, size_t k) { |
1853 | | return (((uint64_t)p[0]) << 16) | (((uint64_t)p[k >> 1]) << 8) | p[k - 1]; |
1854 | | } |
1855 | | |
1856 | | inline static const uint64_t secret[4] = { |
1857 | | 0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull, |
1858 | | 0x4b33a62ed433d4a3ull, 0x4d5a2da51de1aa47ull}; |
1859 | | public: |
1860 | | //wyhash main function https://github.com/wangyi-fudan/wyhash |
1861 | | static uint64_t wyhashstr(const char *key, const size_t len) |
1862 | | { |
1863 | | uint64_t a = 0, b = 0, seed = secret[0]; |
1864 | | const uint8_t *p = (const uint8_t*)key; |
1865 | | if (EMH_LIKELY(len <= 16)) { |
1866 | | if (EMH_LIKELY(len >= 4)) { |
1867 | | const auto half = (len >> 3) << 2; |
1868 | | a = (wyr4(p) << 32U) | wyr4(p + half); p += len - 4; |
1869 | | b = (wyr4(p) << 32U) | wyr4(p - half); |
1870 | | } else if (len) { |
1871 | | a = wyr3(p, len); |
1872 | | } |
1873 | | } else { |
1874 | | size_t i = len; |
1875 | | if (EMH_UNLIKELY(i > 48)) { |
1876 | | uint64_t see1 = seed, see2 = seed; |
1877 | | do { |
1878 | | seed = wymix(wyr8(p + 0) ^ secret[1], wyr8(p + 8) ^ seed); |
1879 | | see1 = wymix(wyr8(p + 16) ^ secret[2], wyr8(p + 24) ^ see1); |
1880 | | see2 = wymix(wyr8(p + 32) ^ secret[3], wyr8(p + 40) ^ see2); |
1881 | | p += 48; i -= 48; |
1882 | | } while (EMH_LIKELY(i > 48)); |
1883 | | seed ^= see1 ^ see2; |
1884 | | } |
1885 | | while (i > 16) { |
1886 | | seed = wymix(wyr8(p) ^ secret[1], wyr8(p + 8) ^ seed); |
1887 | | i -= 16; p += 16; |
1888 | | } |
1889 | | a = wyr8(p + i - 16); |
1890 | | b = wyr8(p + i - 8); |
1891 | | } |
1892 | | |
1893 | | return wymix(secret[1] ^ len, wymix(a ^ secret[1], b ^ seed)); |
1894 | | } |
1895 | | #endif |
1896 | | |
1897 | | private: |
1898 | | template<typename UType, typename std::enable_if<std::is_integral<UType>::value, uint32_t>::type = 0> |
1899 | | inline uint64_t hash_key(const UType key) const |
1900 | | { |
1901 | | #if EMH_INT_HASH |
1902 | | return hash64(key); |
1903 | | #else |
1904 | | return _hasher(key); |
1905 | | #endif |
1906 | | } |
1907 | | |
1908 | | template<typename UType, typename std::enable_if<std::is_same<UType, std::string>::value, uint32_t>::type = 0> |
1909 | | inline uint64_t hash_key(const UType& key) const |
1910 | 0 | { |
1911 | | #if EMH_WYHASH_HASH |
1912 | | return wyhashstr(key.data(), key.size()); |
1913 | | #else |
1914 | 0 | return _hasher(key); |
1915 | 0 | #endif |
1916 | 0 | } |
1917 | | |
1918 | | template<typename UType, typename std::enable_if<!std::is_integral<UType>::value && !std::is_same<UType, std::string>::value, uint32_t>::type = 0> |
1919 | | inline uint64_t hash_key(const UType& key) const |
1920 | 2.24M | { |
1921 | 2.24M | return _hasher(key); |
1922 | 2.24M | } _ZNK7emhash87HashMapI11StringPieceP4NodeNSt3__14hashIS1_EENS4_8equal_toIS1_EENS4_9allocatorINS4_4pairIS1_S3_EEEENS_13DefaultPolicyEE8hash_keyIS1_TnNS4_9enable_ifIXaantsr3std11is_integralIT_EE5valuentsr3std7is_sameISH_NS4_12basic_stringIcNS4_11char_traitsIcEENS9_IcEEEEEE5valueEjE4typeELj0EEEmRKSH_ Line | Count | Source | 1920 | 2.24M | { | 1921 | 2.24M | return _hasher(key); | 1922 | 2.24M | } |
Unexecuted instantiation: _ZNK7emhash87HashMapI11StringPieceNSt3__110unique_ptrIN8BuildLog8LogEntryENS2_14default_deleteIS5_EEEENS2_4hashIS1_EENS2_8equal_toIS1_EENS2_9allocatorINS2_4pairIS1_S8_EEEENS_13DefaultPolicyEE8hash_keyIS1_TnNS2_9enable_ifIXaantsr3std11is_integralIT_EE5valuentsr3std7is_sameISL_NS2_12basic_stringIcNS2_11char_traitsIcEENSD_IcEEEEEE5valueEjE4typeELj0EEEmRKSL_ |
1923 | | |
1924 | | private: |
1925 | | using PairAlloc = typename std::allocator_traits<AllocT>::template rebind_alloc<value_type>; |
1926 | | using PairAllocTraits = std::allocator_traits<PairAlloc>; |
1927 | | using IndexAlloc = typename std::allocator_traits<AllocT>::template rebind_alloc<Index>; |
1928 | | using IndexAllocTraits = std::allocator_traits<IndexAlloc>; |
1929 | | |
1930 | | Index* _index; |
1931 | | value_type*_pairs; |
1932 | | |
1933 | | HashT _hasher; |
1934 | | EqT _eq; |
1935 | | uint32_t _mlf; |
1936 | | size_type _mask; |
1937 | | size_type _num_buckets; |
1938 | | size_type _num_filled; |
1939 | | size_type _last; |
1940 | | #if EMH_HIGH_LOAD |
1941 | | size_type _ehead; |
1942 | | #endif |
1943 | | size_type _etail; |
1944 | | size_type _pairs_capacity; |
1945 | | PairAlloc _pair_allocator; |
1946 | | IndexAlloc _index_allocator; |
1947 | | }; |
1948 | | } // namespace emhash |
1949 | | |