RavEngine
Loading...
Searching...
No Matches
phmap.h
1#if !defined(phmap_h_guard_)
2#define phmap_h_guard_
3
4// ---------------------------------------------------------------------------
5// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// https://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18//
19// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp)
20// with modifications.
21//
22// Copyright 2018 The Abseil Authors.
23//
24// Licensed under the Apache License, Version 2.0 (the "License");
25// you may not use this file except in compliance with the License.
26// You may obtain a copy of the License at
27//
28// https://www.apache.org/licenses/LICENSE-2.0
29//
30// Unless required by applicable law or agreed to in writing, software
31// distributed under the License is distributed on an "AS IS" BASIS,
32// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33// See the License for the specific language governing permissions and
34// limitations under the License.
35// ---------------------------------------------------------------------------
36
37#ifdef _MSC_VER
38 #pragma warning(push)
39
40 #pragma warning(disable : 4127) // conditional expression is constant
41 #pragma warning(disable : 4324) // structure was padded due to alignment specifier
42 #pragma warning(disable : 4514) // unreferenced inline function has been removed
43 #pragma warning(disable : 4623) // default constructor was implicitly defined as deleted
44 #pragma warning(disable : 4625) // copy constructor was implicitly defined as deleted
45 #pragma warning(disable : 4626) // assignment operator was implicitly defined as deleted
46 #pragma warning(disable : 4710) // function not inlined
47 #pragma warning(disable : 4711) // selected for automatic inline expansion
48 #pragma warning(disable : 4820) // '6' bytes padding added after data member
49 #pragma warning(disable : 4868) // compiler may not enforce left-to-right evaluation order in braced initializer list
50 #pragma warning(disable : 5027) // move assignment operator was implicitly defined as deleted
51 #pragma warning(disable : 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
52#endif
53
54#include <algorithm>
55#include <cmath>
56#include <cstring>
57#include <iterator>
58#include <limits>
59#include <memory>
60#include <tuple>
61#include <type_traits>
62#include <utility>
63#include <array>
64#include <cassert>
65#include <atomic>
66
67#include "phmap_fwd_decl.h"
68#include "phmap_utils.h"
69#include "phmap_base.h"
70
71#if PHMAP_HAVE_STD_STRING_VIEW
72 #include <string_view>
73#endif
74
75namespace phmap {
76
77namespace priv {
78
79// --------------------------------------------------------------------------
80template <size_t Width>
82{
83public:
84 probe_seq(size_t hashval, size_t mask) {
85 assert(((mask + 1) & mask) == 0 && "not a mask");
86 mask_ = mask;
87 offset_ = hashval & mask_;
88 }
89 size_t offset() const { return offset_; }
90 size_t offset(size_t i) const { return (offset_ + i) & mask_; }
91
92 void next() {
93 index_ += Width;
94 offset_ += index_;
95 offset_ &= mask_;
96 }
97 // 0-based probe index. The i-th probe in the probe sequence.
98 size_t getindex() const { return index_; }
99
100private:
101 size_t mask_;
102 size_t offset_;
103 size_t index_ = 0;
104};
105
106// --------------------------------------------------------------------------
107template <class ContainerKey, class Hash, class Eq>
109{
110 template <class PassedKey, class... Args>
111 std::pair<
112 decltype(std::declval<const Hash&>()(std::declval<const PassedKey&>())),
113 decltype(std::declval<const Eq&>()(std::declval<const ContainerKey&>(),
114 std::declval<const PassedKey&>()))>*
115 operator()(const PassedKey&, const Args&...) const;
116};
117
118// --------------------------------------------------------------------------
119template <class E, class Policy, class Hash, class Eq, class... Ts>
120struct IsDecomposable : std::false_type {};
121
122template <class Policy, class Hash, class Eq, class... Ts>
124 phmap::void_t<decltype(
125 Policy::apply(RequireUsableKey<typename Policy::key_type, Hash, Eq>(),
126 std::declval<Ts>()...))>,
127 Policy, Hash, Eq, Ts...> : std::true_type {};
128
129// TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it.
130// --------------------------------------------------------------------------
131template <class T>
132constexpr bool IsNoThrowSwappable() {
133 using std::swap;
134 return noexcept(swap(std::declval<T&>(), std::declval<T&>()));
135}
136
137// --------------------------------------------------------------------------
138template <typename T>
139int TrailingZeros(T x) {
140 PHMAP_IF_CONSTEXPR(sizeof(T) == 8)
141 return base_internal::CountTrailingZerosNonZero64(static_cast<uint64_t>(x));
142 else
143 return base_internal::CountTrailingZerosNonZero32(static_cast<uint32_t>(x));
144}
145
146// --------------------------------------------------------------------------
147template <typename T>
148int LeadingZeros(T x) {
149 PHMAP_IF_CONSTEXPR(sizeof(T) == 8)
150 return base_internal::CountLeadingZeros64(static_cast<uint64_t>(x));
151 else
152 return base_internal::CountLeadingZeros32(static_cast<uint32_t>(x));
153}
154
155// --------------------------------------------------------------------------
156// An abstraction over a bitmask. It provides an easy way to iterate through the
157// indexes of the set bits of a bitmask. When Shift=0 (platforms with SSE),
158// this is a true bitmask. On non-SSE, platforms the arithematic used to
159// emulate the SSE behavior works in bytes (Shift=3) and leaves each bytes as
160// either 0x00 or 0x80.
161//
162// For example:
163// for (int i : BitMask<uint32_t, 16>(0x5)) -> yields 0, 2
164// for (int i : BitMask<uint64_t, 8, 3>(0x0000000080800000)) -> yields 2, 3
165// --------------------------------------------------------------------------
166template <class T, int SignificantBits, int Shift = 0>
168{
169 static_assert(std::is_unsigned<T>::value, "");
170 static_assert(Shift == 0 || Shift == 3, "");
171
172public:
173 // These are useful for unit tests (gunit).
174 using value_type = int;
175 using iterator = BitMask;
176 using const_iterator = BitMask;
177
178 explicit BitMask(T mask) : mask_(mask) {}
179 BitMask& operator++() {
180 mask_ &= (mask_ - 1);
181 return *this;
182 }
183 explicit operator bool() const { return mask_ != 0; }
184 int operator*() const { return LowestBitSet(); }
185 int LowestBitSet() const {
186 return priv::TrailingZeros(mask_) >> Shift;
187 }
188 int HighestBitSet() const {
189 return (sizeof(T) * CHAR_BIT - priv::LeadingZeros(mask_) -
190 1) >>
191 Shift;
192 }
193
194 BitMask begin() const { return *this; }
195 BitMask end() const { return BitMask(0); }
196
197 int TrailingZeros() const {
198 return priv::TrailingZeros(mask_) >> Shift;
199 }
200
201 int LeadingZeros() const {
202 constexpr int total_significant_bits = SignificantBits << Shift;
203 constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
204 return priv::LeadingZeros(mask_ << extra_bits) >> Shift;
205 }
206
207private:
208 friend bool operator==(const BitMask& a, const BitMask& b) {
209 return a.mask_ == b.mask_;
210 }
211 friend bool operator!=(const BitMask& a, const BitMask& b) {
212 return a.mask_ != b.mask_;
213 }
214
215 T mask_;
216};
217
218// --------------------------------------------------------------------------
219using ctrl_t = signed char;
220using h2_t = uint8_t;
221
222// --------------------------------------------------------------------------
223// The values here are selected for maximum performance. See the static asserts
224// below for details.
225// --------------------------------------------------------------------------
226enum Ctrl : ctrl_t
227{
228 kEmpty = -128, // 0b10000000
229 kDeleted = -2, // 0b11111110
230 kSentinel = -1, // 0b11111111
231};
232
233static_assert(
234 kEmpty & kDeleted & kSentinel & 0x80,
235 "Special markers need to have the MSB to make checking for them efficient");
236static_assert(kEmpty < kSentinel && kDeleted < kSentinel,
237 "kEmpty and kDeleted must be smaller than kSentinel to make the "
238 "SIMD test of IsEmptyOrDeleted() efficient");
239static_assert(kSentinel == -1,
240 "kSentinel must be -1 to elide loading it from memory into SIMD "
241 "registers (pcmpeqd xmm, xmm)");
242static_assert(kEmpty == -128,
243 "kEmpty must be -128 to make the SIMD check for its "
244 "existence efficient (psignb xmm, xmm)");
245static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F,
246 "kEmpty and kDeleted must share an unset bit that is not shared "
247 "by kSentinel to make the scalar test for MatchEmptyOrDeleted() "
248 "efficient");
249static_assert(kDeleted == -2,
250 "kDeleted must be -2 to make the implementation of "
251 "ConvertSpecialToEmptyAndFullToDeleted efficient");
252
253// --------------------------------------------------------------------------
254// A single block of empty control bytes for tables without any slots allocated.
255// This enables removing a branch in the hot path of find().
256// --------------------------------------------------------------------------
257inline ctrl_t* EmptyGroup() {
258 alignas(16) static constexpr ctrl_t empty_group[] = {
259 kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,
260 kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty};
261 return const_cast<ctrl_t*>(empty_group);
262}
263
264// --------------------------------------------------------------------------
265inline size_t HashSeed(const ctrl_t* ctrl) {
266 // The low bits of the pointer have little or no entropy because of
267 // alignment. We shift the pointer to try to use higher entropy bits. A
268 // good number seems to be 12 bits, because that aligns with page size.
269 return reinterpret_cast<uintptr_t>(ctrl) >> 12;
270}
271
272#ifdef PHMAP_NON_DETERMINISTIC
273
274inline size_t H1(size_t hashval, const ctrl_t* ctrl) {
275 // use ctrl_ pointer to add entropy to ensure
276 // non-deterministic iteration order.
277 return (hashval >> 7) ^ HashSeed(ctrl);
278}
279
280#else
281
282inline size_t H1(size_t hashval, const ctrl_t* ) {
283 return (hashval >> 7);
284}
285
286#endif
287
288
289inline ctrl_t H2(size_t hashval) { return (ctrl_t)(hashval & 0x7F); }
290
291inline bool IsEmpty(ctrl_t c) { return c == kEmpty; }
292inline bool IsFull(ctrl_t c) { return c >= 0; }
293inline bool IsDeleted(ctrl_t c) { return c == kDeleted; }
294inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; }
295
296#if PHMAP_HAVE_SSE2
297
298#ifdef _MSC_VER
299 #pragma warning(push)
300 #pragma warning(disable : 4365) // conversion from 'int' to 'T', signed/unsigned mismatch
301#endif
302
303// --------------------------------------------------------------------------
304// https://github.com/abseil/abseil-cpp/issues/209
305// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853
306// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char
307// Work around this by using the portable implementation of Group
308// when using -funsigned-char under GCC.
309// --------------------------------------------------------------------------
310inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) {
311#if defined(__GNUC__) && !defined(__clang__)
312 #pragma GCC diagnostic push
313 #pragma GCC diagnostic ignored "-Woverflow"
314
315 if (std::is_unsigned<char>::value) {
316 const __m128i mask = _mm_set1_epi8(static_cast<char>(0x80));
317 const __m128i diff = _mm_subs_epi8(b, a);
318 return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask);
319 }
320
321 #pragma GCC diagnostic pop
322#endif
323 return _mm_cmpgt_epi8(a, b);
324}
325
326// --------------------------------------------------------------------------
327// --------------------------------------------------------------------------
328struct GroupSse2Impl
329{
330 enum { kWidth = 16 }; // the number of slots per group
331
332 explicit GroupSse2Impl(const ctrl_t* pos) {
333 ctrl = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pos));
334 }
335
336 // Returns a bitmask representing the positions of slots that match hash.
337 // ----------------------------------------------------------------------
338 BitMask<uint32_t, kWidth> Match(h2_t hash) const {
339 auto match = _mm_set1_epi8((char)hash);
340 return BitMask<uint32_t, kWidth>(
341 _mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)));
342 }
343
344 // Returns a bitmask representing the positions of empty slots.
345 // ------------------------------------------------------------
346 BitMask<uint32_t, kWidth> MatchEmpty() const {
347#if PHMAP_HAVE_SSSE3
348 // This only works because kEmpty is -128.
349 return BitMask<uint32_t, kWidth>(
350 _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)));
351#else
352 return Match(static_cast<h2_t>(kEmpty));
353#endif
354 }
355
356 // Returns a bitmask representing the positions of empty or deleted slots.
357 // -----------------------------------------------------------------------
358 BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const {
359 auto special = _mm_set1_epi8(kSentinel);
360 return BitMask<uint32_t, kWidth>(
361 _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)));
362 }
363
364 // Returns the number of trailing empty or deleted elements in the group.
365 // ----------------------------------------------------------------------
366 uint32_t CountLeadingEmptyOrDeleted() const {
367 auto special = _mm_set1_epi8(kSentinel);
368 return TrailingZeros(
369 _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1);
370 }
371
372 // ----------------------------------------------------------------------
373 void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
374 auto msbs = _mm_set1_epi8(static_cast<char>(-128));
375 auto x126 = _mm_set1_epi8(126);
376#if PHMAP_HAVE_SSSE3
377 auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs);
378#else
379 auto zero = _mm_setzero_si128();
380 auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl);
381 auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126));
382#endif
383 _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res);
384 }
385
386 __m128i ctrl;
387};
388
389#ifdef _MSC_VER
390 #pragma warning(pop)
391#endif
392
393#endif // PHMAP_HAVE_SSE2
394
395// --------------------------------------------------------------------------
396// --------------------------------------------------------------------------
398{
399 enum { kWidth = 8 };
400
401 explicit GroupPortableImpl(const ctrl_t* pos)
402 : ctrl(little_endian::Load64(pos)) {}
403
404 BitMask<uint64_t, kWidth, 3> Match(h2_t hash) const {
405 // For the technique, see:
406 // http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord
407 // (Determine if a word has a byte equal to n).
408 //
409 // Caveat: there are false positives but:
410 // - they only occur if there is a real match
411 // - they never occur on kEmpty, kDeleted, kSentinel
412 // - they will be handled gracefully by subsequent checks in code
413 //
414 // Example:
415 // v = 0x1716151413121110
416 // hash = 0x12
417 // retval = (v - lsbs) & ~v & msbs = 0x0000000080800000
418 constexpr uint64_t msbs = 0x8080808080808080ULL;
419 constexpr uint64_t lsbs = 0x0101010101010101ULL;
420 auto x = ctrl ^ (lsbs * hash);
421 return BitMask<uint64_t, kWidth, 3>((x - lsbs) & ~x & msbs);
422 }
423
424 BitMask<uint64_t, kWidth, 3> MatchEmpty() const {
425 constexpr uint64_t msbs = 0x8080808080808080ULL;
426 return BitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 6)) & msbs);
427 }
428
429 BitMask<uint64_t, kWidth, 3> MatchEmptyOrDeleted() const {
430 constexpr uint64_t msbs = 0x8080808080808080ULL;
431 return BitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 7)) & msbs);
432 }
433
434 uint32_t CountLeadingEmptyOrDeleted() const {
435 constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL;
436 return (uint32_t)((TrailingZeros(((~ctrl & (ctrl >> 7)) | gaps) + 1) + 7) >> 3);
437 }
438
439 void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
440 constexpr uint64_t msbs = 0x8080808080808080ULL;
441 constexpr uint64_t lsbs = 0x0101010101010101ULL;
442 auto x = ctrl & msbs;
443 auto res = (~x + (x >> 7)) & ~lsbs;
444 little_endian::Store64(dst, res);
445 }
446
447 uint64_t ctrl;
448};
449
450#if PHMAP_HAVE_SSE2
451 using Group = GroupSse2Impl;
452#else
453 using Group = GroupPortableImpl;
454#endif
455
456template <class Policy, class Hash, class Eq, class Alloc>
457class raw_hash_set;
458
459inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; }
460
461// --------------------------------------------------------------------------
462// PRECONDITION:
463// IsValidCapacity(capacity)
464// ctrl[capacity] == kSentinel
465// ctrl[i] != kSentinel for all i < capacity
466// Applies mapping for every byte in ctrl:
467// DELETED -> EMPTY
468// EMPTY -> EMPTY
469// FULL -> DELETED
470// --------------------------------------------------------------------------
471inline void ConvertDeletedToEmptyAndFullToDeleted(
472 ctrl_t* ctrl, size_t capacity)
473{
474 assert(ctrl[capacity] == kSentinel);
475 assert(IsValidCapacity(capacity));
476 for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) {
477 Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
478 }
479 // Copy the cloned ctrl bytes.
480 std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth);
481 ctrl[capacity] = kSentinel;
482}
483
484// --------------------------------------------------------------------------
485// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1.
486// --------------------------------------------------------------------------
487inline size_t NormalizeCapacity(size_t n)
488{
489 return n ? ~size_t{} >> LeadingZeros(n) : 1;
490}
491
492// --------------------------------------------------------------------------
493// We use 7/8th as maximum load factor.
494// For 16-wide groups, that gives an average of two empty slots per group.
495// --------------------------------------------------------------------------
496inline size_t CapacityToGrowth(size_t capacity)
497{
498 assert(IsValidCapacity(capacity));
499 // `capacity*7/8`
500 PHMAP_IF_CONSTEXPR (Group::kWidth == 8) {
501 if (capacity == 7)
502 {
503 // x-x/8 does not work when x==7.
504 return 6;
505 }
506 }
507 return capacity - capacity / 8;
508}
509
510// --------------------------------------------------------------------------
511// From desired "growth" to a lowerbound of the necessary capacity.
512// Might not be a valid one and required NormalizeCapacity().
513// --------------------------------------------------------------------------
514inline size_t GrowthToLowerboundCapacity(size_t growth)
515{
516 // `growth*8/7`
517 PHMAP_IF_CONSTEXPR (Group::kWidth == 8) {
518 if (growth == 7)
519 {
520 // x+(x-1)/7 does not work when x==7.
521 return 8;
522 }
523 }
524 return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7);
525}
526
527namespace hashtable_debug_internal {
528
529// If it is a map, call get<0>().
530using std::get;
531template <typename T, typename = typename T::mapped_type>
532auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) {
533 return get<0>(pair);
534}
535
536// If it is not a map, return the value directly.
537template <typename T>
538const typename T::key_type& GetKey(const typename T::key_type& key, char) {
539 return key;
540}
541
542// --------------------------------------------------------------------------
543// Containers should specialize this to provide debug information for that
544// container.
545// --------------------------------------------------------------------------
546template <class Container, typename Enabler = void>
548{
549 // Returns the number of probes required to find `key` in `c`. The "number of
550 // probes" is a concept that can vary by container. Implementations should
551 // return 0 when `key` was found in the minimum number of operations and
552 // should increment the result for each non-trivial operation required to find
553 // `key`.
554 //
555 // The default implementation uses the bucket api from the standard and thus
556 // works for `std::unordered_*` containers.
557 // --------------------------------------------------------------------------
558 static size_t GetNumProbes(const Container& c,
559 const typename Container::key_type& key) {
560 if (!c.bucket_count()) return {};
561 size_t num_probes = 0;
562 size_t bucket = c.bucket(key);
563 for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) {
564 if (it == e) return num_probes;
565 if (c.key_eq()(key, GetKey<Container>(*it, 0))) return num_probes;
566 }
567 }
568};
569
570} // namespace hashtable_debug_internal
571
572// ----------------------------------------------------------------------------
573// I N F O Z S T U B S
574// ----------------------------------------------------------------------------
576{
577 void PrepareForSampling() {}
578};
579
580inline void RecordRehashSlow(HashtablezInfo*, size_t ) {}
581
582static inline void RecordInsertSlow(HashtablezInfo* , size_t, size_t ) {}
583
584static inline void RecordEraseSlow(HashtablezInfo*) {}
585
586static inline HashtablezInfo* SampleSlow(int64_t*) { return nullptr; }
587static inline void UnsampleSlow(HashtablezInfo* ) {}
588
590{
591public:
592 inline void RecordStorageChanged(size_t , size_t ) {}
593 inline void RecordRehash(size_t ) {}
594 inline void RecordInsert(size_t , size_t ) {}
595 inline void RecordErase() {}
596 friend inline void swap(HashtablezInfoHandle& ,
597 HashtablezInfoHandle& ) noexcept {}
598};
599
600static inline HashtablezInfoHandle Sample() { return HashtablezInfoHandle(); }
601
603{
604public:
605 // Returns a global Sampler.
606 static HashtablezSampler& Global() { static HashtablezSampler hzs; return hzs; }
607 HashtablezInfo* Register() { static HashtablezInfo info; return &info; }
608 void Unregister(HashtablezInfo* ) {}
609
610 using DisposeCallback = void (*)(const HashtablezInfo&);
611 DisposeCallback SetDisposeCallback(DisposeCallback ) { return nullptr; }
612 int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& ) { return 0; }
613};
614
615static inline void SetHashtablezEnabled(bool ) {}
616static inline void SetHashtablezSampleParameter(int32_t ) {}
617static inline void SetHashtablezMaxSamples(int32_t ) {}
618
619
620namespace memory_internal {
621
622// Constructs T into uninitialized storage pointed by `ptr` using the args
623// specified in the tuple.
624// ----------------------------------------------------------------------------
625template <class Alloc, class T, class Tuple, size_t... I>
626void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t,
629 *alloc, ptr, std::get<I>(std::forward<Tuple>(t))...);
630}
631
632template <class T, class F>
634 template <class... Args>
635 decltype(std::declval<F>()(std::declval<T>())) operator()(
636 Args&&... args) const {
637 return std::forward<F>(f)(T(std::forward<Args>(args)...));
638 }
639 F&& f;
640};
641
642template <class T, class Tuple, size_t... Is, class F>
643decltype(std::declval<F>()(std::declval<T>())) WithConstructedImpl(
644 Tuple&& t, phmap::index_sequence<Is...>, F&& f) {
645 return WithConstructedImplF<T, F>{std::forward<F>(f)}(
646 std::get<Is>(std::forward<Tuple>(t))...);
647}
648
649template <class T, size_t... Is>
650auto TupleRefImpl(T&& t, phmap::index_sequence<Is...>)
651 -> decltype(std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...)) {
652 return std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...);
653}
654
655// Returns a tuple of references to the elements of the input tuple. T must be a
656// tuple.
657// ----------------------------------------------------------------------------
658template <class T>
659auto TupleRef(T&& t) -> decltype(
660 TupleRefImpl(std::forward<T>(t),
661 phmap::make_index_sequence<
662 std::tuple_size<typename std::decay<T>::type>::value>())) {
663 return TupleRefImpl(
664 std::forward<T>(t),
665 phmap::make_index_sequence<
666 std::tuple_size<typename std::decay<T>::type>::value>());
667}
668
669template <class F, class K, class V>
670decltype(std::declval<F>()(std::declval<const K&>(), std::piecewise_construct,
671 std::declval<std::tuple<K>>(), std::declval<V>()))
672DecomposePairImpl(F&& f, std::pair<std::tuple<K>, V> p) {
673 const auto& key = std::get<0>(p.first);
674 return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
675 std::move(p.second));
676}
677
678} // namespace memory_internal
679
680
681// ----------------------------------------------------------------------------
682// R A W _ H A S H _ S E T
683// ----------------------------------------------------------------------------
684// An open-addressing
685// hashtable with quadratic probing.
686//
687// This is a low level hashtable on top of which different interfaces can be
688// implemented, like flat_hash_set, node_hash_set, string_hash_set, etc.
689//
690// The table interface is similar to that of std::unordered_set. Notable
691// differences are that most member functions support heterogeneous keys when
692// BOTH the hash and eq functions are marked as transparent. They do so by
693// providing a typedef called `is_transparent`.
694//
695// When heterogeneous lookup is enabled, functions that take key_type act as if
696// they have an overload set like:
697//
698// iterator find(const key_type& key);
699// template <class K>
700// iterator find(const K& key);
701//
702// size_type erase(const key_type& key);
703// template <class K>
704// size_type erase(const K& key);
705//
706// std::pair<iterator, iterator> equal_range(const key_type& key);
707// template <class K>
708// std::pair<iterator, iterator> equal_range(const K& key);
709//
710// When heterogeneous lookup is disabled, only the explicit `key_type` overloads
711// exist.
712//
713// find() also supports passing the hash explicitly:
714//
715// iterator find(const key_type& key, size_t hash);
716// template <class U>
717// iterator find(const U& key, size_t hash);
718//
719// In addition the pointer to element and iterator stability guarantees are
720// weaker: all iterators and pointers are invalidated after a new element is
721// inserted.
722//
723// IMPLEMENTATION DETAILS
724//
725// The table stores elements inline in a slot array. In addition to the slot
726// array the table maintains some control state per slot. The extra state is one
727// byte per slot and stores empty or deleted marks, or alternatively 7 bits from
728// the hash of an occupied slot. The table is split into logical groups of
729// slots, like so:
730//
731// Group 1 Group 2 Group 3
732// +---------------+---------------+---------------+
733// | | | | | | | | | | | | | | | | | | | | | | | | |
734// +---------------+---------------+---------------+
735//
736// On lookup the hash is split into two parts:
737// - H2: 7 bits (those stored in the control bytes)
738// - H1: the rest of the bits
739// The groups are probed using H1. For each group the slots are matched to H2 in
740// parallel. Because H2 is 7 bits (128 states) and the number of slots per group
741// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit.
742//
743// On insert, once the right group is found (as in lookup), its slots are
744// filled in order.
745//
746// On erase a slot is cleared. In case the group did not have any empty slots
747// before the erase, the erased slot is marked as deleted.
748//
749// Groups without empty slots (but maybe with deleted slots) extend the probe
750// sequence. The probing algorithm is quadratic. Given N the number of groups,
751// the probing function for the i'th probe is:
752//
753// P(0) = H1 % N
754//
755// P(i) = (P(i - 1) + i) % N
756//
757// This probing function guarantees that after N probes, all the groups of the
758// table will be probed exactly once.
759// ----------------------------------------------------------------------------
760template <class Policy, class Hash, class Eq, class Alloc>
762{
763 using PolicyTraits = hash_policy_traits<Policy>;
764 using KeyArgImpl =
765 KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
766
767public:
768 using init_type = typename PolicyTraits::init_type;
769 using key_type = typename PolicyTraits::key_type;
770 // TODO(sbenza): Hide slot_type as it is an implementation detail. Needs user
771 // code fixes!
772 using slot_type = typename PolicyTraits::slot_type;
773 using allocator_type = Alloc;
774 using size_type = size_t;
775 using difference_type = ptrdiff_t;
776 using hasher = Hash;
777 using key_equal = Eq;
778 using policy_type = Policy;
779 using value_type = typename PolicyTraits::value_type;
780 using reference = value_type&;
781 using const_reference = const value_type&;
782 using pointer = typename phmap::allocator_traits<
783 allocator_type>::template rebind_traits<value_type>::pointer;
784 using const_pointer = typename phmap::allocator_traits<
785 allocator_type>::template rebind_traits<value_type>::const_pointer;
786
787 // Alias used for heterogeneous lookup functions.
788 // `key_arg<K>` evaluates to `K` when the functors are transparent and to
789 // `key_type` otherwise. It permits template argument deduction on `K` for the
790 // transparent case.
791 template <class K>
792 using key_arg = typename KeyArgImpl::template type<K, key_type>;
793
794private:
795 // Give an early error when key_type is not hashable/eq.
796 auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k));
797 auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k));
798
799 using Layout = phmap::priv::Layout<ctrl_t, slot_type>;
800
801 static Layout MakeLayout(size_t capacity) {
802 assert(IsValidCapacity(capacity));
803 return Layout(capacity + Group::kWidth + 1, capacity);
804 }
805
807 using SlotAlloc = typename phmap::allocator_traits<
808 allocator_type>::template rebind_alloc<slot_type>;
809 using SlotAllocTraits = typename phmap::allocator_traits<
810 allocator_type>::template rebind_traits<slot_type>;
811
812 static_assert(std::is_lvalue_reference<reference>::value,
813 "Policy::element() must return a reference");
814
815 template <typename T>
816 struct SameAsElementReference
817 : std::is_same<typename std::remove_cv<
818 typename std::remove_reference<reference>::type>::type,
819 typename std::remove_cv<
820 typename std::remove_reference<T>::type>::type> {};
821
822 // An enabler for insert(T&&): T must be convertible to init_type or be the
823 // same as [cv] value_type [ref].
824 // Note: we separate SameAsElementReference into its own type to avoid using
825 // reference unless we need to. MSVC doesn't seem to like it in some
826 // cases.
827 template <class T>
828 using RequiresInsertable = typename std::enable_if<
830 SameAsElementReference<T>>::value,
831 int>::type;
832
833 // RequiresNotInit is a workaround for gcc prior to 7.1.
834 // See https://godbolt.org/g/Y4xsUh.
835 template <class T>
836 using RequiresNotInit =
837 typename std::enable_if<!std::is_same<T, init_type>::value, int>::type;
838
839 template <class... Ts>
840 using IsDecomposable = IsDecomposable<void, PolicyTraits, Hash, Eq, Ts...>;
841
842public:
843 static_assert(std::is_same<pointer, value_type*>::value,
844 "Allocators with custom pointer types are not supported");
845 static_assert(std::is_same<const_pointer, const value_type*>::value,
846 "Allocators with custom pointer types are not supported");
847
848 class iterator
849 {
850 friend class raw_hash_set;
851
852 public:
853 using iterator_category = std::forward_iterator_tag;
854 using value_type = typename raw_hash_set::value_type;
855 using reference =
856 phmap::conditional_t<PolicyTraits::constant_iterators::value,
857 const value_type&, value_type&>;
858 using pointer = phmap::remove_reference_t<reference>*;
859 using difference_type = typename raw_hash_set::difference_type;
860
861 iterator() {}
862
863 // PRECONDITION: not an end() iterator.
864 reference operator*() const { return PolicyTraits::element(slot_); }
865
866 // PRECONDITION: not an end() iterator.
867 pointer operator->() const { return &operator*(); }
868
869 // PRECONDITION: not an end() iterator.
870 iterator& operator++() {
871 ++ctrl_;
872 ++slot_;
873 skip_empty_or_deleted();
874 return *this;
875 }
876 // PRECONDITION: not an end() iterator.
877 iterator operator++(int) {
878 auto tmp = *this;
879 ++*this;
880 return tmp;
881 }
882
883#if PHMAP_BIDIRECTIONAL
884 // PRECONDITION: not a begin() iterator.
885 iterator& operator--() {
886 assert(ctrl_);
887 do {
888 --ctrl_;
889 --slot_;
890 } while (IsEmptyOrDeleted(*ctrl_));
891 return *this;
892 }
893
894 // PRECONDITION: not a begin() iterator.
895 iterator operator--(int) {
896 auto tmp = *this;
897 --*this;
898 return tmp;
899 }
900#endif
901
902 friend bool operator==(const iterator& a, const iterator& b) {
903 return a.ctrl_ == b.ctrl_;
904 }
905 friend bool operator!=(const iterator& a, const iterator& b) {
906 return !(a == b);
907 }
908
909 private:
910 iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end()
911 iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {}
912
913 void skip_empty_or_deleted() {
914 while (IsEmptyOrDeleted(*ctrl_)) {
915 // ctrl is not necessarily aligned to Group::kWidth. It is also likely
916 // to read past the space for ctrl bytes and into slots. This is ok
917 // because ctrl has sizeof() == 1 and slot has sizeof() >= 1 so there
918 // is no way to read outside the combined slot array.
919 uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted();
920 ctrl_ += shift;
921 slot_ += shift;
922 }
923 }
924
925 ctrl_t* ctrl_ = nullptr;
926 // To avoid uninitialized member warnings, put slot_ in an anonymous union.
927 // The member is not initialized on singleton and end iterators.
928 union {
929 slot_type* slot_;
930 };
931 };
932
934 {
935 friend class raw_hash_set;
936
937 public:
938 using iterator_category = typename iterator::iterator_category;
939 using value_type = typename raw_hash_set::value_type;
940 using reference = typename raw_hash_set::const_reference;
941 using pointer = typename raw_hash_set::const_pointer;
942 using difference_type = typename raw_hash_set::difference_type;
943
944 const_iterator() {}
945 // Implicit construction from iterator.
946 const_iterator(iterator i) : inner_(std::move(i)) {}
947
948 reference operator*() const { return *inner_; }
949 pointer operator->() const { return inner_.operator->(); }
950
951 const_iterator& operator++() {
952 ++inner_;
953 return *this;
954 }
955 const_iterator operator++(int) { return inner_++; }
956
957 friend bool operator==(const const_iterator& a, const const_iterator& b) {
958 return a.inner_ == b.inner_;
959 }
960 friend bool operator!=(const const_iterator& a, const const_iterator& b) {
961 return !(a == b);
962 }
963
964 private:
965 const_iterator(const ctrl_t* ctrl, const slot_type* slot)
966 : inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot)) {}
967
968 iterator inner_;
969 };
970
971 using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
972 using insert_return_type = InsertReturnType<iterator, node_type>;
973
974 raw_hash_set() noexcept(
975 std::is_nothrow_default_constructible<hasher>::value&&
976 std::is_nothrow_default_constructible<key_equal>::value&&
977 std::is_nothrow_default_constructible<allocator_type>::value) {}
978
979 explicit raw_hash_set(size_t bucket_cnt, const hasher& hashfn = hasher(),
980 const key_equal& eq = key_equal(),
981 const allocator_type& alloc = allocator_type())
982 : ctrl_(EmptyGroup()), settings_(0, hashfn, eq, alloc) {
983 if (bucket_cnt) {
984 capacity_ = NormalizeCapacity(bucket_cnt);
985 reset_growth_left();
986 initialize_slots();
987 }
988 }
989
990 raw_hash_set(size_t bucket_cnt, const hasher& hashfn,
991 const allocator_type& alloc)
992 : raw_hash_set(bucket_cnt, hashfn, key_equal(), alloc) {}
993
994 raw_hash_set(size_t bucket_cnt, const allocator_type& alloc)
995 : raw_hash_set(bucket_cnt, hasher(), key_equal(), alloc) {}
996
997 explicit raw_hash_set(const allocator_type& alloc)
998 : raw_hash_set(0, hasher(), key_equal(), alloc) {}
999
1000 template <class InputIter>
1001 raw_hash_set(InputIter first, InputIter last, size_t bucket_cnt = 0,
1002 const hasher& hashfn = hasher(), const key_equal& eq = key_equal(),
1003 const allocator_type& alloc = allocator_type())
1004 : raw_hash_set(bucket_cnt, hashfn, eq, alloc) {
1005 insert(first, last);
1006 }
1007
1008 template <class InputIter>
1009 raw_hash_set(InputIter first, InputIter last, size_t bucket_cnt,
1010 const hasher& hashfn, const allocator_type& alloc)
1011 : raw_hash_set(first, last, bucket_cnt, hashfn, key_equal(), alloc) {}
1012
1013 template <class InputIter>
1014 raw_hash_set(InputIter first, InputIter last, size_t bucket_cnt,
1015 const allocator_type& alloc)
1016 : raw_hash_set(first, last, bucket_cnt, hasher(), key_equal(), alloc) {}
1017
1018 template <class InputIter>
1019 raw_hash_set(InputIter first, InputIter last, const allocator_type& alloc)
1020 : raw_hash_set(first, last, 0, hasher(), key_equal(), alloc) {}
1021
1022 // Instead of accepting std::initializer_list<value_type> as the first
1023 // argument like std::unordered_set<value_type> does, we have two overloads
1024 // that accept std::initializer_list<T> and std::initializer_list<init_type>.
1025 // This is advantageous for performance.
1026 //
1027 // // Turns {"abc", "def"} into std::initializer_list<std::string>, then
1028 // // copies the strings into the set.
1029 // std::unordered_set<std::string> s = {"abc", "def"};
1030 //
1031 // // Turns {"abc", "def"} into std::initializer_list<const char*>, then
1032 // // copies the strings into the set.
1033 // phmap::flat_hash_set<std::string> s = {"abc", "def"};
1034 //
1035 // The same trick is used in insert().
1036 //
1037 // The enabler is necessary to prevent this constructor from triggering where
1038 // the copy constructor is meant to be called.
1039 //
1040 // phmap::flat_hash_set<int> a, b{a};
1041 //
1042 // RequiresNotInit<T> is a workaround for gcc prior to 7.1.
1043 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
1044 raw_hash_set(std::initializer_list<T> init, size_t bucket_cnt = 0,
1045 const hasher& hashfn = hasher(), const key_equal& eq = key_equal(),
1046 const allocator_type& alloc = allocator_type())
1047 : raw_hash_set(init.begin(), init.end(), bucket_cnt, hashfn, eq, alloc) {}
1048
1049 raw_hash_set(std::initializer_list<init_type> init, size_t bucket_cnt = 0,
1050 const hasher& hashfn = hasher(), const key_equal& eq = key_equal(),
1051 const allocator_type& alloc = allocator_type())
1052 : raw_hash_set(init.begin(), init.end(), bucket_cnt, hashfn, eq, alloc) {}
1053
1054 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
1055 raw_hash_set(std::initializer_list<T> init, size_t bucket_cnt,
1056 const hasher& hashfn, const allocator_type& alloc)
1057 : raw_hash_set(init, bucket_cnt, hashfn, key_equal(), alloc) {}
1058
1059 raw_hash_set(std::initializer_list<init_type> init, size_t bucket_cnt,
1060 const hasher& hashfn, const allocator_type& alloc)
1061 : raw_hash_set(init, bucket_cnt, hashfn, key_equal(), alloc) {}
1062
1063 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
1064 raw_hash_set(std::initializer_list<T> init, size_t bucket_cnt,
1065 const allocator_type& alloc)
1066 : raw_hash_set(init, bucket_cnt, hasher(), key_equal(), alloc) {}
1067
1068 raw_hash_set(std::initializer_list<init_type> init, size_t bucket_cnt,
1069 const allocator_type& alloc)
1070 : raw_hash_set(init, bucket_cnt, hasher(), key_equal(), alloc) {}
1071
1072 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
1073 raw_hash_set(std::initializer_list<T> init, const allocator_type& alloc)
1074 : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {}
1075
1076 raw_hash_set(std::initializer_list<init_type> init,
1077 const allocator_type& alloc)
1078 : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {}
1079
1080 raw_hash_set(const raw_hash_set& that)
1081 : raw_hash_set(that, AllocTraits::select_on_container_copy_construction(
1082 that.alloc_ref())) {}
1083
1084 raw_hash_set(const raw_hash_set& that, const allocator_type& a)
1085 : raw_hash_set(0, that.hash_ref(), that.eq_ref(), a) {
1086 reserve(that.size());
1087 // Because the table is guaranteed to be empty, we can do something faster
1088 // than a full `insert`.
1089 for (const auto& v : that) {
1090 const size_t hashval = PolicyTraits::apply(HashElement{hash_ref()}, v);
1091 auto target = find_first_non_full(hashval);
1092 set_ctrl(target.offset, H2(hashval));
1093 emplace_at(target.offset, v);
1094 infoz_.RecordInsert(hashval, target.probe_length);
1095 }
1096 size_ = that.size();
1097 growth_left() -= that.size();
1098 }
1099
1100 raw_hash_set(raw_hash_set&& that) noexcept(
1101 std::is_nothrow_copy_constructible<hasher>::value&&
1102 std::is_nothrow_copy_constructible<key_equal>::value&&
1103 std::is_nothrow_copy_constructible<allocator_type>::value)
1104 : ctrl_(phmap::exchange(that.ctrl_, EmptyGroup())),
1105 slots_(phmap::exchange(that.slots_, nullptr)),
1106 size_(phmap::exchange(that.size_, 0)),
1107 capacity_(phmap::exchange(that.capacity_, 0)),
1108 infoz_(phmap::exchange(that.infoz_, HashtablezInfoHandle())),
1109 // Hash, equality and allocator are copied instead of moved because
1110 // `that` must be left valid. If Hash is std::function<Key>, moving it
1111 // would create a nullptr functor that cannot be called.
1112 settings_(that.settings_) {
1113 // growth_left was copied above, reset the one from `that`.
1114 that.growth_left() = 0;
1115 }
1116
1117 raw_hash_set(raw_hash_set&& that, const allocator_type& a)
1118 : ctrl_(EmptyGroup()),
1119 slots_(nullptr),
1120 size_(0),
1121 capacity_(0),
1122 settings_(0, that.hash_ref(), that.eq_ref(), a) {
1123 if (a == that.alloc_ref()) {
1124 std::swap(ctrl_, that.ctrl_);
1125 std::swap(slots_, that.slots_);
1126 std::swap(size_, that.size_);
1127 std::swap(capacity_, that.capacity_);
1128 std::swap(growth_left(), that.growth_left());
1129 std::swap(infoz_, that.infoz_);
1130 } else {
1131 reserve(that.size());
1132 // Note: this will copy elements of dense_set and unordered_set instead of
1133 // moving them. This can be fixed if it ever becomes an issue.
1134 for (auto& elem : that) insert(std::move(elem));
1135 }
1136 }
1137
1138 raw_hash_set& operator=(const raw_hash_set& that) {
1139 raw_hash_set tmp(that,
1140 AllocTraits::propagate_on_container_copy_assignment::value
1141 ? that.alloc_ref()
1142 : alloc_ref());
1143 swap(tmp);
1144 return *this;
1145 }
1146
1147 raw_hash_set& operator=(raw_hash_set&& that) noexcept(
1149 std::is_nothrow_move_assignable<hasher>::value&&
1150 std::is_nothrow_move_assignable<key_equal>::value) {
1151 // TODO(sbenza): We should only use the operations from the noexcept clause
1152 // to make sure we actually adhere to that contract.
1153 return move_assign(
1154 std::move(that),
1155 typename AllocTraits::propagate_on_container_move_assignment());
1156 }
1157
1158 ~raw_hash_set() { destroy_slots(); }
1159
1160 iterator begin() {
1161 auto it = iterator_at(0);
1162 it.skip_empty_or_deleted();
1163 return it;
1164 }
1165 iterator end()
1166 {
1167#if PHMAP_BIDIRECTIONAL
1168 return iterator_at(capacity_);
1169#else
1170 return {ctrl_ + capacity_};
1171#endif
1172 }
1173
1174 const_iterator begin() const {
1175 return const_cast<raw_hash_set*>(this)->begin();
1176 }
1177 const_iterator end() const { return const_cast<raw_hash_set*>(this)->end(); }
1178 const_iterator cbegin() const { return begin(); }
1179 const_iterator cend() const { return end(); }
1180
1181 bool empty() const { return !size(); }
1182 size_t size() const { return size_; }
1183 size_t capacity() const { return capacity_; }
1184 size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
1185
1186 PHMAP_ATTRIBUTE_REINITIALIZES void clear() {
1187 // Iterating over this container is O(bucket_count()). When bucket_count()
1188 // is much greater than size(), iteration becomes prohibitively expensive.
1189 // For clear() it is more important to reuse the allocated array when the
1190 // container is small because allocation takes comparatively long time
1191 // compared to destruction of the elements of the container. So we pick the
1192 // largest bucket_count() threshold for which iteration is still fast and
1193 // past that we simply deallocate the array.
1194 if (empty())
1195 return;
1196 if (capacity_ > 127) {
1197 destroy_slots();
1198 } else if (capacity_) {
1199 for (size_t i = 0; i != capacity_; ++i) {
1200 if (IsFull(ctrl_[i])) {
1201 PolicyTraits::destroy(&alloc_ref(), slots_ + i);
1202 }
1203 }
1204 size_ = 0;
1205 reset_ctrl();
1206 reset_growth_left();
1207 }
1208 assert(empty());
1209 infoz_.RecordStorageChanged(0, capacity_);
1210 }
1211
1212 // This overload kicks in when the argument is an rvalue of insertable and
1213 // decomposable type other than init_type.
1214 //
1215 // flat_hash_map<std::string, int> m;
1216 // m.insert(std::make_pair("abc", 42));
1217 template <class T, RequiresInsertable<T> = 0,
1218 typename std::enable_if<IsDecomposable<T>::value, int>::type = 0,
1219 T* = nullptr>
1220 std::pair<iterator, bool> insert(T&& value) {
1221 return emplace(std::forward<T>(value));
1222 }
1223
1224 // This overload kicks in when the argument is a bitfield or an lvalue of
1225 // insertable and decomposable type.
1226 //
1227 // union { int n : 1; };
1228 // flat_hash_set<int> s;
1229 // s.insert(n);
1230 //
1231 // flat_hash_set<std::string> s;
1232 // const char* p = "hello";
1233 // s.insert(p);
1234 //
1235 // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
1236 // RequiresInsertable<T> with RequiresInsertable<const T&>.
1237 // We are hitting this bug: https://godbolt.org/g/1Vht4f.
1238 template <class T, RequiresInsertable<T> = 0,
1239 typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
1240 std::pair<iterator, bool> insert(const T& value) {
1241 return emplace(value);
1242 }
1243
1244 // This overload kicks in when the argument is an rvalue of init_type. Its
1245 // purpose is to handle brace-init-list arguments.
1246 //
1247 // flat_hash_set<std::string, int> s;
1248 // s.insert({"abc", 42});
1249 std::pair<iterator, bool> insert(init_type&& value) {
1250 return emplace(std::move(value));
1251 }
1252
1253 template <class T, RequiresInsertable<T> = 0,
1254 typename std::enable_if<IsDecomposable<T>::value, int>::type = 0,
1255 T* = nullptr>
1256 iterator insert(const_iterator, T&& value) {
1257 return insert(std::forward<T>(value)).first;
1258 }
1259
1260 // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
1261 // RequiresInsertable<T> with RequiresInsertable<const T&>.
1262 // We are hitting this bug: https://godbolt.org/g/1Vht4f.
1263 template <class T, RequiresInsertable<T> = 0,
1264 typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
1265 iterator insert(const_iterator, const T& value) {
1266 return insert(value).first;
1267 }
1268
1269 iterator insert(const_iterator, init_type&& value) {
1270 return insert(std::move(value)).first;
1271 }
1272
1273 template <typename It>
1274 using IsRandomAccess = std::is_same<typename std::iterator_traits<It>::iterator_category,
1275 std::random_access_iterator_tag>;
1276
1277
1278 template<typename T>
1280 {
1281 private:
1282 using yes = std::true_type;
1283 using no = std::false_type;
1284
1285 template<typename U> static auto test(int) -> decltype(std::declval<U>() - std::declval<U>() == 1, yes());
1286 template<typename> static no test(...);
1287
1288 public:
1289 static constexpr bool value = std::is_same<decltype(test<T>(0)), yes>::value;
1290 };
1291
1292 template <class InputIt, typename phmap::enable_if_t<has_difference_operator<InputIt>::value, int> = 0>
1293 void insert(InputIt first, InputIt last) {
1294 this->reserve(this->size() + (last - first));
1295 for (; first != last; ++first)
1296 emplace(*first);
1297 }
1298
1299 template <class InputIt, typename phmap::enable_if_t<!has_difference_operator<InputIt>::value, int> = 0>
1300 void insert(InputIt first, InputIt last) {
1301 for (; first != last; ++first)
1302 emplace(*first);
1303 }
1304
1305 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<const T&> = 0>
1306 void insert(std::initializer_list<T> ilist) {
1307 insert(ilist.begin(), ilist.end());
1308 }
1309
1310 void insert(std::initializer_list<init_type> ilist) {
1311 insert(ilist.begin(), ilist.end());
1312 }
1313
1314 insert_return_type insert(node_type&& node) {
1315 if (!node) return {end(), false, node_type()};
1316 const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node));
1317 auto res = PolicyTraits::apply(
1318 InsertSlot<false>{*this, std::move(*CommonAccess::GetSlot(node))},
1319 elem);
1320 if (res.second) {
1321 CommonAccess::Reset(&node);
1322 return {res.first, true, node_type()};
1323 } else {
1324 return {res.first, false, std::move(node)};
1325 }
1326 }
1327
1328 insert_return_type insert(node_type&& node, size_t hashval) {
1329 if (!node) return {end(), false, node_type()};
1330 const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node));
1331 auto res = PolicyTraits::apply(
1332 InsertSlotWithHash<false>{*this, std::move(*CommonAccess::GetSlot(node)), hashval},
1333 elem);
1334 if (res.second) {
1335 CommonAccess::Reset(&node);
1336 return {res.first, true, node_type()};
1337 } else {
1338 return {res.first, false, std::move(node)};
1339 }
1340 }
1341
1342 iterator insert(const_iterator, node_type&& node) {
1343 return insert(std::move(node)).first;
1344 }
1345
1346 // This overload kicks in if we can deduce the key from args. This enables us
1347 // to avoid constructing value_type if an entry with the same key already
1348 // exists.
1349 //
1350 // For example:
1351 //
1352 // flat_hash_map<std::string, std::string> m = {{"abc", "def"}};
1353 // // Creates no std::string copies and makes no heap allocations.
1354 // m.emplace("abc", "xyz");
1355 template <class... Args, typename std::enable_if<
1356 IsDecomposable<Args...>::value, int>::type = 0>
1357 std::pair<iterator, bool> emplace(Args&&... args) {
1358 return PolicyTraits::apply(EmplaceDecomposable{*this},
1359 std::forward<Args>(args)...);
1360 }
1361
1362 // This overload kicks in if we cannot deduce the key from args. It constructs
1363 // value_type unconditionally and then either moves it into the table or
1364 // destroys.
1365 template <class... Args, typename std::enable_if<
1366 !IsDecomposable<Args...>::value, int>::type = 0>
1367 std::pair<iterator, bool> emplace(Args&&... args) {
1368 typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type
1369 raw;
1370 slot_type* slot = reinterpret_cast<slot_type*>(&raw);
1371
1372 PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
1373 const auto& elem = PolicyTraits::element(slot);
1374 return PolicyTraits::apply(InsertSlot<true>{*this, std::move(*slot)}, elem);
1375 }
1376
1377 template <class... Args>
1378 iterator emplace_hint(const_iterator, Args&&... args) {
1379 return emplace(std::forward<Args>(args)...).first;
1380 }
1381
1382 // Extension API: support for lazy emplace.
1383 //
1384 // Looks up key in the table. If found, returns the iterator to the element.
1385 // Otherwise calls f with one argument of type raw_hash_set::constructor. f
1386 // MUST call raw_hash_set::constructor with arguments as if a
1387 // raw_hash_set::value_type is constructed, otherwise the behavior is
1388 // undefined.
1389 //
1390 // For example:
1391 //
1392 // std::unordered_set<ArenaString> s;
1393 // // Makes ArenaStr even if "abc" is in the map.
1394 // s.insert(ArenaString(&arena, "abc"));
1395 //
1396 // flat_hash_set<ArenaStr> s;
1397 // // Makes ArenaStr only if "abc" is not in the map.
1398 // s.lazy_emplace("abc", [&](const constructor& ctor) {
1399 // ctor(&arena, "abc");
1400 // });
1401 //
1402 // WARNING: This API is currently experimental. If there is a way to implement
1403 // the same thing with the rest of the API, prefer that.
1405 {
1406 friend class raw_hash_set;
1407
1408 public:
1409 template <class... Args>
1410 void operator()(Args&&... args) const {
1411 assert(*slot_);
1412 PolicyTraits::construct(alloc_, *slot_, std::forward<Args>(args)...);
1413 *slot_ = nullptr;
1414 }
1415
1416 private:
1417 constructor(allocator_type* a, slot_type** slot) : alloc_(a), slot_(slot) {}
1418
1419 allocator_type* alloc_;
1420 slot_type** slot_;
1421 };
1422
1423 template <class K = key_type, class F>
1424 iterator lazy_emplace(const key_arg<K>& key, F&& f) {
1425 auto res = find_or_prepare_insert(key);
1426 if (res.second) {
1427 lazy_emplace_at(res.first, std::forward<F>(f));
1428 }
1429 return iterator_at(res.first);
1430 }
1431
1432 template <class K = key_type, class F>
1433 iterator lazy_emplace_with_hash(const key_arg<K>& key, size_t &hashval, F&& f) {
1434 auto res = find_or_prepare_insert(key, hashval);
1435 if (res.second) {
1436 lazy_emplace_at(res.first, std::forward<F>(f));
1437 }
1438 return iterator_at(res.first);
1439 }
1440
1441 template <class K = key_type, class F>
1442 void lazy_emplace_at(size_t& idx, F&& f) {
1443 slot_type* slot = slots_ + idx;
1444 std::forward<F>(f)(constructor(&alloc_ref(), &slot));
1445 assert(!slot);
1446 }
1447
1448
1449 // Extension API: support for heterogeneous keys.
1450 //
1451 // std::unordered_set<std::string> s;
1452 // // Turns "abc" into std::string.
1453 // s.erase("abc");
1454 //
1455 // flat_hash_set<std::string> s;
1456 // // Uses "abc" directly without copying it into std::string.
1457 // s.erase("abc");
1458 template <class K = key_type>
1459 size_type erase(const key_arg<K>& key) {
1460 auto it = find(key);
1461 if (it == end()) return 0;
1462 _erase(it);
1463 return 1;
1464 }
1465
1466
1467 iterator erase(const_iterator cit) { return erase(cit.inner_); }
1468
1469 // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`,
1470 // this method returns void to reduce algorithmic complexity to O(1). In
1471 // order to erase while iterating across a map, use the following idiom (which
1472 // also works for standard containers):
1473 //
1474 // for (auto it = m.begin(), end = m.end(); it != end;) {
1475 // if (<pred>) {
1476 // m._erase(it++);
1477 // } else {
1478 // ++it;
1479 // }
1480 // }
1481 void _erase(iterator it) {
1482 assert(it != end());
1483 PolicyTraits::destroy(&alloc_ref(), it.slot_);
1484 erase_meta_only(it);
1485 }
1486 void _erase(const_iterator cit) { _erase(cit.inner_); }
1487
1488 // This overload is necessary because otherwise erase<K>(const K&) would be
1489 // a better match if non-const iterator is passed as an argument.
1490 iterator erase(iterator it) {
1491 auto res = it;
1492 ++res;
1493 _erase(it);
1494 return res;
1495 }
1496
1497 iterator erase(const_iterator first, const_iterator last) {
1498 while (first != last) {
1499 _erase(first++);
1500 }
1501 return last.inner_;
1502 }
1503
1504 // Moves elements from `src` into `this`.
1505 // If the element already exists in `this`, it is left unmodified in `src`.
1506 template <typename H, typename E>
1507 void merge(raw_hash_set<Policy, H, E, Alloc>& src) { // NOLINT
1508 assert(this != &src);
1509 for (auto it = src.begin(), e = src.end(); it != e; ++it) {
1510 if (PolicyTraits::apply(InsertSlot<false>{*this, std::move(*it.slot_)},
1511 PolicyTraits::element(it.slot_))
1512 .second) {
1513 src.erase_meta_only(it);
1514 }
1515 }
1516 }
1517
1518 template <typename H, typename E>
1519 void merge(raw_hash_set<Policy, H, E, Alloc>&& src) {
1520 merge(src);
1521 }
1522
1523 node_type extract(const_iterator position) {
1524 auto node =
1525 CommonAccess::Make<node_type>(alloc_ref(), position.inner_.slot_);
1526 erase_meta_only(position);
1527 return node;
1528 }
1529
1530 template <
1531 class K = key_type,
1532 typename std::enable_if<!std::is_same<K, iterator>::value, int>::type = 0>
1533 node_type extract(const key_arg<K>& key) {
1534 auto it = find(key);
1535 return it == end() ? node_type() : extract(const_iterator{it});
1536 }
1537
1538 void swap(raw_hash_set& that) noexcept(
1539 IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() &&
1540 (!AllocTraits::propagate_on_container_swap::value ||
1541 IsNoThrowSwappable<allocator_type>())) {
1542 using std::swap;
1543 swap(ctrl_, that.ctrl_);
1544 swap(slots_, that.slots_);
1545 swap(size_, that.size_);
1546 swap(capacity_, that.capacity_);
1547 swap(growth_left(), that.growth_left());
1548 swap(hash_ref(), that.hash_ref());
1549 swap(eq_ref(), that.eq_ref());
1550 swap(infoz_, that.infoz_);
1551 if (AllocTraits::propagate_on_container_swap::value) {
1552 swap(alloc_ref(), that.alloc_ref());
1553 } else {
1554 // If the allocators do not compare equal it is officially undefined
1555 // behavior. We choose to do nothing.
1556 }
1557 }
1558
1559#ifndef PHMAP_NON_DETERMINISTIC
1560 template<typename OutputArchive>
1561 bool dump(OutputArchive&) const;
1562
1563 template<typename InputArchive>
1564 bool load(InputArchive&);
1565#endif
1566
1567 void rehash(size_t n) {
1568 if (n == 0 && capacity_ == 0) return;
1569 if (n == 0 && size_ == 0) {
1570 destroy_slots();
1571 infoz_.RecordStorageChanged(0, 0);
1572 return;
1573 }
1574 // bitor is a faster way of doing `max` here. We will round up to the next
1575 // power-of-2-minus-1, so bitor is good enough.
1576 auto m = NormalizeCapacity((std::max)(n, size()));
1577 // n == 0 unconditionally rehashes as per the standard.
1578 if (n == 0 || m > capacity_) {
1579 resize(m);
1580 }
1581 }
1582
1583 void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); }
1584
1585 // Extension API: support for heterogeneous keys.
1586 //
1587 // std::unordered_set<std::string> s;
1588 // // Turns "abc" into std::string.
1589 // s.count("abc");
1590 //
1591 // ch_set<std::string> s;
1592 // // Uses "abc" directly without copying it into std::string.
1593 // s.count("abc");
1594 template <class K = key_type>
1595 size_t count(const key_arg<K>& key) const {
1596 return find(key) == end() ? size_t(0) : size_t(1);
1597 }
1598
1599 // Issues CPU prefetch instructions for the memory needed to find or insert
1600 // a key. Like all lookup functions, this support heterogeneous keys.
1601 //
1602 // NOTE: This is a very low level operation and should not be used without
1603 // specific benchmarks indicating its importance.
1604 void prefetch_hash(size_t hashval) const {
1605 (void)hashval;
1606#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
1607 auto seq = probe(hashval);
1608 _mm_prefetch((const char *)(ctrl_ + seq.offset()), _MM_HINT_NTA);
1609 _mm_prefetch((const char *)(slots_ + seq.offset()), _MM_HINT_NTA);
1610#elif defined(__GNUC__)
1611 auto seq = probe(hashval);
1612 __builtin_prefetch(static_cast<const void*>(ctrl_ + seq.offset()));
1613 __builtin_prefetch(static_cast<const void*>(slots_ + seq.offset()));
1614#endif // __GNUC__
1615 }
1616
1617 template <class K = key_type>
1618 void prefetch(const key_arg<K>& key) const {
1619 prefetch_hash(this->hash(key));
1620 }
1621
1622 // The API of find() has two extensions.
1623 //
1624 // 1. The hash can be passed by the user. It must be equal to the hash of the
1625 // key.
1626 //
1627 // 2. The type of the key argument doesn't have to be key_type. This is so
1628 // called heterogeneous key support.
1629 template <class K = key_type>
1630 iterator find(const key_arg<K>& key, size_t hashval) {
1631 auto seq = probe(hashval);
1632 while (true) {
1633 Group g{ctrl_ + seq.offset()};
1634 for (int i : g.Match((h2_t)H2(hashval))) {
1635 if (PHMAP_PREDICT_TRUE(PolicyTraits::apply(
1636 EqualElement<K>{key, eq_ref()},
1637 PolicyTraits::element(slots_ + seq.offset((size_t)i)))))
1638 return iterator_at(seq.offset((size_t)i));
1639 }
1640 if (PHMAP_PREDICT_TRUE(g.MatchEmpty()))
1641 return end();
1642 seq.next();
1643 }
1644 }
1645 template <class K = key_type>
1646 iterator find(const key_arg<K>& key) {
1647 return find(key, this->hash(key));
1648 }
1649
1650 template <class K = key_type>
1651 const_iterator find(const key_arg<K>& key, size_t hashval) const {
1652 return const_cast<raw_hash_set*>(this)->find(key, hashval);
1653 }
1654 template <class K = key_type>
1655 const_iterator find(const key_arg<K>& key) const {
1656 return find(key, this->hash(key));
1657 }
1658
1659 template <class K = key_type>
1660 bool contains(const key_arg<K>& key) const {
1661 return find(key) != end();
1662 }
1663
1664 template <class K = key_type>
1665 bool contains(const key_arg<K>& key, size_t hashval) const {
1666 return find(key, hashval) != end();
1667 }
1668
1669 template <class K = key_type>
1670 std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
1671 auto it = find(key);
1672 if (it != end()) return {it, std::next(it)};
1673 return {it, it};
1674 }
1675 template <class K = key_type>
1676 std::pair<const_iterator, const_iterator> equal_range(
1677 const key_arg<K>& key) const {
1678 auto it = find(key);
1679 if (it != end()) return {it, std::next(it)};
1680 return {it, it};
1681 }
1682
1683 size_t bucket_count() const { return capacity_; }
1684 float load_factor() const {
1685 return capacity_ ? static_cast<double>(size()) / capacity_ : 0.0;
1686 }
1687 float max_load_factor() const { return 1.0f; }
1688 void max_load_factor(float) {
1689 // Does nothing.
1690 }
1691
1692 hasher hash_function() const { return hash_ref(); } // warning: doesn't match internal hash - use hash() member function
1693 key_equal key_eq() const { return eq_ref(); }
1694 allocator_type get_allocator() const { return alloc_ref(); }
1695
1696 friend bool operator==(const raw_hash_set& a, const raw_hash_set& b) {
1697 if (a.size() != b.size()) return false;
1698 const raw_hash_set* outer = &a;
1699 const raw_hash_set* inner = &b;
1700 if (outer->capacity() > inner->capacity())
1701 std::swap(outer, inner);
1702 for (const value_type& elem : *outer)
1703 if (!inner->has_element(elem)) return false;
1704 return true;
1705 }
1706
1707 friend bool operator!=(const raw_hash_set& a, const raw_hash_set& b) {
1708 return !(a == b);
1709 }
1710
1711 friend void swap(raw_hash_set& a,
1712 raw_hash_set& b) noexcept(noexcept(a.swap(b))) {
1713 a.swap(b);
1714 }
1715
1716 template <class K>
1717 size_t hash(const K& key) const {
1718 return HashElement{hash_ref()}(key);
1719 }
1720
1721private:
1722 template <class Container, typename Enabler>
1724
1725 struct FindElement
1726 {
1727 template <class K, class... Args>
1728 const_iterator operator()(const K& key, Args&&...) const {
1729 return s.find(key);
1730 }
1731 const raw_hash_set& s;
1732 };
1733
1734 struct HashElement
1735 {
1736 template <class K, class... Args>
1737 size_t operator()(const K& key, Args&&...) const {
1738 return phmap_mix<sizeof(size_t)>()(h(key));
1739 }
1740 const hasher& h;
1741 };
1742
1743 template <class K1>
1744 struct EqualElement
1745 {
1746 template <class K2, class... Args>
1747 bool operator()(const K2& lhs, Args&&...) const {
1748 return eq(lhs, rhs);
1749 }
1750 const K1& rhs;
1751 const key_equal& eq;
1752 };
1753
1754 template <class K, class... Args>
1755 std::pair<iterator, bool> emplace_decomposable(const K& key, size_t hashval,
1756 Args&&... args)
1757 {
1758 auto res = find_or_prepare_insert(key, hashval);
1759 if (res.second) {
1760 emplace_at(res.first, std::forward<Args>(args)...);
1761 }
1762 return {iterator_at(res.first), res.second};
1763 }
1764
1765 struct EmplaceDecomposable
1766 {
1767 template <class K, class... Args>
1768 std::pair<iterator, bool> operator()(const K& key, Args&&... args) const {
1769 return s.emplace_decomposable(key, s.hash(key), std::forward<Args>(args)...);
1770 }
1771 raw_hash_set& s;
1772 };
1773
1774 template <bool do_destroy>
1775 struct InsertSlot
1776 {
1777 template <class K, class... Args>
1778 std::pair<iterator, bool> operator()(const K& key, Args&&...) && {
1779 auto res = s.find_or_prepare_insert(key);
1780 if (res.second) {
1781 PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot);
1782 } else if (do_destroy) {
1783 PolicyTraits::destroy(&s.alloc_ref(), &slot);
1784 }
1785 return {s.iterator_at(res.first), res.second};
1786 }
1787 raw_hash_set& s;
1788 // Constructed slot. Either moved into place or destroyed.
1789 slot_type&& slot;
1790 };
1791
1792 template <bool do_destroy>
1793 struct InsertSlotWithHash
1794 {
1795 template <class K, class... Args>
1796 std::pair<iterator, bool> operator()(const K& key, Args&&...) && {
1797 auto res = s.find_or_prepare_insert(key, hashval);
1798 if (res.second) {
1799 PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot);
1800 } else if (do_destroy) {
1801 PolicyTraits::destroy(&s.alloc_ref(), &slot);
1802 }
1803 return {s.iterator_at(res.first), res.second};
1804 }
1805 raw_hash_set& s;
1806 // Constructed slot. Either moved into place or destroyed.
1807 slot_type&& slot;
1808 size_t &hashval;
1809 };
1810
1811 // "erases" the object from the container, except that it doesn't actually
1812 // destroy the object. It only updates all the metadata of the class.
1813 // This can be used in conjunction with Policy::transfer to move the object to
1814 // another place.
1815 void erase_meta_only(const_iterator it) {
1816 assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator");
1817 --size_;
1818 const size_t index = (size_t)(it.inner_.ctrl_ - ctrl_);
1819 const size_t index_before = (index - Group::kWidth) & capacity_;
1820 const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty();
1821 const auto empty_before = Group(ctrl_ + index_before).MatchEmpty();
1822
1823 // We count how many consecutive non empties we have to the right and to the
1824 // left of `it`. If the sum is >= kWidth then there is at least one probe
1825 // window that might have seen a full group.
1826 bool was_never_full =
1827 empty_before && empty_after &&
1828 static_cast<size_t>(empty_after.TrailingZeros() +
1829 empty_before.LeadingZeros()) < Group::kWidth;
1830
1831 set_ctrl(index, was_never_full ? kEmpty : kDeleted);
1832 growth_left() += was_never_full;
1833 infoz_.RecordErase();
1834 }
1835
1836 void initialize_slots() {
1837 assert(capacity_);
1838 if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value &&
1839 slots_ == nullptr) {
1840 infoz_ = Sample();
1841 }
1842
1843 auto layout = MakeLayout(capacity_);
1844 char* mem = static_cast<char*>(
1845 Allocate<Layout::Alignment()>(&alloc_ref(), layout.AllocSize()));
1846 ctrl_ = reinterpret_cast<ctrl_t*>(layout.template Pointer<0>(mem));
1847 slots_ = layout.template Pointer<1>(mem);
1848 reset_ctrl();
1849 reset_growth_left();
1850 infoz_.RecordStorageChanged(size_, capacity_);
1851 }
1852
1853 void destroy_slots() {
1854 if (!capacity_) return;
1855 for (size_t i = 0; i != capacity_; ++i) {
1856 if (IsFull(ctrl_[i])) {
1857 PolicyTraits::destroy(&alloc_ref(), slots_ + i);
1858 }
1859 }
1860 auto layout = MakeLayout(capacity_);
1861 // Unpoison before returning the memory to the allocator.
1862 SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
1863 Deallocate<Layout::Alignment()>(&alloc_ref(), ctrl_, layout.AllocSize());
1864 ctrl_ = EmptyGroup();
1865 slots_ = nullptr;
1866 size_ = 0;
1867 capacity_ = 0;
1868 growth_left() = 0;
1869 }
1870
1871 void resize(size_t new_capacity) {
1872 assert(IsValidCapacity(new_capacity));
1873 auto* old_ctrl = ctrl_;
1874 auto* old_slots = slots_;
1875 const size_t old_capacity = capacity_;
1876 capacity_ = new_capacity;
1877 initialize_slots();
1878
1879 for (size_t i = 0; i != old_capacity; ++i) {
1880 if (IsFull(old_ctrl[i])) {
1881 size_t hashval = PolicyTraits::apply(HashElement{hash_ref()},
1882 PolicyTraits::element(old_slots + i));
1883 auto target = find_first_non_full(hashval);
1884 size_t new_i = target.offset;
1885 set_ctrl(new_i, H2(hashval));
1886 PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i);
1887 }
1888 }
1889 if (old_capacity) {
1890 SanitizerUnpoisonMemoryRegion(old_slots,
1891 sizeof(slot_type) * old_capacity);
1892 auto layout = MakeLayout(old_capacity);
1893 Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl,
1894 layout.AllocSize());
1895 }
1896 }
1897
1898 void drop_deletes_without_resize() PHMAP_ATTRIBUTE_NOINLINE {
1899 assert(IsValidCapacity(capacity_));
1900 assert(!is_small());
1901 // Algorithm:
1902 // - mark all DELETED slots as EMPTY
1903 // - mark all FULL slots as DELETED
1904 // - for each slot marked as DELETED
1905 // hash = Hash(element)
1906 // target = find_first_non_full(hash)
1907 // if target is in the same group
1908 // mark slot as FULL
1909 // else if target is EMPTY
1910 // transfer element to target
1911 // mark slot as EMPTY
1912 // mark target as FULL
1913 // else if target is DELETED
1914 // swap current element with target element
1915 // mark target as FULL
1916 // repeat procedure for current slot with moved from element (target)
1917 ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_);
1918 typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type
1919 raw;
1920 slot_type* slot = reinterpret_cast<slot_type*>(&raw);
1921 for (size_t i = 0; i != capacity_; ++i) {
1922 if (!IsDeleted(ctrl_[i])) continue;
1923 size_t hashval = PolicyTraits::apply(HashElement{hash_ref()},
1924 PolicyTraits::element(slots_ + i));
1925 auto target = find_first_non_full(hashval);
1926 size_t new_i = target.offset;
1927
1928 // Verify if the old and new i fall within the same group wrt the hashval.
1929 // If they do, we don't need to move the object as it falls already in the
1930 // best probe we can.
1931 const auto probe_index = [&](size_t pos) {
1932 return ((pos - probe(hashval).offset()) & capacity_) / Group::kWidth;
1933 };
1934
1935 // Element doesn't move.
1936 if (PHMAP_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
1937 set_ctrl(i, H2(hashval));
1938 continue;
1939 }
1940 if (IsEmpty(ctrl_[new_i])) {
1941 // Transfer element to the empty spot.
1942 // set_ctrl poisons/unpoisons the slots so we have to call it at the
1943 // right time.
1944 set_ctrl(new_i, H2(hashval));
1945 PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i);
1946 set_ctrl(i, kEmpty);
1947 } else {
1948 assert(IsDeleted(ctrl_[new_i]));
1949 set_ctrl(new_i, H2(hashval));
1950 // Until we are done rehashing, DELETED marks previously FULL slots.
1951 // Swap i and new_i elements.
1952 PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i);
1953 PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i);
1954 PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot);
1955 --i; // repeat
1956 }
1957 }
1958 reset_growth_left();
1959 }
1960
1961 void rehash_and_grow_if_necessary() {
1962 if (capacity_ == 0) {
1963 resize(1);
1964 } else if (size() <= CapacityToGrowth(capacity()) / 2) {
1965 // Squash DELETED without growing if there is enough capacity.
1966 drop_deletes_without_resize();
1967 } else {
1968 // Otherwise grow the container.
1969 resize(capacity_ * 2 + 1);
1970 }
1971 }
1972
1973 bool has_element(const value_type& elem, size_t hashval) const {
1974 auto seq = probe(hashval);
1975 while (true) {
1976 Group g{ctrl_ + seq.offset()};
1977 for (int i : g.Match((h2_t)H2(hashval))) {
1978 if (PHMAP_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset((size_t)i)) ==
1979 elem))
1980 return true;
1981 }
1982 if (PHMAP_PREDICT_TRUE(g.MatchEmpty())) return false;
1983 seq.next();
1984 assert(seq.getindex() < capacity_ && "full table!");
1985 }
1986 return false;
1987 }
1988
1989 bool has_element(const value_type& elem) const {
1990 size_t hashval = PolicyTraits::apply(HashElement{hash_ref()}, elem);
1991 return has_element(elem, hashval);
1992 }
1993
1994 // Probes the raw_hash_set with the probe sequence for hash and returns the
1995 // pointer to the first empty or deleted slot.
1996 // NOTE: this function must work with tables having both kEmpty and kDelete
1997 // in one group. Such tables appears during drop_deletes_without_resize.
1998 //
1999 // This function is very useful when insertions happen and:
2000 // - the input is already a set
2001 // - there are enough slots
2002 // - the element with the hash is not in the table
2003 struct FindInfo
2004 {
2005 size_t offset;
2006 size_t probe_length;
2007 };
2008 FindInfo find_first_non_full(size_t hashval) {
2009 auto seq = probe(hashval);
2010 while (true) {
2011 Group g{ctrl_ + seq.offset()};
2012 auto mask = g.MatchEmptyOrDeleted();
2013 if (mask) {
2014 return {seq.offset((size_t)mask.LowestBitSet()), seq.getindex()};
2015 }
2016 assert(seq.getindex() < capacity_ && "full table!");
2017 seq.next();
2018 }
2019 }
2020
2021 // TODO(alkis): Optimize this assuming *this and that don't overlap.
2022 raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) {
2023 raw_hash_set tmp(std::move(that));
2024 swap(tmp);
2025 return *this;
2026 }
2027 raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) {
2028 raw_hash_set tmp(std::move(that), alloc_ref());
2029 swap(tmp);
2030 return *this;
2031 }
2032
2033protected:
2034 template <class K>
2035 std::pair<size_t, bool> find_or_prepare_insert(const K& key, size_t hashval) {
2036 auto seq = probe(hashval);
2037 while (true) {
2038 Group g{ctrl_ + seq.offset()};
2039 for (int i : g.Match((h2_t)H2(hashval))) {
2040 if (PHMAP_PREDICT_TRUE(PolicyTraits::apply(
2041 EqualElement<K>{key, eq_ref()},
2042 PolicyTraits::element(slots_ + seq.offset((size_t)i)))))
2043 return {seq.offset((size_t)i), false};
2044 }
2045 if (PHMAP_PREDICT_TRUE(g.MatchEmpty())) break;
2046 seq.next();
2047 }
2048 return {prepare_insert(hashval), true};
2049 }
2050
2051 template <class K>
2052 std::pair<size_t, bool> find_or_prepare_insert(const K& key) {
2053 return find_or_prepare_insert(key, this->hash(key));
2054 }
2055
2056 size_t prepare_insert(size_t hashval) PHMAP_ATTRIBUTE_NOINLINE {
2057 auto target = find_first_non_full(hashval);
2058 if (PHMAP_PREDICT_FALSE(growth_left() == 0 &&
2059 !IsDeleted(ctrl_[target.offset]))) {
2060 rehash_and_grow_if_necessary();
2061 target = find_first_non_full(hashval);
2062 }
2063 ++size_;
2064 growth_left() -= IsEmpty(ctrl_[target.offset]);
2065 set_ctrl(target.offset, H2(hashval));
2066 infoz_.RecordInsert(hashval, target.probe_length);
2067 return target.offset;
2068 }
2069
2070 // Constructs the value in the space pointed by the iterator. This only works
2071 // after an unsuccessful find_or_prepare_insert() and before any other
2072 // modifications happen in the raw_hash_set.
2073 //
2074 // PRECONDITION: i is an index returned from find_or_prepare_insert(k), where
2075 // k is the key decomposed from `forward<Args>(args)...`, and the bool
2076 // returned by find_or_prepare_insert(k) was true.
2077 // POSTCONDITION: *m.iterator_at(i) == value_type(forward<Args>(args)...).
2078 template <class... Args>
2079 void emplace_at(size_t i, Args&&... args) {
2080 PolicyTraits::construct(&alloc_ref(), slots_ + i,
2081 std::forward<Args>(args)...);
2082
2083 assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) ==
2084 iterator_at(i) &&
2085 "constructed value does not match the lookup key");
2086 }
2087
2088 iterator iterator_at(size_t i) { return {ctrl_ + i, slots_ + i}; }
2089 const_iterator iterator_at(size_t i) const { return {ctrl_ + i, slots_ + i}; }
2090
2091private:
2092 friend struct RawHashSetTestOnlyAccess;
2093
2094 probe_seq<Group::kWidth> probe(size_t hashval) const {
2095 return probe_seq<Group::kWidth>(H1(hashval, ctrl_), capacity_);
2096 }
2097
2098 // Reset all ctrl bytes back to kEmpty, except the sentinel.
2099 void reset_ctrl() {
2100 std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth);
2101 ctrl_[capacity_] = kSentinel;
2102 SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_);
2103 }
2104
2105 void reset_growth_left() {
2106 growth_left() = CapacityToGrowth(capacity()) - size_;
2107 }
2108
2109 // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at
2110 // the end too.
2111 void set_ctrl(size_t i, ctrl_t h) {
2112 assert(i < capacity_);
2113
2114 if (IsFull(h)) {
2115 SanitizerUnpoisonObject(slots_ + i);
2116 } else {
2117 SanitizerPoisonObject(slots_ + i);
2118 }
2119
2120 ctrl_[i] = h;
2121 ctrl_[((i - Group::kWidth) & capacity_) + 1 +
2122 ((Group::kWidth - 1) & capacity_)] = h;
2123 }
2124
2125 size_t& growth_left() { return settings_.template get<0>(); }
2126
2127 template <size_t N,
2128 template <class, class, class, class> class RefSet,
2129 class M, class P, class H, class E, class A>
2130 friend class parallel_hash_set;
2131
2132 template <size_t N,
2133 template <class, class, class, class> class RefSet,
2134 class M, class P, class H, class E, class A>
2135 friend class parallel_hash_map;
2136
2137 // The representation of the object has two modes:
2138 // - small: For capacities < kWidth-1
2139 // - large: For the rest.
2140 //
2141 // Differences:
2142 // - In small mode we are able to use the whole capacity. The extra control
2143 // bytes give us at least one "empty" control byte to stop the iteration.
2144 // This is important to make 1 a valid capacity.
2145 //
2146 // - In small mode only the first `capacity()` control bytes after the
2147 // sentinel are valid. The rest contain dummy kEmpty values that do not
2148 // represent a real slot. This is important to take into account on
2149 // find_first_non_full(), where we never try ShouldInsertBackwards() for
2150 // small tables.
2151 bool is_small() const { return capacity_ < Group::kWidth - 1; }
2152
2153 hasher& hash_ref() { return settings_.template get<1>(); }
2154 const hasher& hash_ref() const { return settings_.template get<1>(); }
2155 key_equal& eq_ref() { return settings_.template get<2>(); }
2156 const key_equal& eq_ref() const { return settings_.template get<2>(); }
2157 allocator_type& alloc_ref() { return settings_.template get<3>(); }
2158 const allocator_type& alloc_ref() const {
2159 return settings_.template get<3>();
2160 }
2161
2162 // TODO(alkis): Investigate removing some of these fields:
2163 // - ctrl/slots can be derived from each other
2164 // - size can be moved into the slot array
2165 ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t]
2166 slot_type* slots_ = nullptr; // [capacity * slot_type]
2167 size_t size_ = 0; // number of full slots
2168 size_t capacity_ = 0; // total number of slots
2169 HashtablezInfoHandle infoz_;
2170 phmap::priv::CompressedTuple<size_t /* growth_left */, hasher,
2171 key_equal, allocator_type>
2172 settings_{0, hasher{}, key_equal{}, allocator_type{}};
2173};
2174
2175
2176// --------------------------------------------------------------------------
2177// --------------------------------------------------------------------------
2178template <class Policy, class Hash, class Eq, class Alloc>
2179class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc>
2180{
2181 // P is Policy. It's passed as a template argument to support maps that have
2182 // incomplete types as values, as in unordered_map<K, IncompleteType>.
2183 // MappedReference<> may be a non-reference type.
2184 template <class P>
2185 using MappedReference = decltype(P::value(
2186 std::addressof(std::declval<typename raw_hash_map::reference>())));
2187
2188 // MappedConstReference<> may be a non-reference type.
2189 template <class P>
2190 using MappedConstReference = decltype(P::value(
2191 std::addressof(std::declval<typename raw_hash_map::const_reference>())));
2192
2193 using KeyArgImpl =
2194 KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
2195
2197
2198public:
2199 using key_type = typename Policy::key_type;
2200 using mapped_type = typename Policy::mapped_type;
2201 template <class K>
2202 using key_arg = typename KeyArgImpl::template type<K, key_type>;
2203
2204 static_assert(!std::is_reference<key_type>::value, "");
2205 // TODO(alkis): remove this assertion and verify that reference mapped_type is
2206 // supported.
2207 static_assert(!std::is_reference<mapped_type>::value, "");
2208
2209 using iterator = typename raw_hash_map::raw_hash_set::iterator;
2210 using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator;
2211
2212 raw_hash_map() {}
2213 using Base::raw_hash_set; // use raw_hash_set constructor
2214
2215 // The last two template parameters ensure that both arguments are rvalues
2216 // (lvalue arguments are handled by the overloads below). This is necessary
2217 // for supporting bitfield arguments.
2218 //
2219 // union { int n : 1; };
2220 // flat_hash_map<int, int> m;
2221 // m.insert_or_assign(n, n);
2222 template <class K = key_type, class V = mapped_type, K* = nullptr,
2223 V* = nullptr>
2224 std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v) {
2225 return insert_or_assign_impl(std::forward<K>(k), std::forward<V>(v));
2226 }
2227
2228 template <class K = key_type, class V = mapped_type, K* = nullptr>
2229 std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v) {
2230 return insert_or_assign_impl(std::forward<K>(k), v);
2231 }
2232
2233 template <class K = key_type, class V = mapped_type, V* = nullptr>
2234 std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v) {
2235 return insert_or_assign_impl(k, std::forward<V>(v));
2236 }
2237
2238 template <class K = key_type, class V = mapped_type>
2239 std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v) {
2240 return insert_or_assign_impl(k, v);
2241 }
2242
2243 template <class K = key_type, class V = mapped_type, K* = nullptr,
2244 V* = nullptr>
2245 iterator insert_or_assign(const_iterator, key_arg<K>&& k, V&& v) {
2246 return insert_or_assign(std::forward<K>(k), std::forward<V>(v)).first;
2247 }
2248
2249 template <class K = key_type, class V = mapped_type, K* = nullptr>
2250 iterator insert_or_assign(const_iterator, key_arg<K>&& k, const V& v) {
2251 return insert_or_assign(std::forward<K>(k), v).first;
2252 }
2253
2254 template <class K = key_type, class V = mapped_type, V* = nullptr>
2255 iterator insert_or_assign(const_iterator, const key_arg<K>& k, V&& v) {
2256 return insert_or_assign(k, std::forward<V>(v)).first;
2257 }
2258
2259 template <class K = key_type, class V = mapped_type>
2260 iterator insert_or_assign(const_iterator, const key_arg<K>& k, const V& v) {
2261 return insert_or_assign(k, v).first;
2262 }
2263
2264 template <class K = key_type, class... Args,
2265 typename std::enable_if<
2266 !std::is_convertible<K, const_iterator>::value, int>::type = 0,
2267 K* = nullptr>
2268 std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args) {
2269 return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...);
2270 }
2271
2272 template <class K = key_type, class... Args,
2273 typename std::enable_if<
2274 !std::is_convertible<K, const_iterator>::value, int>::type = 0>
2275 std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args) {
2276 return try_emplace_impl(k, std::forward<Args>(args)...);
2277 }
2278
2279 template <class K = key_type, class... Args, K* = nullptr>
2280 iterator try_emplace(const_iterator, key_arg<K>&& k, Args&&... args) {
2281 return try_emplace(std::forward<K>(k), std::forward<Args>(args)...).first;
2282 }
2283
2284 template <class K = key_type, class... Args>
2285 iterator try_emplace(const_iterator, const key_arg<K>& k, Args&&... args) {
2286 return try_emplace(k, std::forward<Args>(args)...).first;
2287 }
2288
2289 template <class K = key_type, class P = Policy>
2290 MappedReference<P> at(const key_arg<K>& key) {
2291 auto it = this->find(key);
2292 if (it == this->end())
2293 phmap::base_internal::ThrowStdOutOfRange("phmap at(): lookup non-existent key");
2294 return Policy::value(&*it);
2295 }
2296
2297 template <class K = key_type, class P = Policy>
2298 MappedConstReference<P> at(const key_arg<K>& key) const {
2299 auto it = this->find(key);
2300 if (it == this->end())
2301 phmap::base_internal::ThrowStdOutOfRange("phmap at(): lookup non-existent key");
2302 return Policy::value(&*it);
2303 }
2304
2305 template <class K = key_type, class P = Policy, K* = nullptr>
2306 MappedReference<P> operator[](key_arg<K>&& key) {
2307 return Policy::value(&*try_emplace(std::forward<K>(key)).first);
2308 }
2309
2310 template <class K = key_type, class P = Policy>
2311 MappedReference<P> operator[](const key_arg<K>& key) {
2312 return Policy::value(&*try_emplace(key).first);
2313 }
2314
2315private:
2316 template <class K, class V>
2317 std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
2318 auto res = this->find_or_prepare_insert(k);
2319 if (res.second)
2320 this->emplace_at(res.first, std::forward<K>(k), std::forward<V>(v));
2321 else
2322 Policy::value(&*this->iterator_at(res.first)) = std::forward<V>(v);
2323 return {this->iterator_at(res.first), res.second};
2324 }
2325
2326 template <class K = key_type, class... Args>
2327 std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) {
2328 auto res = this->find_or_prepare_insert(k);
2329 if (res.second)
2330 this->emplace_at(res.first, std::piecewise_construct,
2331 std::forward_as_tuple(std::forward<K>(k)),
2332 std::forward_as_tuple(std::forward<Args>(args)...));
2333 return {this->iterator_at(res.first), res.second};
2334 }
2335};
2336
2337// ----------------------------------------------------------------------------
2338// ----------------------------------------------------------------------------
2339// Returns "random" seed.
2340inline size_t RandomSeed()
2341{
2342#if PHMAP_HAVE_THREAD_LOCAL
2343 static thread_local size_t counter = 0;
2344 size_t value = ++counter;
2345#else // PHMAP_HAVE_THREAD_LOCAL
2346 static std::atomic<size_t> counter(0);
2347 size_t value = counter.fetch_add(1, std::memory_order_relaxed);
2348#endif // PHMAP_HAVE_THREAD_LOCAL
2349 return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
2350}
2351
2352// ----------------------------------------------------------------------------
2353// ----------------------------------------------------------------------------
2354template <size_t N,
2355 template <class, class, class, class> class RefSet,
2356 class Mtx_,
2357 class Policy, class Hash, class Eq, class Alloc>
2359{
2360 using PolicyTraits = hash_policy_traits<Policy>;
2361 using KeyArgImpl =
2362 KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
2363
2364 static_assert(N <= 12, "N = 12 means 4096 hash tables!");
2365 constexpr static size_t num_tables = 1 << N;
2366 constexpr static size_t mask = num_tables - 1;
2367
2368public:
2369 using EmbeddedSet = RefSet<Policy, Hash, Eq, Alloc>;
2370 using EmbeddedIterator= typename EmbeddedSet::iterator;
2371 using EmbeddedConstIterator= typename EmbeddedSet::const_iterator;
2372 using constructor = typename EmbeddedSet::constructor;
2373 using init_type = typename PolicyTraits::init_type;
2374 using key_type = typename PolicyTraits::key_type;
2375 using slot_type = typename PolicyTraits::slot_type;
2376 using allocator_type = Alloc;
2377 using size_type = size_t;
2378 using difference_type = ptrdiff_t;
2379 using hasher = Hash;
2380 using key_equal = Eq;
2381 using policy_type = Policy;
2382 using value_type = typename PolicyTraits::value_type;
2383 using reference = value_type&;
2384 using const_reference = const value_type&;
2385 using pointer = typename phmap::allocator_traits<
2386 allocator_type>::template rebind_traits<value_type>::pointer;
2387 using const_pointer = typename phmap::allocator_traits<
2388 allocator_type>::template rebind_traits<value_type>::const_pointer;
2389
2390 // Alias used for heterogeneous lookup functions.
2391 // `key_arg<K>` evaluates to `K` when the functors are transparent and to
2392 // `key_type` otherwise. It permits template argument deduction on `K` for the
2393 // transparent case.
2394 // --------------------------------------------------------------------
2395 template <class K>
2396 using key_arg = typename KeyArgImpl::template type<K, key_type>;
2397
2398protected:
2400
2401 // --------------------------------------------------------------------
2402 struct Inner : public Lockable
2403 {
2404 bool operator==(const Inner& o) const
2405 {
2406 typename Lockable::SharedLocks l(const_cast<Inner &>(*this), const_cast<Inner &>(o));
2407 return set_ == o.set_;
2408 }
2409
2410 EmbeddedSet set_;
2411 };
2412
2413private:
2414 // Give an early error when key_type is not hashable/eq.
2415 // --------------------------------------------------------------------
2416 auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k));
2417 auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k));
2418
2420
2421 static_assert(std::is_lvalue_reference<reference>::value,
2422 "Policy::element() must return a reference");
2423
2424 template <typename T>
2425 struct SameAsElementReference : std::is_same<
2426 typename std::remove_cv<typename std::remove_reference<reference>::type>::type,
2427 typename std::remove_cv<typename std::remove_reference<T>::type>::type> {};
2428
2429 // An enabler for insert(T&&): T must be convertible to init_type or be the
2430 // same as [cv] value_type [ref].
2431 // Note: we separate SameAsElementReference into its own type to avoid using
2432 // reference unless we need to. MSVC doesn't seem to like it in some
2433 // cases.
2434 // --------------------------------------------------------------------
2435 template <class T>
2436 using RequiresInsertable = typename std::enable_if<
2438 SameAsElementReference<T>>::value,
2439 int>::type;
2440
2441 // RequiresNotInit is a workaround for gcc prior to 7.1.
2442 // See https://godbolt.org/g/Y4xsUh.
2443 template <class T>
2444 using RequiresNotInit =
2445 typename std::enable_if<!std::is_same<T, init_type>::value, int>::type;
2446
2447 template <class... Ts>
2448 using IsDecomposable = IsDecomposable<void, PolicyTraits, Hash, Eq, Ts...>;
2449
2450public:
2451 static_assert(std::is_same<pointer, value_type*>::value,
2452 "Allocators with custom pointer types are not supported");
2453 static_assert(std::is_same<const_pointer, const value_type*>::value,
2454 "Allocators with custom pointer types are not supported");
2455
2456 // --------------------- i t e r a t o r ------------------------------
2458 {
2459 friend class parallel_hash_set;
2460
2461 public:
2462 using iterator_category = std::forward_iterator_tag;
2463 using value_type = typename parallel_hash_set::value_type;
2464 using reference =
2465 phmap::conditional_t<PolicyTraits::constant_iterators::value,
2466 const value_type&, value_type&>;
2467 using pointer = phmap::remove_reference_t<reference>*;
2468 using difference_type = typename parallel_hash_set::difference_type;
2469 using Inner = typename parallel_hash_set::Inner;
2470 using EmbeddedSet = typename parallel_hash_set::EmbeddedSet;
2471 using EmbeddedIterator = typename EmbeddedSet::iterator;
2472
2473 iterator() {}
2474
2475 reference operator*() const { return *it_; }
2476 pointer operator->() const { return &operator*(); }
2477
2478 iterator& operator++() {
2479 assert(inner_); // null inner means we are already at the end
2480 ++it_;
2481 skip_empty();
2482 return *this;
2483 }
2484
2485 iterator operator++(int) {
2486 assert(inner_); // null inner means we are already at the end
2487 auto tmp = *this;
2488 ++*this;
2489 return tmp;
2490 }
2491
2492 friend bool operator==(const iterator& a, const iterator& b) {
2493 return a.inner_ == b.inner_ && (!a.inner_ || a.it_ == b.it_);
2494 }
2495
2496 friend bool operator!=(const iterator& a, const iterator& b) {
2497 return !(a == b);
2498 }
2499
2500 private:
2501 iterator(Inner *inner, Inner *inner_end, const EmbeddedIterator& it) :
2502 inner_(inner), inner_end_(inner_end), it_(it) { // for begin() and end()
2503 if (inner)
2504 it_end_ = inner->set_.end();
2505 }
2506
2507 void skip_empty() {
2508 while (it_ == it_end_) {
2509 ++inner_;
2510 if (inner_ == inner_end_) {
2511 inner_ = nullptr; // marks end()
2512 break;
2513 }
2514 else {
2515 it_ = inner_->set_.begin();
2516 it_end_ = inner_->set_.end();
2517 }
2518 }
2519 }
2520
2521 Inner *inner_ = nullptr;
2522 Inner *inner_end_ = nullptr;
2523 EmbeddedIterator it_, it_end_;
2524 };
2525
2526 // --------------------- c o n s t i t e r a t o r -----------------
2528 {
2529 friend class parallel_hash_set;
2530
2531 public:
2532 using iterator_category = typename iterator::iterator_category;
2533 using value_type = typename parallel_hash_set::value_type;
2534 using reference = typename parallel_hash_set::const_reference;
2535 using pointer = typename parallel_hash_set::const_pointer;
2536 using difference_type = typename parallel_hash_set::difference_type;
2537 using Inner = typename parallel_hash_set::Inner;
2538
2539 const_iterator() {}
2540 // Implicit construction from iterator.
2541 const_iterator(iterator i) : iter_(std::move(i)) {}
2542
2543 reference operator*() const { return *(iter_); }
2544 pointer operator->() const { return iter_.operator->(); }
2545
2546 const_iterator& operator++() {
2547 ++iter_;
2548 return *this;
2549 }
2550 const_iterator operator++(int) { return iter_++; }
2551
2552 friend bool operator==(const const_iterator& a, const const_iterator& b) {
2553 return a.iter_ == b.iter_;
2554 }
2555 friend bool operator!=(const const_iterator& a, const const_iterator& b) {
2556 return !(a == b);
2557 }
2558
2559 private:
2560 const_iterator(const Inner *inner, const Inner *inner_end, const EmbeddedIterator& it)
2561 : iter_(const_cast<Inner**>(inner),
2562 const_cast<Inner**>(inner_end),
2563 const_cast<EmbeddedIterator*>(it)) {}
2564
2565 iterator iter_;
2566 };
2567
2568 using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
2569 using insert_return_type = InsertReturnType<iterator, node_type>;
2570
2571 // ------------------------- c o n s t r u c t o r s ------------------
2572
2573 parallel_hash_set() noexcept(
2574 std::is_nothrow_default_constructible<hasher>::value&&
2575 std::is_nothrow_default_constructible<key_equal>::value&&
2576 std::is_nothrow_default_constructible<allocator_type>::value) {}
2577
2578 explicit parallel_hash_set(size_t bucket_cnt,
2579 const hasher& hash_param = hasher(),
2580 const key_equal& eq = key_equal(),
2581 const allocator_type& alloc = allocator_type()) {
2582 for (auto& inner : sets_)
2583 inner.set_ = EmbeddedSet(bucket_cnt / N, hash_param, eq, alloc);
2584 }
2585
2586 parallel_hash_set(size_t bucket_cnt,
2587 const hasher& hash_param,
2588 const allocator_type& alloc)
2589 : parallel_hash_set(bucket_cnt, hash_param, key_equal(), alloc) {}
2590
2591 parallel_hash_set(size_t bucket_cnt, const allocator_type& alloc)
2592 : parallel_hash_set(bucket_cnt, hasher(), key_equal(), alloc) {}
2593
2594 explicit parallel_hash_set(const allocator_type& alloc)
2595 : parallel_hash_set(0, hasher(), key_equal(), alloc) {}
2596
2597 template <class InputIter>
2598 parallel_hash_set(InputIter first, InputIter last, size_t bucket_cnt = 0,
2599 const hasher& hash_param = hasher(), const key_equal& eq = key_equal(),
2600 const allocator_type& alloc = allocator_type())
2601 : parallel_hash_set(bucket_cnt, hash_param, eq, alloc) {
2602 insert(first, last);
2603 }
2604
2605 template <class InputIter>
2606 parallel_hash_set(InputIter first, InputIter last, size_t bucket_cnt,
2607 const hasher& hash_param, const allocator_type& alloc)
2608 : parallel_hash_set(first, last, bucket_cnt, hash_param, key_equal(), alloc) {}
2609
2610 template <class InputIter>
2611 parallel_hash_set(InputIter first, InputIter last, size_t bucket_cnt,
2612 const allocator_type& alloc)
2613 : parallel_hash_set(first, last, bucket_cnt, hasher(), key_equal(), alloc) {}
2614
2615 template <class InputIter>
2616 parallel_hash_set(InputIter first, InputIter last, const allocator_type& alloc)
2617 : parallel_hash_set(first, last, 0, hasher(), key_equal(), alloc) {}
2618
2619 // Instead of accepting std::initializer_list<value_type> as the first
2620 // argument like std::unordered_set<value_type> does, we have two overloads
2621 // that accept std::initializer_list<T> and std::initializer_list<init_type>.
2622 // This is advantageous for performance.
2623 //
2624 // // Turns {"abc", "def"} into std::initializer_list<std::string>, then copies
2625 // // the strings into the set.
2626 // std::unordered_set<std::string> s = {"abc", "def"};
2627 //
2628 // // Turns {"abc", "def"} into std::initializer_list<const char*>, then
2629 // // copies the strings into the set.
2630 // phmap::flat_hash_set<std::string> s = {"abc", "def"};
2631 //
2632 // The same trick is used in insert().
2633 //
2634 // The enabler is necessary to prevent this constructor from triggering where
2635 // the copy constructor is meant to be called.
2636 //
2637 // phmap::flat_hash_set<int> a, b{a};
2638 //
2639 // RequiresNotInit<T> is a workaround for gcc prior to 7.1.
2640 // --------------------------------------------------------------------
2641 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
2642 parallel_hash_set(std::initializer_list<T> init, size_t bucket_cnt = 0,
2643 const hasher& hash_param = hasher(), const key_equal& eq = key_equal(),
2644 const allocator_type& alloc = allocator_type())
2645 : parallel_hash_set(init.begin(), init.end(), bucket_cnt, hash_param, eq, alloc) {}
2646
2647 parallel_hash_set(std::initializer_list<init_type> init, size_t bucket_cnt = 0,
2648 const hasher& hash_param = hasher(), const key_equal& eq = key_equal(),
2649 const allocator_type& alloc = allocator_type())
2650 : parallel_hash_set(init.begin(), init.end(), bucket_cnt, hash_param, eq, alloc) {}
2651
2652 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
2653 parallel_hash_set(std::initializer_list<T> init, size_t bucket_cnt,
2654 const hasher& hash_param, const allocator_type& alloc)
2655 : parallel_hash_set(init, bucket_cnt, hash_param, key_equal(), alloc) {}
2656
2657 parallel_hash_set(std::initializer_list<init_type> init, size_t bucket_cnt,
2658 const hasher& hash_param, const allocator_type& alloc)
2659 : parallel_hash_set(init, bucket_cnt, hash_param, key_equal(), alloc) {}
2660
2661 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
2662 parallel_hash_set(std::initializer_list<T> init, size_t bucket_cnt,
2663 const allocator_type& alloc)
2664 : parallel_hash_set(init, bucket_cnt, hasher(), key_equal(), alloc) {}
2665
2666 parallel_hash_set(std::initializer_list<init_type> init, size_t bucket_cnt,
2667 const allocator_type& alloc)
2668 : parallel_hash_set(init, bucket_cnt, hasher(), key_equal(), alloc) {}
2669
2670 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<T> = 0>
2671 parallel_hash_set(std::initializer_list<T> init, const allocator_type& alloc)
2672 : parallel_hash_set(init, 0, hasher(), key_equal(), alloc) {}
2673
2674 parallel_hash_set(std::initializer_list<init_type> init,
2675 const allocator_type& alloc)
2676 : parallel_hash_set(init, 0, hasher(), key_equal(), alloc) {}
2677
2678 parallel_hash_set(const parallel_hash_set& that)
2679 : parallel_hash_set(that, AllocTraits::select_on_container_copy_construction(
2680 that.alloc_ref())) {}
2681
2682 parallel_hash_set(const parallel_hash_set& that, const allocator_type& a)
2683 : parallel_hash_set(0, that.hash_ref(), that.eq_ref(), a) {
2684 for (size_t i=0; i<num_tables; ++i)
2685 sets_[i].set_ = { that.sets_[i].set_, a };
2686 }
2687
2688 parallel_hash_set(parallel_hash_set&& that) noexcept(
2689 std::is_nothrow_copy_constructible<hasher>::value&&
2690 std::is_nothrow_copy_constructible<key_equal>::value&&
2691 std::is_nothrow_copy_constructible<allocator_type>::value)
2692 : parallel_hash_set(std::move(that), that.alloc_ref()) {
2693 }
2694
2695 parallel_hash_set(parallel_hash_set&& that, const allocator_type& a)
2696 {
2697 for (size_t i=0; i<num_tables; ++i)
2698 sets_[i].set_ = { std::move(that.sets_[i]).set_, a };
2699 }
2700
2701 parallel_hash_set& operator=(const parallel_hash_set& that) {
2702 for (size_t i=0; i<num_tables; ++i)
2703 sets_[i].set_ = that.sets_[i].set_;
2704 return *this;
2705 }
2706
2707 parallel_hash_set& operator=(parallel_hash_set&& that) noexcept(
2709 std::is_nothrow_move_assignable<hasher>::value &&
2710 std::is_nothrow_move_assignable<key_equal>::value) {
2711 for (size_t i=0; i<num_tables; ++i)
2712 sets_[i].set_ = std::move(that.sets_[i].set_);
2713 return *this;
2714 }
2715
2716 ~parallel_hash_set() {}
2717
2718 iterator begin() {
2719 auto it = iterator(&sets_[0], &sets_[0] + num_tables, sets_[0].set_.begin());
2720 it.skip_empty();
2721 return it;
2722 }
2723
2724 iterator end() { return iterator(); }
2725 const_iterator begin() const { return const_cast<parallel_hash_set *>(this)->begin(); }
2726 const_iterator end() const { return const_cast<parallel_hash_set *>(this)->end(); }
2727 const_iterator cbegin() const { return begin(); }
2728 const_iterator cend() const { return end(); }
2729
2730 bool empty() const { return !size(); }
2731
2732 size_t size() const {
2733 size_t sz = 0;
2734 for (const auto& inner : sets_)
2735 sz += inner.set_.size();
2736 return sz;
2737 }
2738
2739 size_t capacity() const {
2740 size_t c = 0;
2741 for (const auto& inner : sets_)
2742 c += inner.set_.capacity();
2743 return c;
2744 }
2745
2746 size_t max_size() const { return (std::numeric_limits<size_t>::max)(); }
2747
2748 PHMAP_ATTRIBUTE_REINITIALIZES void clear() {
2749 for (auto& inner : sets_)
2750 {
2751 typename Lockable::UniqueLock m(inner);
2752 inner.set_.clear();
2753 }
2754 }
2755
2756 // extension - clears only soecified submap
2757 // ----------------------------------------
2758 void clear(std::size_t submap_index) {
2759 Inner& inner = sets_[submap_index];
2760 typename Lockable::UniqueLock m(inner);
2761 inner.set_.clear();
2762 }
2763
2764 // This overload kicks in when the argument is an rvalue of insertable and
2765 // decomposable type other than init_type.
2766 //
2767 // flat_hash_map<std::string, int> m;
2768 // m.insert(std::make_pair("abc", 42));
2769 // --------------------------------------------------------------------
2770 template <class T, RequiresInsertable<T> = 0,
2771 typename std::enable_if<IsDecomposable<T>::value, int>::type = 0,
2772 T* = nullptr>
2773 std::pair<iterator, bool> insert(T&& value) {
2774 return emplace(std::forward<T>(value));
2775 }
2776
2777 // This overload kicks in when the argument is a bitfield or an lvalue of
2778 // insertable and decomposable type.
2779 //
2780 // union { int n : 1; };
2781 // flat_hash_set<int> s;
2782 // s.insert(n);
2783 //
2784 // flat_hash_set<std::string> s;
2785 // const char* p = "hello";
2786 // s.insert(p);
2787 //
2788 // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
2789 // RequiresInsertable<T> with RequiresInsertable<const T&>.
2790 // We are hitting this bug: https://godbolt.org/g/1Vht4f.
2791 // --------------------------------------------------------------------
2792 template <
2793 class T, RequiresInsertable<T> = 0,
2794 typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
2795 std::pair<iterator, bool> insert(const T& value) {
2796 return emplace(value);
2797 }
2798
2799 // This overload kicks in when the argument is an rvalue of init_type. Its
2800 // purpose is to handle brace-init-list arguments.
2801 //
2802 // flat_hash_set<std::pair<std::string, int>> s;
2803 // s.insert({"abc", 42});
2804 // --------------------------------------------------------------------
2805 std::pair<iterator, bool> insert(init_type&& value) {
2806 return emplace(std::move(value));
2807 }
2808
2809 template <class T, RequiresInsertable<T> = 0,
2810 typename std::enable_if<IsDecomposable<T>::value, int>::type = 0,
2811 T* = nullptr>
2812 iterator insert(const_iterator, T&& value) {
2813 return insert(std::forward<T>(value)).first;
2814 }
2815
2816 // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace
2817 // RequiresInsertable<T> with RequiresInsertable<const T&>.
2818 // We are hitting this bug: https://godbolt.org/g/1Vht4f.
2819 // --------------------------------------------------------------------
2820 template <
2821 class T, RequiresInsertable<T> = 0,
2822 typename std::enable_if<IsDecomposable<const T&>::value, int>::type = 0>
2823 iterator insert(const_iterator, const T& value) {
2824 return insert(value).first;
2825 }
2826
2827 iterator insert(const_iterator, init_type&& value) {
2828 return insert(std::move(value)).first;
2829 }
2830
2831 template <class InputIt>
2832 void insert(InputIt first, InputIt last) {
2833 for (; first != last; ++first) insert(*first);
2834 }
2835
2836 template <class T, RequiresNotInit<T> = 0, RequiresInsertable<const T&> = 0>
2837 void insert(std::initializer_list<T> ilist) {
2838 insert(ilist.begin(), ilist.end());
2839 }
2840
2841 void insert(std::initializer_list<init_type> ilist) {
2842 insert(ilist.begin(), ilist.end());
2843 }
2844
2845 insert_return_type insert(node_type&& node) {
2846 if (!node)
2847 return {end(), false, node_type()};
2848 auto& key = node.key();
2849 size_t hashval = this->hash(key);
2850 Inner& inner = sets_[subidx(hashval)];
2851 auto& set = inner.set_;
2852
2853 typename Lockable::UniqueLock m(inner);
2854 auto res = set.insert(std::move(node), hashval);
2855 return { make_iterator(&inner, res.position),
2856 res.inserted,
2857 res.inserted ? node_type() : std::move(res.node) };
2858 }
2859
2860 iterator insert(const_iterator, node_type&& node) {
2861 return insert(std::move(node)).first;
2862 }
2863
2865 {
2866 template <class Key, class... Args>
2867 Key operator()(Key&& k, const Args&...) const {
2868 return std::forward<Key>(k);
2869 }
2870 };
2871
2872 // --------------------------------------------------------------------
2873 // phmap expension: emplace_with_hash
2874 // ----------------------------------
2875 // same as emplace, but hashval is provided
2876 // --------------------------------------------------------------------
2877 template <class K, class... Args>
2878 std::pair<iterator, bool> emplace_decomposable_with_hash(const K& key, size_t hashval, Args&&... args)
2879 {
2880 Inner& inner = sets_[subidx(hashval)];
2881 auto& set = inner.set_;
2882 typename Lockable::UniqueLock m(inner);
2883 return make_rv(&inner, set.emplace_decomposable(key, hashval, std::forward<Args>(args)...));
2884 }
2885
2887 {
2888 template <class K, class... Args>
2889 std::pair<iterator, bool> operator()(const K& key, Args&&... args) const {
2890 return s.emplace_decomposable_with_hash(key, hashval, std::forward<Args>(args)...);
2891 }
2893 size_t hashval;
2894 };
2895
2896 // This overload kicks in if we can deduce the key from args. This enables us
2897 // to avoid constructing value_type if an entry with the same key already
2898 // exists.
2899 //
2900 // For example:
2901 //
2902 // flat_hash_map<std::string, std::string> m = {{"abc", "def"}};
2903 // // Creates no std::string copies and makes no heap allocations.
2904 // m.emplace("abc", "xyz");
2905 // --------------------------------------------------------------------
2906 template <class... Args, typename std::enable_if<
2907 IsDecomposable<Args...>::value, int>::type = 0>
2908 std::pair<iterator, bool> emplace_with_hash(size_t hashval, Args&&... args) {
2909 return PolicyTraits::apply(EmplaceDecomposableHashval{*this, hashval},
2910 std::forward<Args>(args)...);
2911 }
2912
2913 // This overload kicks in if we cannot deduce the key from args. It constructs
2914 // value_type unconditionally and then either moves it into the table or
2915 // destroys.
2916 // --------------------------------------------------------------------
2917 template <class... Args, typename std::enable_if<
2918 !IsDecomposable<Args...>::value, int>::type = 0>
2919 std::pair<iterator, bool> emplace_with_hash(size_t hashval, Args&&... args) {
2920 typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type raw;
2921 slot_type* slot = reinterpret_cast<slot_type*>(&raw);
2922
2923 PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
2924 const auto& elem = PolicyTraits::element(slot);
2925 Inner& inner = sets_[subidx(hashval)];
2926 auto& set = inner.set_;
2927 typename Lockable::UniqueLock m(inner);
2928 typename EmbeddedSet::template InsertSlotWithHash<true> f {
2929 inner, std::move(*slot), hashval};
2930 return make_rv(PolicyTraits::apply(f, elem));
2931 }
2932
2933 template <class... Args>
2934 iterator emplace_hint_with_hash(size_t hashval, const_iterator, Args&&... args) {
2935 return emplace_with_hash(hashval, std::forward<Args>(args)...).first;
2936 }
2937
2938 template <class K = key_type, class F>
2939 iterator lazy_emplace_with_hash(size_t hashval, const key_arg<K>& key, F&& f) {
2940 Inner& inner = sets_[subidx(hashval)];
2941 auto& set = inner.set_;
2942 typename Lockable::UniqueLock m(inner);
2943 return make_iterator(&inner, set.lazy_emplace_with_hash(key, hashval, std::forward<F>(f)));
2944 }
2945
2946 // --------------------------------------------------------------------
2947 // end of phmap expension
2948 // --------------------------------------------------------------------
2949
2950 template <class K, class... Args>
2951 std::pair<iterator, bool> emplace_decomposable(const K& key, Args&&... args)
2952 {
2953 size_t hashval = this->hash(key);
2954 Inner& inner = sets_[subidx(hashval)];
2955 auto& set = inner.set_;
2956 typename Lockable::UniqueLock m(inner);
2957 return make_rv(&inner, set.emplace_decomposable(key, hashval, std::forward<Args>(args)...));
2958 }
2959
2961 {
2962 template <class K, class... Args>
2963 std::pair<iterator, bool> operator()(const K& key, Args&&... args) const {
2964 return s.emplace_decomposable(key, std::forward<Args>(args)...);
2965 }
2967 };
2968
2969 // This overload kicks in if we can deduce the key from args. This enables us
2970 // to avoid constructing value_type if an entry with the same key already
2971 // exists.
2972 //
2973 // For example:
2974 //
2975 // flat_hash_map<std::string, std::string> m = {{"abc", "def"}};
2976 // // Creates no std::string copies and makes no heap allocations.
2977 // m.emplace("abc", "xyz");
2978 // --------------------------------------------------------------------
2979 template <class... Args, typename std::enable_if<
2980 IsDecomposable<Args...>::value, int>::type = 0>
2981 std::pair<iterator, bool> emplace(Args&&... args) {
2982 return PolicyTraits::apply(EmplaceDecomposable{*this},
2983 std::forward<Args>(args)...);
2984 }
2985
2986 // This overload kicks in if we cannot deduce the key from args. It constructs
2987 // value_type unconditionally and then either moves it into the table or
2988 // destroys.
2989 // --------------------------------------------------------------------
2990 template <class... Args, typename std::enable_if<
2991 !IsDecomposable<Args...>::value, int>::type = 0>
2992 std::pair<iterator, bool> emplace(Args&&... args) {
2993 typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type raw;
2994 slot_type* slot = reinterpret_cast<slot_type*>(&raw);
2995 size_t hashval = this->hash(PolicyTraits::key(slot));
2996
2997 PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
2998 const auto& elem = PolicyTraits::element(slot);
2999 Inner& inner = sets_[subidx(hashval)];
3000 auto& set = inner.set_;
3001 typename Lockable::UniqueLock m(inner);
3002 typename EmbeddedSet::template InsertSlotWithHash<true> f {
3003 inner, std::move(*slot), hashval};
3004 return make_rv(PolicyTraits::apply(f, elem));
3005 }
3006
3007 template <class... Args>
3008 iterator emplace_hint(const_iterator, Args&&... args) {
3009 return emplace(std::forward<Args>(args)...).first;
3010 }
3011
3012 iterator make_iterator(Inner* inner, const EmbeddedIterator it)
3013 {
3014 if (it == inner->set_.end())
3015 return iterator();
3016 return iterator(inner, &sets_[0] + num_tables, it);
3017 }
3018
3019 std::pair<iterator, bool> make_rv(Inner* inner,
3020 const std::pair<EmbeddedIterator, bool>& res)
3021 {
3022 return {iterator(inner, &sets_[0] + num_tables, res.first), res.second};
3023 }
3024
3025 template <class K = key_type, class F>
3026 iterator lazy_emplace(const key_arg<K>& key, F&& f) {
3027 auto hashval = this->hash(key);
3028 Inner& inner = sets_[subidx(hashval)];
3029 auto& set = inner.set_;
3030 typename Lockable::UniqueLock m(inner);
3031 return make_iterator(&inner, set.lazy_emplace_with_hash(key, hashval, std::forward<F>(f)));
3032 }
3033
3034 template <class K = key_type, class FExists, class FEmplace>
3035 bool lazy_emplace_l(const key_arg<K>& key, FExists&& fExists, FEmplace&& fEmplace) {
3036 typename Lockable::UniqueLock m;
3037 auto res = this->find_or_prepare_insert(key, m);
3038 Inner* inner = std::get<0>(res);
3039 if (std::get<2>(res))
3040 inner->set_.lazy_emplace_at(std::get<1>(res), std::forward<FEmplace>(fEmplace));
3041 else {
3042 auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
3043 std::forward<FExists>(fExists)(Policy::value(&*it));
3044 }
3045 return std::get<2>(res);
3046 }
3047
3048 // Extension API: support for heterogeneous keys.
3049 //
3050 // std::unordered_set<std::string> s;
3051 // // Turns "abc" into std::string.
3052 // s.erase("abc");
3053 //
3054 // flat_hash_set<std::string> s;
3055 // // Uses "abc" directly without copying it into std::string.
3056 // s.erase("abc");
3057 // --------------------------------------------------------------------
3058 template <class K = key_type>
3059 size_type erase(const key_arg<K>& key) {
3060 auto hashval = this->hash(key);
3061 Inner& inner = sets_[subidx(hashval)];
3062 auto& set = inner.set_;
3063 typename Lockable::UpgradeLock m(inner);
3064 auto it = set.find(key, hashval);
3065 if (it == set.end())
3066 return 0;
3067
3068 typename Lockable::UpgradeToUnique unique(m);
3069 set._erase(it);
3070 return 1;
3071 }
3072
3073 // --------------------------------------------------------------------
3074 iterator erase(const_iterator cit) { return erase(cit.iter_); }
3075
3076 // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`,
3077 // this method returns void to reduce algorithmic complexity to O(1). In
3078 // order to erase while iterating across a map, use the following idiom (which
3079 // also works for standard containers):
3080 //
3081 // for (auto it = m.begin(), end = m.end(); it != end;) {
3082 // if (<pred>) {
3083 // m._erase(it++);
3084 // } else {
3085 // ++it;
3086 // }
3087 // }
3088 // --------------------------------------------------------------------
3089 void _erase(iterator it) {
3090 assert(it.inner_ != nullptr);
3091 it.inner_->set_._erase(it.it_);
3092 }
3093 void _erase(const_iterator cit) { _erase(cit.iter_); }
3094
3095 // This overload is necessary because otherwise erase<K>(const K&) would be
3096 // a better match if non-const iterator is passed as an argument.
3097 // --------------------------------------------------------------------
3098 iterator erase(iterator it) { _erase(it++); return it; }
3099
3100 iterator erase(const_iterator first, const_iterator last) {
3101 while (first != last) {
3102 _erase(first++);
3103 }
3104 return last.iter_;
3105 }
3106
3107 // Moves elements from `src` into `this`.
3108 // If the element already exists in `this`, it is left unmodified in `src`.
3109 // --------------------------------------------------------------------
3110 template <typename E = Eq>
3111 void merge(parallel_hash_set<N, RefSet, Mtx_, Policy, Hash, E, Alloc>& src) { // NOLINT
3112 assert(this != &src);
3113 if (this != &src)
3114 {
3115 for (size_t i=0; i<num_tables; ++i)
3116 {
3117 typename Lockable::UniqueLocks l(sets_[i], src.sets_[i]);
3118 sets_[i].set_.merge(src.sets_[i].set_);
3119 }
3120 }
3121 }
3122
3123 template <typename E = Eq>
3124 void merge(parallel_hash_set<N, RefSet, Mtx_, Policy, Hash, E, Alloc>&& src) {
3125 merge(src);
3126 }
3127
3128 node_type extract(const_iterator position) {
3129 return position.iter_.inner_->set_.extract(EmbeddedConstIterator(position.iter_.it_));
3130 }
3131
3132 template <
3133 class K = key_type,
3134 typename std::enable_if<!std::is_same<K, iterator>::value, int>::type = 0>
3135 node_type extract(const key_arg<K>& key) {
3136 auto it = find(key);
3137 return it == end() ? node_type() : extract(const_iterator{it});
3138 }
3139
3140 void swap(parallel_hash_set& that) noexcept(
3141 IsNoThrowSwappable<EmbeddedSet>() &&
3142 (!AllocTraits::propagate_on_container_swap::value ||
3143 IsNoThrowSwappable<allocator_type>())) {
3144 using std::swap;
3145 for (size_t i=0; i<num_tables; ++i)
3146 {
3147 typename Lockable::UniqueLocks l(sets_[i], that.sets_[i]);
3148 swap(sets_[i].set_, that.sets_[i].set_);
3149 }
3150 }
3151
3152 void rehash(size_t n) {
3153 size_t nn = n / num_tables;
3154 for (auto& inner : sets_)
3155 {
3156 typename Lockable::UniqueLock m(inner);
3157 inner.set_.rehash(nn);
3158 }
3159 }
3160
3161 void reserve(size_t n)
3162 {
3163 size_t target = GrowthToLowerboundCapacity(n);
3164 size_t normalized = 16 * NormalizeCapacity(n / num_tables);
3165 rehash(normalized > target ? normalized : target);
3166 }
3167
3168 // Extension API: support for heterogeneous keys.
3169 //
3170 // std::unordered_set<std::string> s;
3171 // // Turns "abc" into std::string.
3172 // s.count("abc");
3173 //
3174 // ch_set<std::string> s;
3175 // // Uses "abc" directly without copying it into std::string.
3176 // s.count("abc");
3177 // --------------------------------------------------------------------
3178 template <class K = key_type>
3179 size_t count(const key_arg<K>& key) const {
3180 return find(key) == end() ? 0 : 1;
3181 }
3182
3183 // Issues CPU prefetch instructions for the memory needed to find or insert
3184 // a key. Like all lookup functions, this support heterogeneous keys.
3185 //
3186 // NOTE: This is a very low level operation and should not be used without
3187 // specific benchmarks indicating its importance.
3188 // --------------------------------------------------------------------
3189 template <class K = key_type>
3190 void prefetch(const key_arg<K>& key) const {
3191 (void)key;
3192 size_t hashval = this->hash(key);
3193 const Inner& inner = sets_[subidx(hashval)];
3194 const auto& set = inner.set_;
3195 typename Lockable::SharedLock m(const_cast<Inner&>(inner));
3196 set.prefetch_hash(hashval);
3197 }
3198
3199 // The API of find() has two extensions.
3200 //
3201 // 1. The hash can be passed by the user. It must be equal to the hash of the
3202 // key.
3203 //
3204 // 2. The type of the key argument doesn't have to be key_type. This is so
3205 // called heterogeneous key support.
3206 // --------------------------------------------------------------------
3207 template <class K = key_type>
3208 iterator find(const key_arg<K>& key, size_t hashval) {
3209 typename Lockable::SharedLock m;
3210 return find(key, hashval, m);
3211 }
3212
3213 template <class K = key_type>
3214 iterator find(const key_arg<K>& key) {
3215 return find(key, this->hash(key));
3216 }
3217
3218 template <class K = key_type>
3219 const_iterator find(const key_arg<K>& key, size_t hashval) const {
3220 return const_cast<parallel_hash_set*>(this)->find(key, hashval);
3221 }
3222
3223 template <class K = key_type>
3224 const_iterator find(const key_arg<K>& key) const {
3225 return find(key, this->hash(key));
3226 }
3227
3228 template <class K = key_type>
3229 bool contains(const key_arg<K>& key) const {
3230 return find(key) != end();
3231 }
3232
3233 template <class K = key_type>
3234 bool contains(const key_arg<K>& key, size_t hashval) const {
3235 return find(key, hashval) != end();
3236 }
3237
3238 template <class K = key_type>
3239 std::pair<iterator, iterator> equal_range(const key_arg<K>& key) {
3240 auto it = find(key);
3241 if (it != end()) return {it, std::next(it)};
3242 return {it, it};
3243 }
3244
3245 template <class K = key_type>
3246 std::pair<const_iterator, const_iterator> equal_range(
3247 const key_arg<K>& key) const {
3248 auto it = find(key);
3249 if (it != end()) return {it, std::next(it)};
3250 return {it, it};
3251 }
3252
3253 size_t bucket_count() const {
3254 size_t sz = 0;
3255 for (const auto& inner : sets_)
3256 {
3257 typename Lockable::SharedLock m(const_cast<Inner&>(inner));
3258 sz += inner.set_.bucket_count();
3259 }
3260 return sz;
3261 }
3262
3263 float load_factor() const {
3264 size_t _capacity = bucket_count();
3265 return _capacity ? static_cast<float>(static_cast<double>(size()) / _capacity) : 0;
3266 }
3267
3268 float max_load_factor() const { return 1.0f; }
3269 void max_load_factor(float) {
3270 // Does nothing.
3271 }
3272
3273 hasher hash_function() const { return hash_ref(); } // warning: doesn't match internal hash - use hash() member function
3274 key_equal key_eq() const { return eq_ref(); }
3275 allocator_type get_allocator() const { return alloc_ref(); }
3276
3277 friend bool operator==(const parallel_hash_set& a, const parallel_hash_set& b) {
3278 return std::equal(a.sets_.begin(), a.sets_.end(), b.sets_.begin());
3279 }
3280
3281 friend bool operator!=(const parallel_hash_set& a, const parallel_hash_set& b) {
3282 return !(a == b);
3283 }
3284
3285 friend void swap(parallel_hash_set& a,
3286 parallel_hash_set& b) noexcept(noexcept(a.swap(b))) {
3287 a.swap(b);
3288 }
3289
3290 template <class K>
3291 size_t hash(const K& key) const {
3292 return HashElement{hash_ref()}(key);
3293 }
3294
3295#ifndef PHMAP_NON_DETERMINISTIC
3296 template<typename OutputArchive>
3297 bool dump(OutputArchive& ar) const;
3298
3299 template<typename InputArchive>
3300 bool load(InputArchive& ar);
3301#endif
3302
3303private:
3304 template <class Container, typename Enabler>
3306
3307 struct FindElement
3308 {
3309 template <class K, class... Args>
3310 const_iterator operator()(const K& key, Args&&...) const {
3311 return s.find(key);
3312 }
3313 const parallel_hash_set& s;
3314 };
3315
3316 struct HashElement
3317 {
3318 template <class K, class... Args>
3319 size_t operator()(const K& key, Args&&...) const {
3320 return phmap_mix<sizeof(size_t)>()(h(key));
3321 }
3322 const hasher& h;
3323 };
3324
3325 template <class K1>
3326 struct EqualElement
3327 {
3328 template <class K2, class... Args>
3329 bool operator()(const K2& lhs, Args&&...) const {
3330 return eq(lhs, rhs);
3331 }
3332 const K1& rhs;
3333 const key_equal& eq;
3334 };
3335
3336 // "erases" the object from the container, except that it doesn't actually
3337 // destroy the object. It only updates all the metadata of the class.
3338 // This can be used in conjunction with Policy::transfer to move the object to
3339 // another place.
3340 // --------------------------------------------------------------------
3341 void erase_meta_only(const_iterator cit) {
3342 auto &it = cit.iter_;
3343 assert(it.set_ != nullptr);
3344 it.set_.erase_meta_only(const_iterator(it.it_));
3345 }
3346
3347 void drop_deletes_without_resize() PHMAP_ATTRIBUTE_NOINLINE {
3348 for (auto& inner : sets_)
3349 {
3350 typename Lockable::UniqueLock m(inner);
3351 inner.set_.drop_deletes_without_resize();
3352 }
3353 }
3354
3355 bool has_element(const value_type& elem) const {
3356 size_t hashval = PolicyTraits::apply(HashElement{hash_ref()}, elem);
3357 Inner& inner = sets_[subidx(hashval)];
3358 auto& set = inner.set_;
3359 typename Lockable::SharedLock m(const_cast<Inner&>(inner));
3360 return set.has_element(elem, hashval);
3361 }
3362
3363 // TODO(alkis): Optimize this assuming *this and that don't overlap.
3364 // --------------------------------------------------------------------
3365 parallel_hash_set& move_assign(parallel_hash_set&& that, std::true_type) {
3366 parallel_hash_set tmp(std::move(that));
3367 swap(tmp);
3368 return *this;
3369 }
3370
3371 parallel_hash_set& move_assign(parallel_hash_set&& that, std::false_type) {
3372 parallel_hash_set tmp(std::move(that), alloc_ref());
3373 swap(tmp);
3374 return *this;
3375 }
3376
3377protected:
3378 template <class K = key_type, class L = typename Lockable::SharedLock>
3379 iterator find(const key_arg<K>& key, size_t hashval, L &mutexlock) {
3380 Inner& inner = sets_[subidx(hashval)];
3381 auto& set = inner.set_;
3382 mutexlock = std::move(L(inner));
3383 auto it = set.find(key, hashval);
3384 return make_iterator(&inner, it);
3385 }
3386
3387 template <class K>
3388 std::tuple<Inner*, size_t, bool>
3389 find_or_prepare_insert_with_hash(size_t hashval, const K& key, typename Lockable::UniqueLock &mutexlock) {
3390 Inner& inner = sets_[subidx(hashval)];
3391 auto& set = inner.set_;
3392 mutexlock = std::move(typename Lockable::UniqueLock(inner));
3393 auto p = set.find_or_prepare_insert(key, hashval); // std::pair<size_t, bool>
3394 return std::make_tuple(&inner, p.first, p.second);
3395 }
3396
3397 template <class K>
3398 std::tuple<Inner*, size_t, bool>
3399 find_or_prepare_insert(const K& key, typename Lockable::UniqueLock &mutexlock) {
3400 return find_or_prepare_insert_with_hash<K>(this->hash(key), key, mutexlock);
3401 }
3402
3403 iterator iterator_at(Inner *inner,
3404 const EmbeddedIterator& it) {
3405 return {inner, &sets_[0] + num_tables, it};
3406 }
3407 const_iterator iterator_at(Inner *inner,
3408 const EmbeddedIterator& it) const {
3409 return {inner, &sets_[0] + num_tables, it};
3410 }
3411
3412 static size_t subidx(size_t hashval) {
3413 return ((hashval >> 8) ^ (hashval >> 16) ^ (hashval >> 24)) & mask;
3414 }
3415
3416 static size_t subcnt() {
3417 return num_tables;
3418 }
3419
3420private:
3421 friend struct RawHashSetTestOnlyAccess;
3422
3423 size_t growth_left() {
3424 size_t sz = 0;
3425 for (const auto& set : sets_)
3426 sz += set.growth_left();
3427 return sz;
3428 }
3429
3430 hasher& hash_ref() { return sets_[0].set_.hash_ref(); }
3431 const hasher& hash_ref() const { return sets_[0].set_.hash_ref(); }
3432 key_equal& eq_ref() { return sets_[0].set_.eq_ref(); }
3433 const key_equal& eq_ref() const { return sets_[0].set_.eq_ref(); }
3434 allocator_type& alloc_ref() { return sets_[0].set_.alloc_ref(); }
3435 const allocator_type& alloc_ref() const {
3436 return sets_[0].set_.alloc_ref();
3437 }
3438
3439protected: // protected in case users want to derive fromm this
3440 std::array<Inner, num_tables> sets_;
3441};
3442
3443// --------------------------------------------------------------------------
3444// --------------------------------------------------------------------------
3445template <size_t N,
3446 template <class, class, class, class> class RefSet,
3447 class Mtx_,
3448 class Policy, class Hash, class Eq, class Alloc>
3449class parallel_hash_map : public parallel_hash_set<N, RefSet, Mtx_, Policy, Hash, Eq, Alloc>
3450{
3451 // P is Policy. It's passed as a template argument to support maps that have
3452 // incomplete types as values, as in unordered_map<K, IncompleteType>.
3453 // MappedReference<> may be a non-reference type.
3454 template <class P>
3455 using MappedReference = decltype(P::value(
3456 std::addressof(std::declval<typename parallel_hash_map::reference>())));
3457
3458 // MappedConstReference<> may be a non-reference type.
3459 template <class P>
3460 using MappedConstReference = decltype(P::value(
3461 std::addressof(std::declval<typename parallel_hash_map::const_reference>())));
3462
3463 using KeyArgImpl =
3464 KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
3465
3466 using Base = typename parallel_hash_map::parallel_hash_set;
3468
3469public:
3470 using key_type = typename Policy::key_type;
3471 using mapped_type = typename Policy::mapped_type;
3472 template <class K>
3473 using key_arg = typename KeyArgImpl::template type<K, key_type>;
3474
3475 static_assert(!std::is_reference<key_type>::value, "");
3476 // TODO(alkis): remove this assertion and verify that reference mapped_type is
3477 // supported.
3478 static_assert(!std::is_reference<mapped_type>::value, "");
3479
3480 using iterator = typename parallel_hash_map::parallel_hash_set::iterator;
3481 using const_iterator = typename parallel_hash_map::parallel_hash_set::const_iterator;
3482
3484
3485#ifdef __INTEL_COMPILER
3486 using Base::parallel_hash_set;
3487#else
3488 using parallel_hash_map::parallel_hash_set::parallel_hash_set;
3489#endif
3490
3491 // The last two template parameters ensure that both arguments are rvalues
3492 // (lvalue arguments are handled by the overloads below). This is necessary
3493 // for supporting bitfield arguments.
3494 //
3495 // union { int n : 1; };
3496 // flat_hash_map<int, int> m;
3497 // m.insert_or_assign(n, n);
3498 template <class K = key_type, class V = mapped_type, K* = nullptr,
3499 V* = nullptr>
3500 std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v) {
3501 return insert_or_assign_impl(std::forward<K>(k), std::forward<V>(v));
3502 }
3503
3504 template <class K = key_type, class V = mapped_type, K* = nullptr>
3505 std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v) {
3506 return insert_or_assign_impl(std::forward<K>(k), v);
3507 }
3508
3509 template <class K = key_type, class V = mapped_type, V* = nullptr>
3510 std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v) {
3511 return insert_or_assign_impl(k, std::forward<V>(v));
3512 }
3513
3514 template <class K = key_type, class V = mapped_type>
3515 std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v) {
3516 return insert_or_assign_impl(k, v);
3517 }
3518
3519 template <class K = key_type, class V = mapped_type, K* = nullptr,
3520 V* = nullptr>
3521 iterator insert_or_assign(const_iterator, key_arg<K>&& k, V&& v) {
3522 return insert_or_assign(std::forward<K>(k), std::forward<V>(v)).first;
3523 }
3524
3525 template <class K = key_type, class V = mapped_type, K* = nullptr>
3526 iterator insert_or_assign(const_iterator, key_arg<K>&& k, const V& v) {
3527 return insert_or_assign(std::forward<K>(k), v).first;
3528 }
3529
3530 template <class K = key_type, class V = mapped_type, V* = nullptr>
3531 iterator insert_or_assign(const_iterator, const key_arg<K>& k, V&& v) {
3532 return insert_or_assign(k, std::forward<V>(v)).first;
3533 }
3534
3535 template <class K = key_type, class V = mapped_type>
3536 iterator insert_or_assign(const_iterator, const key_arg<K>& k, const V& v) {
3537 return insert_or_assign(k, v).first;
3538 }
3539
3540 template <class K = key_type, class... Args,
3541 typename std::enable_if<
3542 !std::is_convertible<K, const_iterator>::value, int>::type = 0,
3543 K* = nullptr>
3544 std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args) {
3545 return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...);
3546 }
3547
3548 template <class K = key_type, class... Args,
3549 typename std::enable_if<
3550 !std::is_convertible<K, const_iterator>::value, int>::type = 0>
3551 std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args) {
3552 return try_emplace_impl(k, std::forward<Args>(args)...);
3553 }
3554
3555 template <class K = key_type, class... Args, K* = nullptr>
3556 iterator try_emplace(const_iterator, key_arg<K>&& k, Args&&... args) {
3557 return try_emplace(std::forward<K>(k), std::forward<Args>(args)...).first;
3558 }
3559
3560 template <class K = key_type, class... Args>
3561 iterator try_emplace(const_iterator, const key_arg<K>& k, Args&&... args) {
3562 return try_emplace(k, std::forward<Args>(args)...).first;
3563 }
3564
3565 template <class K = key_type, class P = Policy>
3566 MappedReference<P> at(const key_arg<K>& key) {
3567 auto it = this->find(key);
3568 if (it == this->end())
3569 phmap::base_internal::ThrowStdOutOfRange("phmap at(): lookup non-existent key");
3570 return Policy::value(&*it);
3571 }
3572
3573 template <class K = key_type, class P = Policy>
3574 MappedConstReference<P> at(const key_arg<K>& key) const {
3575 auto it = this->find(key);
3576 if (it == this->end())
3577 phmap::base_internal::ThrowStdOutOfRange("phmap at(): lookup non-existent key");
3578 return Policy::value(&*it);
3579 }
3580
3581 // ----------- phmap extensions --------------------------
3582
3583 template <class K = key_type, class... Args,
3584 typename std::enable_if<
3585 !std::is_convertible<K, const_iterator>::value, int>::type = 0,
3586 K* = nullptr>
3587 std::pair<iterator, bool> try_emplace_with_hash(size_t hashval, key_arg<K>&& k, Args&&... args) {
3588 return try_emplace_impl_with_hash(hashval, std::forward<K>(k), std::forward<Args>(args)...);
3589 }
3590
3591 template <class K = key_type, class... Args,
3592 typename std::enable_if<
3593 !std::is_convertible<K, const_iterator>::value, int>::type = 0>
3594 std::pair<iterator, bool> try_emplace_with_hash(size_t hashval, const key_arg<K>& k, Args&&... args) {
3595 return try_emplace_impl_with_hash(hashval, k, std::forward<Args>(args)...);
3596 }
3597
3598 template <class K = key_type, class... Args, K* = nullptr>
3599 iterator try_emplace_with_hash(size_t hashval, const_iterator, key_arg<K>&& k, Args&&... args) {
3600 return try_emplace_with_hash(hashval, std::forward<K>(k), std::forward<Args>(args)...).first;
3601 }
3602
3603 template <class K = key_type, class... Args>
3604 iterator try_emplace_with_hash(size_t hashval, const_iterator, const key_arg<K>& k, Args&&... args) {
3605 return try_emplace_with_hash(hashval, k, std::forward<Args>(args)...).first;
3606 }
3607
3608 // if map contains key, lambda is called with the mapped value (under read lock protection),
3609 // and if_contains returns true. This is a const API and lambda should not modify the value
3610 // -----------------------------------------------------------------------------------------
3611 template <class K = key_type, class F>
3612 bool if_contains(const key_arg<K>& key, F&& f) const {
3613 return const_cast<parallel_hash_map*>(this)->template
3614 modify_if_impl<K, F, typename Lockable::SharedLock>(key, std::forward<F>(f));
3615 }
3616
3617 // if map contains key, lambda is called with the mapped value (under write lock protection),
3618 // and modify_if returns true. This is a non-const API and lambda is allowed to modify the mapped value
3619 // ----------------------------------------------------------------------------------------------------
3620 template <class K = key_type, class F>
3621 bool modify_if(const key_arg<K>& key, F&& f) {
3622 return modify_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
3623 }
3624
3625
3626 // if map contains key, lambda is called with the mapped value (under write lock protection).
3627 // If the lambda returns true, the key is subsequently erased from the map (the write lock
3628 // is only released after erase).
3629 // returns true if key was erased, false otherwise.
3630 // ----------------------------------------------------------------------------------------------------
3631 template <class K = key_type, class F>
3632 bool erase_if(const key_arg<K>& key, F&& f) {
3633 return erase_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
3634 }
3635
3636 // if map does not contains key, it is inserted and the mapped value is value-constructed
3637 // with the provided arguments (if any), as with try_emplace.
3638 // if map already contains key, then the lambda is called with the mapped value (under
3639 // write lock protection) and can update the mapped value.
3640 // returns true if key was not already present, false otherwise.
3641 // ---------------------------------------------------------------------------------------
3642 template <class K = key_type, class F, class... Args>
3643 bool try_emplace_l(K&& k, F&& f, Args&&... args) {
3644 typename Lockable::UniqueLock m;
3645 auto res = this->find_or_prepare_insert(k, m);
3646 typename Base::Inner *inner = std::get<0>(res);
3647 if (std::get<2>(res))
3648 inner->set_.emplace_at(std::get<1>(res), std::piecewise_construct,
3649 std::forward_as_tuple(std::forward<K>(k)),
3650 std::forward_as_tuple(std::forward<Args>(args)...));
3651 else {
3652 auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
3653 std::forward<F>(f)(Policy::value(&*it));
3654 }
3655 return std::get<2>(res);
3656 }
3657
3658 // ----------- end of phmap extensions --------------------------
3659
3660 template <class K = key_type, class P = Policy, K* = nullptr>
3661 MappedReference<P> operator[](key_arg<K>&& key) {
3662 return Policy::value(&*try_emplace(std::forward<K>(key)).first);
3663 }
3664
3665 template <class K = key_type, class P = Policy>
3666 MappedReference<P> operator[](const key_arg<K>& key) {
3667 return Policy::value(&*try_emplace(key).first);
3668 }
3669
3670private:
3671 template <class K = key_type, class F, class L>
3672 bool modify_if_impl(const key_arg<K>& key, F&& f) {
3673#if __cplusplus >= 201703L
3674 static_assert(std::is_invocable<F, mapped_type&>::value);
3675#endif
3676 L m;
3677 auto it = this->template find<K, L>(key, this->hash(key), m);
3678 if (it == this->end())
3679 return false;
3680 std::forward<F>(f)(Policy::value(&*it));
3681 return true;
3682 }
3683
3684 template <class K = key_type, class F, class L>
3685 bool erase_if_impl(const key_arg<K>& key, F&& f) {
3686#if __cplusplus >= 201703L
3687 static_assert(std::is_invocable<F, mapped_type&>::value);
3688#endif
3689 L m;
3690 auto it = this->template find<K, L>(key, this->hash(key), m);
3691 if (it == this->end())
3692 return false;
3693 if (std::forward<F>(f)(Policy::value(&*it)))
3694 {
3695 this->erase(it);
3696 return true;
3697 }
3698 return false;
3699 }
3700
3701
3702 template <class K, class V>
3703 std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
3704 typename Lockable::UniqueLock m;
3705 auto res = this->find_or_prepare_insert(k, m);
3706 typename Base::Inner *inner = std::get<0>(res);
3707 if (std::get<2>(res))
3708 inner->set_.emplace_at(std::get<1>(res), std::forward<K>(k), std::forward<V>(v));
3709 else
3710 Policy::value(&*inner->set_.iterator_at(std::get<1>(res))) = std::forward<V>(v);
3711 return {this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res))),
3712 std::get<2>(res)};
3713 }
3714
3715 template <class K = key_type, class... Args>
3716 std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) {
3717 typename Lockable::UniqueLock m;
3718 auto res = this->find_or_prepare_insert(k, m);
3719 typename Base::Inner *inner = std::get<0>(res);
3720 if (std::get<2>(res))
3721 inner->set_.emplace_at(std::get<1>(res), std::piecewise_construct,
3722 std::forward_as_tuple(std::forward<K>(k)),
3723 std::forward_as_tuple(std::forward<Args>(args)...));
3724 return {this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res))),
3725 std::get<2>(res)};
3726 }
3727
3728 template <class K = key_type, class... Args>
3729 std::pair<iterator, bool> try_emplace_impl_with_hash(size_t hashval, K&& k, Args&&... args) {
3730 typename Lockable::UniqueLock m;
3731 auto res = this->find_or_prepare_insert_with_hash(hashval, k, m);
3732 typename Base::Inner *inner = std::get<0>(res);
3733 if (std::get<2>(res))
3734 inner->set_.emplace_at(std::get<1>(res), std::piecewise_construct,
3735 std::forward_as_tuple(std::forward<K>(k)),
3736 std::forward_as_tuple(std::forward<Args>(args)...));
3737 return {this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res))),
3738 std::get<2>(res)};
3739 }
3740
3741
3742};
3743
3744
3745// Constructs T into uninitialized storage pointed by `ptr` using the args
3746// specified in the tuple.
3747// ----------------------------------------------------------------------------
3748template <class Alloc, class T, class Tuple>
3749void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) {
3750 memory_internal::ConstructFromTupleImpl(
3751 alloc, ptr, std::forward<Tuple>(t),
3752 phmap::make_index_sequence<
3753 std::tuple_size<typename std::decay<Tuple>::type>::value>());
3754}
3755
3756// Constructs T using the args specified in the tuple and calls F with the
3757// constructed value.
3758// ----------------------------------------------------------------------------
3759template <class T, class Tuple, class F>
3760decltype(std::declval<F>()(std::declval<T>())) WithConstructed(
3761 Tuple&& t, F&& f) {
3762 return memory_internal::WithConstructedImpl<T>(
3763 std::forward<Tuple>(t),
3764 phmap::make_index_sequence<
3765 std::tuple_size<typename std::decay<Tuple>::type>::value>(),
3766 std::forward<F>(f));
3767}
3768
3769// ----------------------------------------------------------------------------
3770// Given arguments of an std::pair's consructor, PairArgs() returns a pair of
3771// tuples with references to the passed arguments. The tuples contain
3772// constructor arguments for the first and the second elements of the pair.
3773//
3774// The following two snippets are equivalent.
3775//
3776// 1. std::pair<F, S> p(args...);
3777//
3778// 2. auto a = PairArgs(args...);
3779// std::pair<F, S> p(std::piecewise_construct,
3780// std::move(p.first), std::move(p.second));
3781// ----------------------------------------------------------------------------
3782inline std::pair<std::tuple<>, std::tuple<>> PairArgs() { return {}; }
3783
3784template <class F, class S>
3785std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(F&& f, S&& s) {
3786 return {std::piecewise_construct, std::forward_as_tuple(std::forward<F>(f)),
3787 std::forward_as_tuple(std::forward<S>(s))};
3788}
3789
3790template <class F, class S>
3791std::pair<std::tuple<const F&>, std::tuple<const S&>> PairArgs(
3792 const std::pair<F, S>& p) {
3793 return PairArgs(p.first, p.second);
3794}
3795
3796template <class F, class S>
3797std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(std::pair<F, S>&& p) {
3798 return PairArgs(std::forward<F>(p.first), std::forward<S>(p.second));
3799}
3800
3801template <class F, class S>
3802auto PairArgs(std::piecewise_construct_t, F&& f, S&& s)
3803 -> decltype(std::make_pair(memory_internal::TupleRef(std::forward<F>(f)),
3804 memory_internal::TupleRef(std::forward<S>(s)))) {
3805 return std::make_pair(memory_internal::TupleRef(std::forward<F>(f)),
3806 memory_internal::TupleRef(std::forward<S>(s)));
3807}
3808
3809// A helper function for implementing apply() in map policies.
3810// ----------------------------------------------------------------------------
3811template <class F, class... Args>
3812auto DecomposePair(F&& f, Args&&... args)
3813 -> decltype(memory_internal::DecomposePairImpl(
3814 std::forward<F>(f), PairArgs(std::forward<Args>(args)...))) {
3815 return memory_internal::DecomposePairImpl(
3816 std::forward<F>(f), PairArgs(std::forward<Args>(args)...));
3817}
3818
3819// A helper function for implementing apply() in set policies.
3820// ----------------------------------------------------------------------------
3821template <class F, class Arg>
3822decltype(std::declval<F>()(std::declval<const Arg&>(), std::declval<Arg>()))
3823DecomposeValue(F&& f, Arg&& arg) {
3824 const auto& key = arg;
3825 return std::forward<F>(f)(key, std::forward<Arg>(arg));
3826}
3827
3828
3829// --------------------------------------------------------------------------
3830// Policy: a policy defines how to perform different operations on
3831// the slots of the hashtable (see hash_policy_traits.h for the full interface
3832// of policy).
3833//
3834// Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The
3835// functor should accept a key and return size_t as hash. For best performance
3836// it is important that the hash function provides high entropy across all bits
3837// of the hash.
3838//
3839// Eq: a (possibly polymorphic) functor that compares two keys for equality. It
3840// should accept two (of possibly different type) keys and return a bool: true
3841// if they are equal, false if they are not. If two keys compare equal, then
3842// their hash values as defined by Hash MUST be equal.
3843//
3844// Allocator: an Allocator [https://devdocs.io/cpp/concept/allocator] with which
3845// the storage of the hashtable will be allocated and the elements will be
3846// constructed and destroyed.
3847// --------------------------------------------------------------------------
3848template <class T>
3850{
3851 using slot_type = T;
3852 using key_type = T;
3853 using init_type = T;
3854 using constant_iterators = std::true_type;
3855
3856 template <class Allocator, class... Args>
3857 static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
3859 std::forward<Args>(args)...);
3860 }
3861
3862 template <class Allocator>
3863 static void destroy(Allocator* alloc, slot_type* slot) {
3865 }
3866
3867 template <class Allocator>
3868 static void transfer(Allocator* alloc, slot_type* new_slot,
3869 slot_type* old_slot) {
3870 construct(alloc, new_slot, std::move(*old_slot));
3871 destroy(alloc, old_slot);
3872 }
3873
3874 static T& element(slot_type* slot) { return *slot; }
3875
3876 template <class F, class... Args>
3877 static decltype(phmap::priv::DecomposeValue(
3878 std::declval<F>(), std::declval<Args>()...))
3879 apply(F&& f, Args&&... args) {
3880 return phmap::priv::DecomposeValue(
3881 std::forward<F>(f), std::forward<Args>(args)...);
3882 }
3883
3884 static size_t space_used(const T*) { return 0; }
3885};
3886
3887// --------------------------------------------------------------------------
3888// --------------------------------------------------------------------------
3889template <class K, class V>
3891{
3893 using slot_type = typename slot_policy::slot_type;
3894 using key_type = K;
3895 using mapped_type = V;
3896 using init_type = std::pair</*non const*/ key_type, mapped_type>;
3897
3898 template <class Allocator, class... Args>
3899 static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
3900 slot_policy::construct(alloc, slot, std::forward<Args>(args)...);
3901 }
3902
3903 template <class Allocator>
3904 static void destroy(Allocator* alloc, slot_type* slot) {
3905 slot_policy::destroy(alloc, slot);
3906 }
3907
3908 template <class Allocator>
3909 static void transfer(Allocator* alloc, slot_type* new_slot,
3910 slot_type* old_slot) {
3911 slot_policy::transfer(alloc, new_slot, old_slot);
3912 }
3913
3914 template <class F, class... Args>
3915 static decltype(phmap::priv::DecomposePair(
3916 std::declval<F>(), std::declval<Args>()...))
3917 apply(F&& f, Args&&... args) {
3918 return phmap::priv::DecomposePair(std::forward<F>(f),
3919 std::forward<Args>(args)...);
3920 }
3921
3922 static size_t space_used(const slot_type*) { return 0; }
3923
3924 static std::pair<const K, V>& element(slot_type* slot) { return slot->value; }
3925
3926 static V& value(std::pair<const K, V>* kv) { return kv->second; }
3927 static const V& value(const std::pair<const K, V>* kv) { return kv->second; }
3928};
3929
3930template <class Reference, class Policy>
3932 static_assert(std::is_lvalue_reference<Reference>::value, "");
3933
3934 using slot_type = typename std::remove_cv<
3935 typename std::remove_reference<Reference>::type>::type*;
3936
3937 template <class Alloc, class... Args>
3938 static void construct(Alloc* alloc, slot_type* slot, Args&&... args) {
3939 *slot = Policy::new_element(alloc, std::forward<Args>(args)...);
3940 }
3941
3942 template <class Alloc>
3943 static void destroy(Alloc* alloc, slot_type* slot) {
3944 Policy::delete_element(alloc, *slot);
3945 }
3946
3947 template <class Alloc>
3948 static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) {
3949 *new_slot = *old_slot;
3950 }
3951
3952 static size_t space_used(const slot_type* slot) {
3953 if (slot == nullptr) return Policy::element_space_used(nullptr);
3954 return Policy::element_space_used(*slot);
3955 }
3956
3957 static Reference element(slot_type* slot) { return **slot; }
3958
3959 template <class T, class P = Policy>
3960 static auto value(T* elem) -> decltype(P::value(elem)) {
3961 return P::value(elem);
3962 }
3963
3964 template <class... Ts, class P = Policy>
3965 static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward<Ts>(ts)...)) {
3966 return P::apply(std::forward<Ts>(ts)...);
3967 }
3968};
3969
3970// --------------------------------------------------------------------------
3971// --------------------------------------------------------------------------
3972template <class T>
3974 : phmap::priv::node_hash_policy<T&, NodeHashSetPolicy<T>>
3975{
3976 using key_type = T;
3977 using init_type = T;
3978 using constant_iterators = std::true_type;
3979
3980 template <class Allocator, class... Args>
3981 static T* new_element(Allocator* alloc, Args&&... args) {
3982 using ValueAlloc =
3983 typename phmap::allocator_traits<Allocator>::template rebind_alloc<T>;
3984 ValueAlloc value_alloc(*alloc);
3985 T* res = phmap::allocator_traits<ValueAlloc>::allocate(value_alloc, 1);
3987 std::forward<Args>(args)...);
3988 return res;
3989 }
3990
3991 template <class Allocator>
3992 static void delete_element(Allocator* alloc, T* elem) {
3993 using ValueAlloc =
3994 typename phmap::allocator_traits<Allocator>::template rebind_alloc<T>;
3995 ValueAlloc value_alloc(*alloc);
3998 }
3999
4000 template <class F, class... Args>
4001 static decltype(phmap::priv::DecomposeValue(
4002 std::declval<F>(), std::declval<Args>()...))
4003 apply(F&& f, Args&&... args) {
4004 return phmap::priv::DecomposeValue(
4005 std::forward<F>(f), std::forward<Args>(args)...);
4006 }
4007
4008 static size_t element_space_used(const T*) { return sizeof(T); }
4009};
4010
4011// --------------------------------------------------------------------------
4012// --------------------------------------------------------------------------
4013template <class Key, class Value>
4016 std::pair<const Key, Value>&, NodeHashMapPolicy<Key, Value>>
4017{
4018 using value_type = std::pair<const Key, Value>;
4019
4020public:
4021 using key_type = Key;
4022 using mapped_type = Value;
4023 using init_type = std::pair</*non const*/ key_type, mapped_type>;
4024
4025 template <class Allocator, class... Args>
4026 static value_type* new_element(Allocator* alloc, Args&&... args) {
4027 using PairAlloc = typename phmap::allocator_traits<
4028 Allocator>::template rebind_alloc<value_type>;
4029 PairAlloc pair_alloc(*alloc);
4030 value_type* res =
4033 std::forward<Args>(args)...);
4034 return res;
4035 }
4036
4037 template <class Allocator>
4038 static void delete_element(Allocator* alloc, value_type* pair) {
4039 using PairAlloc = typename phmap::allocator_traits<
4040 Allocator>::template rebind_alloc<value_type>;
4041 PairAlloc pair_alloc(*alloc);
4044 }
4045
4046 template <class F, class... Args>
4047 static decltype(phmap::priv::DecomposePair(
4048 std::declval<F>(), std::declval<Args>()...))
4049 apply(F&& f, Args&&... args) {
4050 return phmap::priv::DecomposePair(std::forward<F>(f),
4051 std::forward<Args>(args)...);
4052 }
4053
4054 static size_t element_space_used(const value_type*) {
4055 return sizeof(value_type);
4056 }
4057
4058 static Value& value(value_type* elem) { return elem->second; }
4059 static const Value& value(const value_type* elem) { return elem->second; }
4060};
4061
4062
4063// --------------------------------------------------------------------------
4064// hash_default
4065// --------------------------------------------------------------------------
4066
4067#if PHMAP_HAVE_STD_STRING_VIEW
4068
4069// support char16_t wchar_t ....
4070template<class CharT>
4071struct StringHashT
4072{
4073 using is_transparent = void;
4074
4075 size_t operator()(std::basic_string_view<CharT> v) const {
4076 std::string_view bv{reinterpret_cast<const char*>(v.data()), v.size() * sizeof(CharT)};
4077 return std::hash<std::string_view>()(bv);
4078 }
4079};
4080
4081// Supports heterogeneous lookup for basic_string<T>-like elements.
4082template<class CharT>
4083struct StringHashEqT
4084{
4085 using Hash = StringHashT<CharT>;
4086
4087 struct Eq {
4088 using is_transparent = void;
4089
4090 bool operator()(std::basic_string_view<CharT> lhs, std::basic_string_view<CharT> rhs) const {
4091 return lhs == rhs;
4092 }
4093 };
4094};
4095
4096template <>
4097struct HashEq<std::string> : StringHashEqT<char> {};
4098
4099template <>
4100struct HashEq<std::string_view> : StringHashEqT<char> {};
4101
4102// char16_t
4103template <>
4104struct HashEq<std::u16string> : StringHashEqT<char16_t> {};
4105
4106template <>
4107struct HashEq<std::u16string_view> : StringHashEqT<char16_t> {};
4108
4109// wchar_t
4110template <>
4111struct HashEq<std::wstring> : StringHashEqT<wchar_t> {};
4112
4113template <>
4114struct HashEq<std::wstring_view> : StringHashEqT<wchar_t> {};
4115
4116#endif
4117
4118// Supports heterogeneous lookup for pointers and smart pointers.
4119// -------------------------------------------------------------
4120template <class T>
4121struct HashEq<T*>
4122{
4123 struct Hash {
4124 using is_transparent = void;
4125 template <class U>
4126 size_t operator()(const U& ptr) const {
4127 return phmap::Hash<const T*>{}(HashEq::ToPtr(ptr));
4128 }
4129 };
4130
4131 struct Eq {
4132 using is_transparent = void;
4133 template <class A, class B>
4134 bool operator()(const A& a, const B& b) const {
4135 return HashEq::ToPtr(a) == HashEq::ToPtr(b);
4136 }
4137 };
4138
4139private:
4140 static const T* ToPtr(const T* ptr) { return ptr; }
4141
4142 template <class U, class D>
4143 static const T* ToPtr(const std::unique_ptr<U, D>& ptr) {
4144 return ptr.get();
4145 }
4146
4147 template <class U>
4148 static const T* ToPtr(const std::shared_ptr<U>& ptr) {
4149 return ptr.get();
4150 }
4151};
4152
4153template <class T, class D>
4154struct HashEq<std::unique_ptr<T, D>> : HashEq<T*> {};
4155
4156template <class T>
4157struct HashEq<std::shared_ptr<T>> : HashEq<T*> {};
4158
4159namespace hashtable_debug_internal {
4160
4161// --------------------------------------------------------------------------
4162// --------------------------------------------------------------------------
4163template <typename Set>
4164struct HashtableDebugAccess<Set, phmap::void_t<typename Set::raw_hash_set>>
4165{
4166 using Traits = typename Set::PolicyTraits;
4167 using Slot = typename Traits::slot_type;
4168
4169 static size_t GetNumProbes(const Set& set,
4170 const typename Set::key_type& key) {
4171 size_t num_probes = 0;
4172 size_t hashval = set.hash(key);
4173 auto seq = set.probe(hashval);
4174 while (true) {
4175 priv::Group g{set.ctrl_ + seq.offset()};
4176 for (int i : g.Match(priv::H2(hashval))) {
4177 if (Traits::apply(
4178 typename Set::template EqualElement<typename Set::key_type>{
4179 key, set.eq_ref()},
4180 Traits::element(set.slots_ + seq.offset((size_t)i))))
4181 return num_probes;
4182 ++num_probes;
4183 }
4184 if (g.MatchEmpty()) return num_probes;
4185 seq.next();
4186 ++num_probes;
4187 }
4188 }
4189
4190 static size_t AllocatedByteSize(const Set& c) {
4191 size_t capacity = c.capacity_;
4192 if (capacity == 0) return 0;
4193 auto layout = Set::MakeLayout(capacity);
4194 size_t m = layout.AllocSize();
4195
4196 size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
4197 if (per_slot != ~size_t{}) {
4198 m += per_slot * c.size();
4199 } else {
4200 for (size_t i = 0; i != capacity; ++i) {
4201 if (priv::IsFull(c.ctrl_[i])) {
4202 m += Traits::space_used(c.slots_ + i);
4203 }
4204 }
4205 }
4206 return m;
4207 }
4208
4209 static size_t LowerBoundAllocatedByteSize(size_t size) {
4210 size_t capacity = GrowthToLowerboundCapacity(size);
4211 if (capacity == 0) return 0;
4212 auto layout = Set::MakeLayout(NormalizeCapacity(capacity));
4213 size_t m = layout.AllocSize();
4214 size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
4215 if (per_slot != ~size_t{}) {
4216 m += per_slot * size;
4217 }
4218 return m;
4219 }
4220};
4221
4222} // namespace hashtable_debug_internal
4223} // namespace priv
4224
4225// -----------------------------------------------------------------------------
4226// phmap::flat_hash_set
4227// -----------------------------------------------------------------------------
4228// An `phmap::flat_hash_set<T>` is an unordered associative container which has
4229// been optimized for both speed and memory footprint in most common use cases.
4230// Its interface is similar to that of `std::unordered_set<T>` with the
4231// following notable differences:
4232//
4233// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
4234// `insert()`, provided that the set is provided a compatible heterogeneous
4235// hashing function and equality operator.
4236// * Invalidates any references and pointers to elements within the table after
4237// `rehash()`.
4238// * Contains a `capacity()` member function indicating the number of element
4239// slots (open, deleted, and empty) within the hash set.
4240// * Returns `void` from the `_erase(iterator)` overload.
4241// -----------------------------------------------------------------------------
4242template <class T, class Hash, class Eq, class Alloc> // default values in phmap_fwd_decl.h
4245 phmap::priv::FlatHashSetPolicy<T>, Hash, Eq, Alloc>
4246{
4247 using Base = typename flat_hash_set::raw_hash_set;
4248
4249public:
4250 flat_hash_set() {}
4251#ifdef __INTEL_COMPILER
4252 using Base::raw_hash_set;
4253#else
4254 using Base::Base;
4255#endif
4256 using Base::begin;
4257 using Base::cbegin;
4258 using Base::cend;
4259 using Base::end;
4260 using Base::capacity;
4261 using Base::empty;
4262 using Base::max_size;
4263 using Base::size;
4264 using Base::clear; // may shrink - To avoid shrinking `erase(begin(), end())`
4265 using Base::erase;
4266 using Base::insert;
4267 using Base::emplace;
4268 using Base::emplace_hint;
4269 using Base::extract;
4270 using Base::merge;
4271 using Base::swap;
4272 using Base::rehash;
4273 using Base::reserve;
4274 using Base::contains;
4275 using Base::count;
4276 using Base::equal_range;
4277 using Base::find;
4278 using Base::bucket_count;
4279 using Base::load_factor;
4280 using Base::max_load_factor;
4281 using Base::get_allocator;
4282 using Base::hash_function;
4283 using Base::hash;
4284 using Base::key_eq;
4285};
4286
4287// -----------------------------------------------------------------------------
4288// phmap::flat_hash_map
4289// -----------------------------------------------------------------------------
4290//
4291// An `phmap::flat_hash_map<K, V>` is an unordered associative container which
4292// has been optimized for both speed and memory footprint in most common use
4293// cases. Its interface is similar to that of `std::unordered_map<K, V>` with
4294// the following notable differences:
4295//
4296// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
4297// `insert()`, provided that the map is provided a compatible heterogeneous
4298// hashing function and equality operator.
4299// * Invalidates any references and pointers to elements within the table after
4300// `rehash()`.
4301// * Contains a `capacity()` member function indicating the number of element
4302// slots (open, deleted, and empty) within the hash map.
4303// * Returns `void` from the `_erase(iterator)` overload.
4304// -----------------------------------------------------------------------------
4305template <class K, class V, class Hash, class Eq, class Alloc> // default values in phmap_fwd_decl.h
4307 phmap::priv::FlatHashMapPolicy<K, V>,
4308 Hash, Eq, Alloc> {
4309 using Base = typename flat_hash_map::raw_hash_map;
4310
4311public:
4312 flat_hash_map() {}
4313#ifdef __INTEL_COMPILER
4314 using Base::raw_hash_map;
4315#else
4316 using Base::Base;
4317#endif
4318 using Base::begin;
4319 using Base::cbegin;
4320 using Base::cend;
4321 using Base::end;
4322 using Base::capacity;
4323 using Base::empty;
4324 using Base::max_size;
4325 using Base::size;
4326 using Base::clear;
4327 using Base::erase;
4328 using Base::insert;
4329 using Base::insert_or_assign;
4330 using Base::emplace;
4331 using Base::emplace_hint;
4332 using Base::try_emplace;
4333 using Base::extract;
4334 using Base::merge;
4335 using Base::swap;
4336 using Base::rehash;
4337 using Base::reserve;
4338 using Base::at;
4339 using Base::contains;
4340 using Base::count;
4341 using Base::equal_range;
4342 using Base::find;
4343 using Base::operator[];
4344 using Base::bucket_count;
4345 using Base::load_factor;
4346 using Base::max_load_factor;
4347 using Base::get_allocator;
4348 using Base::hash_function;
4349 using Base::hash;
4350 using Base::key_eq;
4351};
4352
4353// -----------------------------------------------------------------------------
4354// phmap::node_hash_set
4355// -----------------------------------------------------------------------------
4356// An `phmap::node_hash_set<T>` is an unordered associative container which
4357// has been optimized for both speed and memory footprint in most common use
4358// cases. Its interface is similar to that of `std::unordered_set<T>` with the
4359// following notable differences:
4360//
4361// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
4362// `insert()`, provided that the map is provided a compatible heterogeneous
4363// hashing function and equality operator.
4364// * Contains a `capacity()` member function indicating the number of element
4365// slots (open, deleted, and empty) within the hash set.
4366// * Returns `void` from the `erase(iterator)` overload.
4367// -----------------------------------------------------------------------------
4368template <class T, class Hash, class Eq, class Alloc> // default values in phmap_fwd_decl.h
4371 phmap::priv::NodeHashSetPolicy<T>, Hash, Eq, Alloc>
4372{
4373 using Base = typename node_hash_set::raw_hash_set;
4374
4375public:
4376 node_hash_set() {}
4377#ifdef __INTEL_COMPILER
4378 using Base::raw_hash_set;
4379#else
4380 using Base::Base;
4381#endif
4382 using Base::begin;
4383 using Base::cbegin;
4384 using Base::cend;
4385 using Base::end;
4386 using Base::capacity;
4387 using Base::empty;
4388 using Base::max_size;
4389 using Base::size;
4390 using Base::clear;
4391 using Base::erase;
4392 using Base::insert;
4393 using Base::emplace;
4394 using Base::emplace_hint;
4395 using Base::extract;
4396 using Base::merge;
4397 using Base::swap;
4398 using Base::rehash;
4399 using Base::reserve;
4400 using Base::contains;
4401 using Base::count;
4402 using Base::equal_range;
4403 using Base::find;
4404 using Base::bucket_count;
4405 using Base::load_factor;
4406 using Base::max_load_factor;
4407 using Base::get_allocator;
4408 using Base::hash_function;
4409 using Base::hash;
4410 using Base::key_eq;
4411 typename Base::hasher hash_funct() { return this->hash_function(); }
4412 void resize(typename Base::size_type hint) { this->rehash(hint); }
4413};
4414
4415// -----------------------------------------------------------------------------
4416// phmap::node_hash_map
4417// -----------------------------------------------------------------------------
4418//
4419// An `phmap::node_hash_map<K, V>` is an unordered associative container which
4420// has been optimized for both speed and memory footprint in most common use
4421// cases. Its interface is similar to that of `std::unordered_map<K, V>` with
4422// the following notable differences:
4423//
4424// * Supports heterogeneous lookup, through `find()`, `operator[]()` and
4425// `insert()`, provided that the map is provided a compatible heterogeneous
4426// hashing function and equality operator.
4427// * Contains a `capacity()` member function indicating the number of element
4428// slots (open, deleted, and empty) within the hash map.
4429// * Returns `void` from the `erase(iterator)` overload.
4430// -----------------------------------------------------------------------------
4431template <class Key, class Value, class Hash, class Eq, class Alloc> // default values in phmap_fwd_decl.h
4434 phmap::priv::NodeHashMapPolicy<Key, Value>, Hash, Eq,
4435 Alloc>
4436{
4437 using Base = typename node_hash_map::raw_hash_map;
4438
4439public:
4440 node_hash_map() {}
4441#ifdef __INTEL_COMPILER
4442 using Base::raw_hash_map;
4443#else
4444 using Base::Base;
4445#endif
4446 using Base::begin;
4447 using Base::cbegin;
4448 using Base::cend;
4449 using Base::end;
4450 using Base::capacity;
4451 using Base::empty;
4452 using Base::max_size;
4453 using Base::size;
4454 using Base::clear;
4455 using Base::erase;
4456 using Base::insert;
4457 using Base::insert_or_assign;
4458 using Base::emplace;
4459 using Base::emplace_hint;
4460 using Base::try_emplace;
4461 using Base::extract;
4462 using Base::merge;
4463 using Base::swap;
4464 using Base::rehash;
4465 using Base::reserve;
4466 using Base::at;
4467 using Base::contains;
4468 using Base::count;
4469 using Base::equal_range;
4470 using Base::find;
4471 using Base::operator[];
4472 using Base::bucket_count;
4473 using Base::load_factor;
4474 using Base::max_load_factor;
4475 using Base::get_allocator;
4476 using Base::hash_function;
4477 using Base::hash;
4478 using Base::key_eq;
4479 typename Base::hasher hash_funct() { return this->hash_function(); }
4480 void resize(typename Base::size_type hint) { this->rehash(hint); }
4481};
4482
4483// -----------------------------------------------------------------------------
4484// phmap::parallel_flat_hash_set
4485// -----------------------------------------------------------------------------
4486template <class T, class Hash, class Eq, class Alloc, size_t N, class Mtx_> // default values in phmap_fwd_decl.h
4489 N, phmap::priv::raw_hash_set, Mtx_,
4490 phmap::priv::FlatHashSetPolicy<T>,
4491 Hash, Eq, Alloc>
4492{
4493 using Base = typename parallel_flat_hash_set::parallel_hash_set;
4494
4495public:
4497#ifdef __INTEL_COMPILER
4498 using Base::parallel_hash_set;
4499#else
4500 using Base::Base;
4501#endif
4502 using Base::hash;
4503 using Base::subidx;
4504 using Base::subcnt;
4505 using Base::begin;
4506 using Base::cbegin;
4507 using Base::cend;
4508 using Base::end;
4509 using Base::capacity;
4510 using Base::empty;
4511 using Base::max_size;
4512 using Base::size;
4513 using Base::clear;
4514 using Base::erase;
4515 using Base::insert;
4516 using Base::emplace;
4517 using Base::emplace_hint;
4518 using Base::emplace_with_hash;
4519 using Base::emplace_hint_with_hash;
4520 using Base::extract;
4521 using Base::merge;
4522 using Base::swap;
4523 using Base::rehash;
4524 using Base::reserve;
4525 using Base::contains;
4526 using Base::count;
4527 using Base::equal_range;
4528 using Base::find;
4529 using Base::bucket_count;
4530 using Base::load_factor;
4531 using Base::max_load_factor;
4532 using Base::get_allocator;
4533 using Base::hash_function;
4534 using Base::key_eq;
4535};
4536
4537// -----------------------------------------------------------------------------
4538// phmap::parallel_flat_hash_map - default values in phmap_fwd_decl.h
4539// -----------------------------------------------------------------------------
4540template <class K, class V, class Hash, class Eq, class Alloc, size_t N, class Mtx_>
4542 N, phmap::priv::raw_hash_set, Mtx_,
4543 phmap::priv::FlatHashMapPolicy<K, V>,
4544 Hash, Eq, Alloc>
4545{
4546 using Base = typename parallel_flat_hash_map::parallel_hash_map;
4547
4548public:
4550#ifdef __INTEL_COMPILER
4551 using Base::parallel_hash_map;
4552#else
4553 using Base::Base;
4554#endif
4555 using Base::hash;
4556 using Base::subidx;
4557 using Base::subcnt;
4558 using Base::begin;
4559 using Base::cbegin;
4560 using Base::cend;
4561 using Base::end;
4562 using Base::capacity;
4563 using Base::empty;
4564 using Base::max_size;
4565 using Base::size;
4566 using Base::clear;
4567 using Base::erase;
4568 using Base::insert;
4569 using Base::insert_or_assign;
4570 using Base::emplace;
4571 using Base::emplace_hint;
4572 using Base::try_emplace;
4573 using Base::emplace_with_hash;
4574 using Base::emplace_hint_with_hash;
4575 using Base::try_emplace_with_hash;
4576 using Base::extract;
4577 using Base::merge;
4578 using Base::swap;
4579 using Base::rehash;
4580 using Base::reserve;
4581 using Base::at;
4582 using Base::contains;
4583 using Base::count;
4584 using Base::equal_range;
4585 using Base::find;
4586 using Base::operator[];
4587 using Base::bucket_count;
4588 using Base::load_factor;
4589 using Base::max_load_factor;
4590 using Base::get_allocator;
4591 using Base::hash_function;
4592 using Base::key_eq;
4593};
4594
4595// -----------------------------------------------------------------------------
4596// phmap::parallel_node_hash_set
4597// -----------------------------------------------------------------------------
4598template <class T, class Hash, class Eq, class Alloc, size_t N, class Mtx_>
4601 N, phmap::priv::raw_hash_set, Mtx_,
4602 phmap::priv::NodeHashSetPolicy<T>, Hash, Eq, Alloc>
4603{
4604 using Base = typename parallel_node_hash_set::parallel_hash_set;
4605
4606public:
4608#ifdef __INTEL_COMPILER
4609 using Base::parallel_hash_set;
4610#else
4611 using Base::Base;
4612#endif
4613 using Base::hash;
4614 using Base::subidx;
4615 using Base::subcnt;
4616 using Base::begin;
4617 using Base::cbegin;
4618 using Base::cend;
4619 using Base::end;
4620 using Base::capacity;
4621 using Base::empty;
4622 using Base::max_size;
4623 using Base::size;
4624 using Base::clear;
4625 using Base::erase;
4626 using Base::insert;
4627 using Base::emplace;
4628 using Base::emplace_hint;
4629 using Base::emplace_with_hash;
4630 using Base::emplace_hint_with_hash;
4631 using Base::extract;
4632 using Base::merge;
4633 using Base::swap;
4634 using Base::rehash;
4635 using Base::reserve;
4636 using Base::contains;
4637 using Base::count;
4638 using Base::equal_range;
4639 using Base::find;
4640 using Base::bucket_count;
4641 using Base::load_factor;
4642 using Base::max_load_factor;
4643 using Base::get_allocator;
4644 using Base::hash_function;
4645 using Base::key_eq;
4646 typename Base::hasher hash_funct() { return this->hash_function(); }
4647 void resize(typename Base::size_type hint) { this->rehash(hint); }
4648};
4649
4650// -----------------------------------------------------------------------------
4651// phmap::parallel_node_hash_map
4652// -----------------------------------------------------------------------------
4653template <class Key, class Value, class Hash, class Eq, class Alloc, size_t N, class Mtx_>
4656 N, phmap::priv::raw_hash_set, Mtx_,
4657 phmap::priv::NodeHashMapPolicy<Key, Value>, Hash, Eq,
4658 Alloc>
4659{
4660 using Base = typename parallel_node_hash_map::parallel_hash_map;
4661
4662public:
4664#ifdef __INTEL_COMPILER
4665 using Base::parallel_hash_map;
4666#else
4667 using Base::Base;
4668#endif
4669 using Base::hash;
4670 using Base::subidx;
4671 using Base::subcnt;
4672 using Base::begin;
4673 using Base::cbegin;
4674 using Base::cend;
4675 using Base::end;
4676 using Base::capacity;
4677 using Base::empty;
4678 using Base::max_size;
4679 using Base::size;
4680 using Base::clear;
4681 using Base::erase;
4682 using Base::insert;
4683 using Base::insert_or_assign;
4684 using Base::emplace;
4685 using Base::emplace_hint;
4686 using Base::try_emplace;
4687 using Base::emplace_with_hash;
4688 using Base::emplace_hint_with_hash;
4689 using Base::try_emplace_with_hash;
4690 using Base::extract;
4691 using Base::merge;
4692 using Base::swap;
4693 using Base::rehash;
4694 using Base::reserve;
4695 using Base::at;
4696 using Base::contains;
4697 using Base::count;
4698 using Base::equal_range;
4699 using Base::find;
4700 using Base::operator[];
4701 using Base::bucket_count;
4702 using Base::load_factor;
4703 using Base::max_load_factor;
4704 using Base::get_allocator;
4705 using Base::hash_function;
4706 using Base::key_eq;
4707 typename Base::hasher hash_funct() { return this->hash_function(); }
4708 void resize(typename Base::size_type hint) { this->rehash(hint); }
4709};
4710
4711} // namespace phmap
4712
4713#ifdef _MSC_VER
4714 #pragma warning(pop)
4715#endif
4716
4717
4718#endif // phmap_h_guard_
Definition phmap_base.h:5044
Definition phmap.h:4308
Definition phmap.h:4246
Definition phmap.h:4436
Definition phmap.h:4372
Definition phmap.h:4545
Definition phmap.h:4492
Definition phmap.h:4659
Definition phmap.h:4603
Definition phmap.h:168
Definition phmap_base.h:4293
Definition phmap.h:590
Definition phmap.h:603
Definition phmap.h:4017
Definition phmap.h:3450
Definition phmap.h:2359
Definition phmap.h:82
Definition phmap.h:2180
Definition phmap.h:762
GLM_FUNC_DECL GLM_CONSTEXPR genType zero()
Definition constants.inl:6
int64 int64_t
Definition fwd.hpp:85
uint32 uint32_t
Definition fwd.hpp:131
Definition phmap_utils.h:149
Definition phmap_base.h:1379
Definition phmap_base.h:224
Definition phmap_base.h:906
Definition phmap.h:3891
Definition phmap.h:3850
Definition phmap.h:398
Definition phmap_fwd_decl.h:48
Definition phmap.h:576
Definition phmap.h:120
Definition phmap.h:3975
Definition phmap.h:109
Definition phmap_base.h:4641
Definition phmap.h:3931
Definition phmap_base.h:4623