RealTimeTransport 1.0.0
Real-time simulation of quantum transport processes
Loading...
Searching...
No Matches
boost_unordered.hpp
1// Copyright 2001, 2002 Peter Dimov and Multi Media Ltd
2// Copyright 2002, 2018-2022 Peter Dimov
3// Copyright 2002-2018 Peter Dimov
4// Copyright 2004 Pavel Vozenilek
5// Copyright 2005-2009 Daniel James
6// Copyright 2005-2014 Daniel James
7// Copyright 2006-2009 Emil Dotchevski and Reverge Studios, Inc
8// Copyright 2007, 2014 Peter Dimov
9// Copyright 2008-2009 Emil Dotchevski and Reverge Studios, Inc
10// Copyright 2008-2016 Daniel James
11// Copyright 2015 Ion Gaztanaga
12// Copyright 2017 Peter Dimov
13// Copyright 2017, 2018 Peter Dimov
14// Copyright 2017, 2022 Peter Dimov
15// Copyright 2018 Glen Joseph Fernandes
16// Copyright 2019, 2021 Peter Dimov
17// Copyright 2021 Peter Dimov
18// Copyright 2021, 2022 Peter Dimov
19// Copyright 2022 Christian Mazakas
20// Copyright 2022 Joaquin M Lopez Munoz
21// Copyright 2022 Peter Dimov
22// Copyright 2022, 2023 Peter Dimov
23// Copyright 2022-2023 Christian Mazakas
24// Copyright 2022-2023 Joaquin M Lopez Munoz
25// Copyright 2023 Christian Mazakas
26// Copyright Beman Dawes 2011
27//
28// Distributed under the Boost Software License, Version 1.0. (See accompanying
29// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
30#pragma once
31
32#include <bit>
33#include <climits>
34#include <cmath>
35#include <complex>
36#include <cstddef>
37#include <cstdint>
38#include <cstdio>
39#include <cstring>
40#include <exception>
41#include <functional>
42#include <initializer_list>
43#include <iosfwd>
44#include <iterator>
45#include <limits>
46#include <memory>
47#include <optional>
48#include <stdexcept>
49#include <string>
50#include <system_error>
51#include <tuple>
52#include <type_traits>
53#include <typeindex>
54#include <utility>
55#include <variant>
56
57#pragma once
58
59// This is a minimal header that contains only the small set
60// config entries needed to use boost::unordered, so that the
61// whole boost config lib doesn't need to be pulled in.
62#ifdef _MSC_VER
63# ifndef BOOST_MSVC
64# define BOOST_MSVC _MSC_VER
65# endif
66#elif defined __clang__
67# ifndef BOOST_CLANG
68# define BOOST_CLANG 1
69# endif
70#elif defined(__GNUC__)
71# ifndef BOOST_GCC
72# define BOOST_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
73# endif
74#endif
75
76#ifndef BOOST_FORCEINLINE
77# if defined(_MSC_VER)
78# define BOOST_FORCEINLINE __forceinline
79# elif defined(__GNUC__) && __GNUC__ > 3
80 // Clang also defines __GNUC__ (as 4)
81# define BOOST_FORCEINLINE inline __attribute__ ((__always_inline__))
82# else
83# define BOOST_FORCEINLINE inline
84# endif
85#endif
86
87#ifndef BOOST_NOINLINE
88# if defined(_MSC_VER)
89# define BOOST_NOINLINE __declspec(noinline)
90# elif defined(__GNUC__) && __GNUC__ > 3
91 // Clang also defines __GNUC__ (as 4)
92# if defined(__CUDACC__)
93 // nvcc doesn't always parse __noinline__,
94 // see: https://svn.boost.org/trac/boost/ticket/9392
95# define BOOST_NOINLINE __attribute__ ((noinline))
96# elif defined(__HIP__)
97 // See https://github.com/boostorg/config/issues/392
98# define BOOST_NOINLINE __attribute__ ((noinline))
99# else
100# define BOOST_NOINLINE __attribute__ ((__noinline__))
101# endif
102# else
103# define BOOST_NOINLINE
104# endif
105#endif
106
107#if defined(BOOST_GCC) || defined(BOOST_CLANG)
108# define BOOST_LIKELY(x) __builtin_expect(x, 1)
109# define BOOST_UNLIKELY(x) __builtin_expect(x, 0)
110# define BOOST_SYMBOL_VISIBLE __attribute__((__visibility__("default")))
111#else
112# define BOOST_SYMBOL_VISIBLE
113#endif
114
115#ifndef BOOST_LIKELY
116# define BOOST_LIKELY(x) x
117#endif
118#ifndef BOOST_UNLIKELY
119# define BOOST_UNLIKELY(x) x
120#endif
121
122#ifndef BOOST_NORETURN
123# if defined(_MSC_VER)
124# define BOOST_NORETURN __declspec(noreturn)
125# elif defined(__GNUC__) || defined(__CODEGEARC__) && defined(__clang__)
126# define BOOST_NORETURN __attribute__ ((__noreturn__))
127# elif defined(__has_attribute) && defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x5130)
128# if __has_attribute(noreturn)
129# define BOOST_NORETURN [[noreturn]]
130# endif
131# elif defined(__has_cpp_attribute)
132# if __has_cpp_attribute(noreturn)
133# define BOOST_NORETURN [[noreturn]]
134# endif
135# endif
136#endif
137
138#ifndef BOOST_NORETURN
139# define BOOST_NO_NORETURN
140# define BOOST_NORETURN
141#endif
142
143#if BOOST_MSVC
144 #if !defined(_CPPUNWIND) && !defined(BOOST_NO_EXCEPTIONS)
145 #define BOOST_NO_EXCEPTIONS
146 #endif
147 #if !defined(_CPPRTTI) && !defined(BOOST_NO_RTTI)
148 #define BOOST_NO_RTTI
149 #endif
150#elif BOOST_GCC
151 #if !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS)
152 #define BOOST_NO_EXCEPTIONS
153 #endif
154 #if !defined(__GXX_RTTI) && !defined(BOOST_NO_RTTI)
155 #define BOOST_NO_RTTI
156 #endif
157#elif BOOST_CLANG
158 #if !__has_feature(cxx_exceptions) && !defined(BOOST_NO_EXCEPTIONS)
159 #define BOOST_NO_EXCEPTIONS
160 #endif
161 #if !__has_feature(cxx_rtti) && !defined(BOOST_NO_RTTI)
162 #define BOOST_NO_RTTI
163 #endif
164#endif
165#ifndef BOOST_CURRENT_FUNCTION_HPP_INCLUDED
166#define BOOST_CURRENT_FUNCTION_HPP_INCLUDED
167
168// MS compatible compilers support #pragma once
169
170#if defined(_MSC_VER) && (_MSC_VER >= 1020)
171# pragma once
172#endif
173
174//
175// boost/current_function.hpp - BOOST_CURRENT_FUNCTION
176//
177// Copyright 2002-2018 Peter Dimov
178//
179// Distributed under the Boost Software License, Version 1.0.
180// See accompanying file LICENSE_1_0.txt or copy at
181// http://www.boost.org/LICENSE_1_0.txt
182//
183// http://www.boost.org/libs/assert
184//
185
186namespace boost
187{
188
189namespace detail
190{
191
192inline void current_function_helper()
193{
194
195#ifdef BOOST_DISABLE_CURRENT_FUNCTION
196
197# define BOOST_CURRENT_FUNCTION "(unknown)"
198
199#elif defined(__GNUC__) || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) || (defined(__ICC) && (__ICC >= 600)) || defined(__ghs__) || defined(__clang__)
200
201# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__
202
203#elif defined(__DMC__) && (__DMC__ >= 0x810)
204
205# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__
206
207#elif defined(__FUNCSIG__)
208
209# define BOOST_CURRENT_FUNCTION __FUNCSIG__
210
211#elif (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)) || (defined(__IBMCPP__) && (__IBMCPP__ >= 500))
212
213# define BOOST_CURRENT_FUNCTION __FUNCTION__
214
215#elif defined(__BORLANDC__) && (__BORLANDC__ >= 0x550)
216
217# define BOOST_CURRENT_FUNCTION __FUNC__
218
219#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
220
221# define BOOST_CURRENT_FUNCTION __func__
222
223#elif defined(__cplusplus) && (__cplusplus >= 201103)
224
225# define BOOST_CURRENT_FUNCTION __func__
226
227#else
228
229# define BOOST_CURRENT_FUNCTION "(unknown)"
230
231#endif
232
233}
234
235} // namespace detail
236
237} // namespace boost
238
239#endif // #ifndef BOOST_CURRENT_FUNCTION_HPP_INCLUDED
240//
241// boost/assert.hpp - BOOST_ASSERT(expr)
242// BOOST_ASSERT_MSG(expr, msg)
243// BOOST_VERIFY(expr)
244// BOOST_VERIFY_MSG(expr, msg)
245// BOOST_ASSERT_IS_VOID
246//
247// Copyright (c) 2001, 2002 Peter Dimov and Multi Media Ltd.
248// Copyright (c) 2007, 2014 Peter Dimov
249// Copyright (c) Beman Dawes 2011
250// Copyright (c) 2015 Ion Gaztanaga
251//
252// Distributed under the Boost Software License, Version 1.0.
253// See accompanying file LICENSE_1_0.txt or copy at
254// http://www.boost.org/LICENSE_1_0.txt
255//
256// Note: There are no include guards. This is intentional.
257//
258// See http://www.boost.org/libs/assert/assert.html for documentation.
259//
260
261//
262// Stop inspect complaining about use of 'assert':
263//
264// boostinspect:naassert_macro
265//
266
267//
268// BOOST_ASSERT, BOOST_ASSERT_MSG, BOOST_ASSERT_IS_VOID
269//
270
271#undef BOOST_ASSERT
272#undef BOOST_ASSERT_MSG
273#undef BOOST_ASSERT_IS_VOID
274
275#if defined(BOOST_DISABLE_ASSERTS) || ( defined(BOOST_ENABLE_ASSERT_DEBUG_HANDLER) && defined(NDEBUG) )
276
277# define BOOST_ASSERT(expr) ((void)0)
278# define BOOST_ASSERT_MSG(expr, msg) ((void)0)
279# define BOOST_ASSERT_IS_VOID
280
281#elif defined(BOOST_ENABLE_ASSERT_HANDLER) || ( defined(BOOST_ENABLE_ASSERT_DEBUG_HANDLER) && !defined(NDEBUG) )
282
283namespace boost
284{
285 void assertion_failed(char const * expr, char const * function, char const * file, long line); // user defined
286 void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line); // user defined
287} // namespace boost
288
289#define BOOST_ASSERT(expr) (BOOST_LIKELY(!!(expr))? ((void)0): ::boost::assertion_failed(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))
290#define BOOST_ASSERT_MSG(expr, msg) (BOOST_LIKELY(!!(expr))? ((void)0): ::boost::assertion_failed_msg(#expr, msg, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))
291
292#else
293
294# include <assert.h> // .h to support old libraries w/o <cassert> - effect is the same
295
296# define BOOST_ASSERT(expr) assert(expr)
297# define BOOST_ASSERT_MSG(expr, msg) assert((expr)&&(msg))
298#ifdef NDEBUG
299# define BOOST_ASSERT_IS_VOID
300#endif
301
302#endif
303
304//
305// BOOST_VERIFY, BOOST_VERIFY_MSG
306//
307
308#undef BOOST_VERIFY
309#undef BOOST_VERIFY_MSG
310
311#if defined(BOOST_DISABLE_ASSERTS) || ( !defined(BOOST_ENABLE_ASSERT_HANDLER) && defined(NDEBUG) )
312
313# define BOOST_VERIFY(expr) ((void)(expr))
314# define BOOST_VERIFY_MSG(expr, msg) ((void)(expr))
315
316#else
317
318# define BOOST_VERIFY(expr) BOOST_ASSERT(expr)
319# define BOOST_VERIFY_MSG(expr, msg) BOOST_ASSERT_MSG(expr,msg)
320
321#endif
322/*
323Copyright 2018 Glen Joseph Fernandes
324(glenjofe@gmail.com)
325
326Distributed under the Boost Software License, Version 1.0.
327(http://www.boost.org/LICENSE_1_0.txt)
328*/
329#ifndef BOOST_CORE_EMPTY_VALUE_HPP
330#define BOOST_CORE_EMPTY_VALUE_HPP
331
332#ifdef _MSC_VER
333#pragma warning(push)
334#pragma warning(disable:4510)
335#endif
336
337namespace boost {
338
339template<class T>
340struct use_empty_value_base {
341 enum {
342 value = __is_empty(T) && !__is_final(T)
343 };
344};
345
346struct empty_init_t { };
347
348namespace empty_ {
349
350template<class T, unsigned N = 0,
351 bool E = boost::use_empty_value_base<T>::value>
352class empty_value {
353public:
354 typedef T type;
355
356 empty_value() = default;
357
358 constexpr empty_value(boost::empty_init_t)
359 : value_() { }
360
361 template<class U, class... Args>
362 constexpr empty_value(boost::empty_init_t, U&& value, Args&&... args)
363 : value_(std::forward<U>(value), std::forward<Args>(args)...) { }
364
365 constexpr const T& get() const noexcept {
366 return value_;
367 }
368
369 constexpr T& get() noexcept {
370 return value_;
371 }
372
373private:
374 T value_;
375};
376
377template<class T, unsigned N>
378class empty_value<T, N, true>
379 : T {
380public:
381 typedef T type;
382
383 empty_value() = default;
384
385 constexpr empty_value(boost::empty_init_t)
386 : T() { }
387
388 template<class U, class... Args>
389 constexpr empty_value(boost::empty_init_t, U&& value, Args&&... args)
390 : T(std::forward<U>(value), std::forward<Args>(args)...) { }
391
392 constexpr const T& get() const noexcept {
393 return *this;
394 }
395
396 constexpr T& get() noexcept {
397 return *this;
398 }
399};
400
401}
402
403using empty_::empty_value;
404
405inline constexpr empty_init_t empty_init = empty_init_t();
406
407}
408
409#ifdef _MSC_VER
410#pragma warning(pop)
411#endif
412
413#endif
414#ifndef BOOST_CORE_NO_EXCEPTIONS_SUPPORT_HPP
415#define BOOST_CORE_NO_EXCEPTIONS_SUPPORT_HPP
416
417#ifdef _MSC_VER
418# pragma once
419#endif
420
421//----------------------------------------------------------------------
422// (C) Copyright 2004 Pavel Vozenilek.
423// Use, modification and distribution is subject to the Boost Software
424// License, Version 1.0. (See accompanying file LICENSE_1_0.txt
425// or copy at http://www.boost.org/LICENSE_1_0.txt)
426//
427//
428// This file contains helper macros used when exception support may be
429// disabled (as indicated by macro BOOST_NO_EXCEPTIONS).
430//
431// Before picking up these macros you may consider using RAII techniques
432// to deal with exceptions - their syntax can be always the same with
433// or without exception support enabled.
434//----------------------------------------------------------------------
435
436#if !(defined BOOST_NO_EXCEPTIONS)
437# define BOOST_TRY { try
438# define BOOST_CATCH(x) catch(x)
439# define BOOST_RETHROW throw;
440# define BOOST_CATCH_END }
441#else
442# if !defined(BOOST_MSVC) || BOOST_MSVC >= 1900
443# define BOOST_TRY { if (true)
444# define BOOST_CATCH(x) else if (false)
445# else
446 // warning C4127: conditional expression is constant
447# define BOOST_TRY {
448 __pragma(warning(push))
449 __pragma(warning(disable: 4127))
450 if (true)
451 __pragma(warning(pop))
452# define BOOST_CATCH(x) else
453 __pragma(warning(push))
454 __pragma(warning(disable: 4127))
455 if (false)
456 __pragma(warning(pop))
457# endif
458# define BOOST_RETHROW
459# define BOOST_CATCH_END }
460#endif
461
462#endif
463/* 32b/64b xmx mix function.
464 *
465 * Copyright 2022 Peter Dimov.
466 * Copyright 2022 Joaquin M Lopez Munoz.
467 * Distributed under the Boost Software License, Version 1.0.
468 * (See accompanying file LICENSE_1_0.txt or copy at
469 * http://www.boost.org/LICENSE_1_0.txt)
470 *
471 * See https://www.boost.org/libs/unordered for library home page.
472 */
473
474#ifndef BOOST_UNORDERED_DETAIL_XMX_HPP
475#define BOOST_UNORDERED_DETAIL_XMX_HPP
476
477namespace boost{
478namespace unordered{
479namespace detail{
480
481/* Bit mixer for improvement of statistical properties of hash functions.
482 * The implementation is different on 64bit and 32bit architectures:
483 *
484 * - 64bit: same as xmx function in
485 * http://jonkagstrom.com/bit-mixer-construction/index.html
486 * - 32bit: generated by Hash Function Prospector
487 * (https://github.com/skeeto/hash-prospector) and selected as the
488 * best overall performer in benchmarks of Boost.Unordered flat containers.
489 * Score assigned by Hash Prospector: 333.7934929677524
490 */
491
492#ifdef SIZE_MAX
493#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0
494#define BOOST_UNORDERED_64B_ARCHITECTURE
495#endif
496#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */
497#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0
498#define BOOST_UNORDERED_64B_ARCHITECTURE
499#endif
500#endif
501
502static inline std::size_t xmx(std::size_t x)noexcept
503{
504#ifdef BOOST_UNORDERED_64B_ARCHITECTURE
505
506 std::uint64_t z=(std::uint64_t)x;
507
508 z^=z>>23;
509 z*=0xff51afd7ed558ccdull;
510 z^=z>>23;
511
512 return (std::size_t)z;
513
514#else /* 32 bits assumed */
515
516 x^=x>>18;
517 x*=0x56b5aaadu;
518 x^=x>>16;
519
520 return x;
521
522#endif
523}
524
525#ifdef BOOST_UNORDERED_64B_ARCHITECTURE
526#undef BOOST_UNORDERED_64B_ARCHITECTURE
527#endif
528
529}
530}
531}
532
533#endif
534#ifndef BOOST_UNORDERED_DETAIL_MULX_HPP
535#define BOOST_UNORDERED_DETAIL_MULX_HPP
536
537// Copyright 2022 Peter Dimov.
538// Copyright 2022 Joaquin M Lopez Munoz.
539// Distributed under the Boost Software License, Version 1.0.
540// https://www.boost.org/LICENSE_1_0.txt)
541
542#if defined(_MSC_VER) && !defined(__clang__)
543# include <intrin.h>
544#endif
545
546namespace boost {
547namespace unordered {
548namespace detail {
549
550// Bit mixer based on the mulx primitive
551
552#if defined(_MSC_VER) && defined(_M_X64) && !defined(__clang__)
553
554__forceinline std::uint64_t mulx64( std::uint64_t x, std::uint64_t y )
555{
556 std::uint64_t r2;
557 std::uint64_t r = _umul128( x, y, &r2 );
558 return r ^ r2;
559}
560
561#elif defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
562
563__forceinline std::uint64_t mulx64( std::uint64_t x, std::uint64_t y )
564{
565 std::uint64_t r = x * y;
566 std::uint64_t r2 = __umulh( x, y );
567 return r ^ r2;
568}
569
570#elif defined(__SIZEOF_INT128__)
571
572inline std::uint64_t mulx64( std::uint64_t x, std::uint64_t y )
573{
574 __uint128_t r = (__uint128_t)x * y;
575 return (std::uint64_t)r ^ (std::uint64_t)( r >> 64 );
576}
577
578#else
579
580inline std::uint64_t mulx64( std::uint64_t x, std::uint64_t y )
581{
582 std::uint64_t x1 = (std::uint32_t)x;
583 std::uint64_t x2 = x >> 32;
584
585 std::uint64_t y1 = (std::uint32_t)y;
586 std::uint64_t y2 = y >> 32;
587
588 std::uint64_t r3 = x2 * y2;
589
590 std::uint64_t r2a = x1 * y2;
591
592 r3 += r2a >> 32;
593
594 std::uint64_t r2b = x2 * y1;
595
596 r3 += r2b >> 32;
597
598 std::uint64_t r1 = x1 * y1;
599
600 std::uint64_t r2 = (r1 >> 32) + (std::uint32_t)r2a + (std::uint32_t)r2b;
601
602 r1 = (r2 << 32) + (std::uint32_t)r1;
603 r3 += r2 >> 32;
604
605 return r1 ^ r3;
606}
607
608#endif
609
610inline std::uint32_t mulx32( std::uint32_t x, std::uint32_t y )
611{
612 std::uint64_t r = (std::uint64_t)x * y;
613
614#ifdef __MSVC_RUNTIME_CHECKS
615
616 return (std::uint32_t)(r & UINT32_MAX) ^ (std::uint32_t)(r >> 32);
617
618#else
619
620 return (std::uint32_t)r ^ (std::uint32_t)(r >> 32);
621
622#endif
623}
624
625#ifdef SIZE_MAX
626#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0
627#define BOOST_UNORDERED_64B_ARCHITECTURE
628#endif
629#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */
630#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0
631#define BOOST_UNORDERED_64B_ARCHITECTURE
632#endif
633#endif
634
635inline std::size_t mulx( std::size_t x ) noexcept
636{
637#ifdef BOOST_UNORDERED_64B_ARCHITECTURE
638
639 // multiplier is phi
640 return (std::size_t)mulx64( (std::uint64_t)x, 0x9E3779B97F4A7C15ull );
641
642#else /* 32 bits assumed */
643
644 // multiplier from https://arxiv.org/abs/2001.05304
645 return mulx32( x, 0xE817FB2Du );
646
647#endif
648}
649
650#ifdef BOOST_UNORDERED_64B_ARCHITECTURE
651#undef BOOST_UNORDERED_64B_ARCHITECTURE
652#endif
653
654} // namespace detail
655} // namespace unordered
656} // namespace boost
657
658#endif // #ifndef BOOST_UNORDERED_DETAIL_MULX_HPP
659/* Hash function characterization.
660 *
661 * Copyright 2022 Joaquin M Lopez Munoz.
662 * Distributed under the Boost Software License, Version 1.0.
663 * (See accompanying file LICENSE_1_0.txt or copy at
664 * http://www.boost.org/LICENSE_1_0.txt)
665 *
666 * See https://www.boost.org/libs/unordered for library home page.
667 */
668
669#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP
670#define BOOST_UNORDERED_HASH_TRAITS_HPP
671
672namespace boost{
673namespace unordered{
674
675namespace detail{
676
677template<typename Hash,typename=void>
678struct hash_is_avalanching_impl: std::false_type{};
679
680template<typename Hash>
681struct hash_is_avalanching_impl<Hash,
682 typename std::void_t<typename Hash::is_avalanching>>:
683 std::true_type{};
684
685}
686
687/* Each trait can be partially specialized by users for concrete hash functions
688 * when actual characterization differs from default.
689 */
690
691/* hash_is_avalanching<Hash>::value is true when the type Hash::is_avalanching
692 * is present, false otherwise.
693 */
694template<typename Hash>
695struct hash_is_avalanching: detail::hash_is_avalanching_impl<Hash>::type{};
696
697}
698}
699
700#endif
701/* Fast open-addressing hash table.
702 *
703 * Copyright 2022-2023 Joaquin M Lopez Munoz.
704 * Copyright 2023 Christian Mazakas.
705 * Distributed under the Boost Software License, Version 1.0.
706 * (See accompanying file LICENSE_1_0.txt or copy at
707 * http://www.boost.org/LICENSE_1_0.txt)
708 *
709 * See https://www.boost.org/libs/unordered for library home page.
710 */
711
712#ifndef BOOST_UNORDERED_DETAIL_FOA_HPP
713#define BOOST_UNORDERED_DETAIL_FOA_HPP
714
715#ifndef BOOST_UNORDERED_DISABLE_SSE2
716#if defined(BOOST_UNORDERED_ENABLE_SSE2)||
717 defined(__SSE2__)||
718 defined(_M_X64)||(defined(_M_IX86_FP)&&_M_IX86_FP>=2)
719#define BOOST_UNORDERED_SSE2
720#endif
721#endif
722
723#ifndef BOOST_UNORDERED_DISABLE_NEON
724#if defined(BOOST_UNORDERED_ENABLE_NEON)||
725 (defined(__ARM_NEON)&&!defined(__ARM_BIG_ENDIAN))
726#define BOOST_UNORDERED_LITTLE_ENDIAN_NEON
727#endif
728#endif
729
730#ifdef BOOST_UNORDERED_SSE2
731#include <emmintrin.h>
732#elif defined(BOOST_UNORDERED_LITTLE_ENDIAN_NEON)
733#include <arm_neon.h>
734#endif
735
736#ifdef __has_builtin
737#define BOOST_UNORDERED_HAS_BUILTIN(x) __has_builtin(x)
738#else
739#define BOOST_UNORDERED_HAS_BUILTIN(x) 0
740#endif
741
742#ifndef NDEBUG
743#define BOOST_UNORDERED_ASSUME(cond) BOOST_ASSERT(cond)
744#elif BOOST_UNORDERED_HAS_BUILTIN(__builtin_assume)
745#define BOOST_UNORDERED_ASSUME(cond) __builtin_assume(cond)
746#elif defined(__GNUC__) || BOOST_UNORDERED_HAS_BUILTIN(__builtin_unreachable)
747#define BOOST_UNORDERED_ASSUME(cond)
748 do{
749 if(!(cond))__builtin_unreachable();
750 }while(0)
751#elif defined(_MSC_VER)
752#define BOOST_UNORDERED_ASSUME(cond) __assume(cond)
753#else
754#define BOOST_UNORDERED_ASSUME(cond)
755 do{
756 static_cast<void>(false&&(cond));
757 }while(0)
758#endif
759
760#define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
761 static_assert(std::is_nothrow_swappable<Hash>::value,
762 "Template parameter Hash is required to be nothrow Swappable.");
763 static_assert(std::is_nothrow_swappable<Pred>::value,
764 "Template parameter Pred is required to be nothrow Swappable");
765
766// This is the only predef defined needed for boost::unordered, so pull it
767// out here so we don't need to include all of predef.
768#if
769 defined(__ARM_ARCH) || defined(__TARGET_ARCH_ARM) ||
770 defined(__TARGET_ARCH_THUMB) || defined(_M_ARM) ||
771 defined(__arm__) || defined(__arm64) || defined(__thumb__) ||
772 defined(_M_ARM64) || defined(__aarch64__) || defined(__AARCH64EL__) ||
773 defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) ||
774 defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) ||
775 defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) ||
776 defined(__ARM_ARCH_6KZ__) || defined(__ARM_ARCH_6T2__) ||
777 defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__) ||
778 defined(__ARM_ARCH_4T__) || defined(__ARM_ARCH_4__)
779#define BOOST_ARCH_ARM 1
780#else
781#define BOOST_ARCH_ARM 0
782#endif
783
784namespace boost{
785namespace unordered{
786namespace detail{
787namespace foa{
788
789static const std::size_t default_bucket_count = 0;
790
791/* foa::table is an open-addressing hash table serving as the foundational core
792 * of boost::unordered_flat_[map|set]. Its main internal design aspects are:
793 *
794 * - Element slots are logically split into groups of size N=15. The number
795 * of groups is always a power of two, so the number of allocated slots
796 is of the form (N*2^n)-1 (final slot reserved for a sentinel mark).
797 * - Positioning is done at the group level rather than the slot level, that
798 * is, for any given element its hash value is used to locate a group and
799 * insertion is performed on the first available element of that group;
800 * if the group is full (overflow), further groups are tried using
801 * quadratic probing.
802 * - Each group has an associated 16B metadata word holding reduced hash
803 * values and overflow information. Reduced hash values are used to
804 * accelerate lookup within the group by using 128-bit SIMD or 64-bit word
805 * operations.
806 */
807
808/* group15 controls metadata information of a group of N=15 element slots.
809 * The 16B metadata word is organized as follows (LSB depicted rightmost):
810 *
811 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
812 * |ofw|h14|h13|h13|h11|h10|h09|h08|h07|h06|h05|h04|h03|h02|h01|h00|
813 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
814 *
815 * hi is 0 if the i-th element slot is avalaible, 1 to mark a sentinel and,
816 * when the slot is occupied, a value in the range [2,255] obtained from the
817 * element's original hash value.
818 * ofw is the so-called overflow byte. If insertion of an element with hash
819 * value h is tried on a full group, then the (h%8)-th bit of the overflow
820 * byte is set to 1 and a further group is probed. Having an overflow byte
821 * brings two advantages:
822 *
823 * - There's no need to reserve a special value of hi to mark tombstone
824 * slots; each reduced hash value keeps then log2(254)=7.99 bits of the
825 * original hash (alternative approaches reserve one full bit to mark
826 * if the slot is available/deleted, so their reduced hash values are 7 bit
827 * strong only).
828 * - When doing an unsuccessful lookup (i.e. the element is not present in
829 * the table), probing stops at the first non-overflowed group. Having 8
830 * bits for signalling overflow makes it very likely that we stop at the
831 * current group (this happens when no element with the same (h%8) value
832 * has overflowed in the group), saving us an additional group check even
833 * under high-load/high-erase conditions. It is critical that hash
834 * reduction is invariant under modulo 8 (see maybe_caused_overflow).
835 *
836 * When looking for an element with hash value h, match(h) returns a bitmask
837 * signalling which slots have the same reduced hash value. If available,
838 * match uses SSE2 or (little endian) Neon 128-bit SIMD operations. On non-SIMD
839 * scenarios, the logical layout described above is physically mapped to two
840 * 64-bit words with *bit interleaving*, i.e. the least significant 16 bits of
841 * the first 64-bit word contain the least significant bits of each byte in the
842 * "logical" 128-bit word, and so forth. With this layout, match can be
843 * implemented with 4 ANDs, 3 shifts, 2 XORs, 1 OR and 1 NOT.
844 *
845 * group15 has no user-defined ctor so that it's a trivial type and can be
846 * initialized via memset etc. Where needed, group15::initialize sets the
847 * metadata to all zeros.
848 */
849
850#ifdef BOOST_UNORDERED_SSE2
851
852struct group15
853{
854 static constexpr int N=15;
855
856 struct dummy_group_type
857 {
858 alignas(16) unsigned char storage[N+1]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0};
859 };
860
861 inline void initialize(){m=_mm_setzero_si128();}
862
863 inline void set(std::size_t pos,std::size_t hash)
864 {
865 BOOST_ASSERT(pos<N);
866 at(pos)=reduced_hash(hash);
867 }
868
869 inline void set_sentinel()
870 {
871 at(N-1)=sentinel_;
872 }
873
874 inline bool is_sentinel(std::size_t pos)const
875 {
876 BOOST_ASSERT(pos<N);
877 return at(pos)==sentinel_;
878 }
879
880 inline void reset(std::size_t pos)
881 {
882 BOOST_ASSERT(pos<N);
883 at(pos)=available_;
884 }
885
886 static inline void reset(unsigned char* pc)
887 {
888 *pc=available_;
889 }
890
891 inline int match(std::size_t hash)const
892 {
893 return _mm_movemask_epi8(
894 _mm_cmpeq_epi8(m,_mm_set1_epi32(match_word(hash))))&0x7FFF;
895 }
896
897 inline bool is_not_overflowed(std::size_t hash)const
898 {
899 static constexpr unsigned char shift[]={1,2,4,8,16,32,64,128};
900
901 return !(overflow()&shift[hash%8]);
902 }
903
904 inline void mark_overflow(std::size_t hash)
905 {
906 overflow()|=static_cast<unsigned char>(1<<(hash%8));
907 }
908
909 static inline bool maybe_caused_overflow(unsigned char* pc)
910 {
911 std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
912 group15 *pg=reinterpret_cast<group15*>(pc-pos);
913 return !pg->is_not_overflowed(*pc);
914 };
915
916 inline int match_available()const
917 {
918 return _mm_movemask_epi8(
919 _mm_cmpeq_epi8(m,_mm_setzero_si128()))&0x7FFF;
920 }
921
922 inline int match_occupied()const
923 {
924 return (~match_available())&0x7FFF;
925 }
926
927 inline int match_really_occupied()const
928 {
929 return at(N-1)==sentinel_?match_occupied()&0x3FFF:match_occupied();
930 }
931
932private:
933 static constexpr unsigned char available_=0,
934 sentinel_=1;
935
936 inline static int match_word(std::size_t hash)
937 {
938 static constexpr std::uint32_t word[]=
939 {
940 0x08080808u,0x09090909u,0x02020202u,0x03030303u,0x04040404u,0x05050505u,0x06060606u,0x07070707u,
941 0x08080808u,0x09090909u,0x0A0A0A0Au,0x0B0B0B0Bu,0x0C0C0C0Cu,0x0D0D0D0Du,0x0E0E0E0Eu,0x0F0F0F0Fu,
942 0x10101010u,0x11111111u,0x12121212u,0x13131313u,0x14141414u,0x15151515u,0x16161616u,0x17171717u,
943 0x18181818u,0x19191919u,0x1A1A1A1Au,0x1B1B1B1Bu,0x1C1C1C1Cu,0x1D1D1D1Du,0x1E1E1E1Eu,0x1F1F1F1Fu,
944 0x20202020u,0x21212121u,0x22222222u,0x23232323u,0x24242424u,0x25252525u,0x26262626u,0x27272727u,
945 0x28282828u,0x29292929u,0x2A2A2A2Au,0x2B2B2B2Bu,0x2C2C2C2Cu,0x2D2D2D2Du,0x2E2E2E2Eu,0x2F2F2F2Fu,
946 0x30303030u,0x31313131u,0x32323232u,0x33333333u,0x34343434u,0x35353535u,0x36363636u,0x37373737u,
947 0x38383838u,0x39393939u,0x3A3A3A3Au,0x3B3B3B3Bu,0x3C3C3C3Cu,0x3D3D3D3Du,0x3E3E3E3Eu,0x3F3F3F3Fu,
948 0x40404040u,0x41414141u,0x42424242u,0x43434343u,0x44444444u,0x45454545u,0x46464646u,0x47474747u,
949 0x48484848u,0x49494949u,0x4A4A4A4Au,0x4B4B4B4Bu,0x4C4C4C4Cu,0x4D4D4D4Du,0x4E4E4E4Eu,0x4F4F4F4Fu,
950 0x50505050u,0x51515151u,0x52525252u,0x53535353u,0x54545454u,0x55555555u,0x56565656u,0x57575757u,
951 0x58585858u,0x59595959u,0x5A5A5A5Au,0x5B5B5B5Bu,0x5C5C5C5Cu,0x5D5D5D5Du,0x5E5E5E5Eu,0x5F5F5F5Fu,
952 0x60606060u,0x61616161u,0x62626262u,0x63636363u,0x64646464u,0x65656565u,0x66666666u,0x67676767u,
953 0x68686868u,0x69696969u,0x6A6A6A6Au,0x6B6B6B6Bu,0x6C6C6C6Cu,0x6D6D6D6Du,0x6E6E6E6Eu,0x6F6F6F6Fu,
954 0x70707070u,0x71717171u,0x72727272u,0x73737373u,0x74747474u,0x75757575u,0x76767676u,0x77777777u,
955 0x78787878u,0x79797979u,0x7A7A7A7Au,0x7B7B7B7Bu,0x7C7C7C7Cu,0x7D7D7D7Du,0x7E7E7E7Eu,0x7F7F7F7Fu,
956 0x80808080u,0x81818181u,0x82828282u,0x83838383u,0x84848484u,0x85858585u,0x86868686u,0x87878787u,
957 0x88888888u,0x89898989u,0x8A8A8A8Au,0x8B8B8B8Bu,0x8C8C8C8Cu,0x8D8D8D8Du,0x8E8E8E8Eu,0x8F8F8F8Fu,
958 0x90909090u,0x91919191u,0x92929292u,0x93939393u,0x94949494u,0x95959595u,0x96969696u,0x97979797u,
959 0x98989898u,0x99999999u,0x9A9A9A9Au,0x9B9B9B9Bu,0x9C9C9C9Cu,0x9D9D9D9Du,0x9E9E9E9Eu,0x9F9F9F9Fu,
960 0xA0A0A0A0u,0xA1A1A1A1u,0xA2A2A2A2u,0xA3A3A3A3u,0xA4A4A4A4u,0xA5A5A5A5u,0xA6A6A6A6u,0xA7A7A7A7u,
961 0xA8A8A8A8u,0xA9A9A9A9u,0xAAAAAAAAu,0xABABABABu,0xACACACACu,0xADADADADu,0xAEAEAEAEu,0xAFAFAFAFu,
962 0xB0B0B0B0u,0xB1B1B1B1u,0xB2B2B2B2u,0xB3B3B3B3u,0xB4B4B4B4u,0xB5B5B5B5u,0xB6B6B6B6u,0xB7B7B7B7u,
963 0xB8B8B8B8u,0xB9B9B9B9u,0xBABABABAu,0xBBBBBBBBu,0xBCBCBCBCu,0xBDBDBDBDu,0xBEBEBEBEu,0xBFBFBFBFu,
964 0xC0C0C0C0u,0xC1C1C1C1u,0xC2C2C2C2u,0xC3C3C3C3u,0xC4C4C4C4u,0xC5C5C5C5u,0xC6C6C6C6u,0xC7C7C7C7u,
965 0xC8C8C8C8u,0xC9C9C9C9u,0xCACACACAu,0xCBCBCBCBu,0xCCCCCCCCu,0xCDCDCDCDu,0xCECECECEu,0xCFCFCFCFu,
966 0xD0D0D0D0u,0xD1D1D1D1u,0xD2D2D2D2u,0xD3D3D3D3u,0xD4D4D4D4u,0xD5D5D5D5u,0xD6D6D6D6u,0xD7D7D7D7u,
967 0xD8D8D8D8u,0xD9D9D9D9u,0xDADADADAu,0xDBDBDBDBu,0xDCDCDCDCu,0xDDDDDDDDu,0xDEDEDEDEu,0xDFDFDFDFu,
968 0xE0E0E0E0u,0xE1E1E1E1u,0xE2E2E2E2u,0xE3E3E3E3u,0xE4E4E4E4u,0xE5E5E5E5u,0xE6E6E6E6u,0xE7E7E7E7u,
969 0xE8E8E8E8u,0xE9E9E9E9u,0xEAEAEAEAu,0xEBEBEBEBu,0xECECECECu,0xEDEDEDEDu,0xEEEEEEEEu,0xEFEFEFEFu,
970 0xF0F0F0F0u,0xF1F1F1F1u,0xF2F2F2F2u,0xF3F3F3F3u,0xF4F4F4F4u,0xF5F5F5F5u,0xF6F6F6F6u,0xF7F7F7F7u,
971 0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu,
972 };
973
974 return (int)word[static_cast<unsigned char>(hash)];
975 }
976
977 inline static unsigned char reduced_hash(std::size_t hash)
978 {
979 return static_cast<unsigned char>(match_word(hash));
980 }
981
982 inline unsigned char& at(std::size_t pos)
983 {
984 return reinterpret_cast<unsigned char*>(&m)[pos];
985 }
986
987 inline unsigned char at(std::size_t pos)const
988 {
989 return reinterpret_cast<const unsigned char*>(&m)[pos];
990 }
991
992 inline unsigned char& overflow()
993 {
994 return at(N);
995 }
996
997 inline unsigned char overflow()const
998 {
999 return at(N);
1000 }
1001
1002 alignas(16) __m128i m;
1003};
1004
1005#elif defined(BOOST_UNORDERED_LITTLE_ENDIAN_NEON)
1006
1007struct group15
1008{
1009 static constexpr int N=15;
1010
1011 struct dummy_group_type
1012 {
1013 alignas(16) unsigned char storage[N+1]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0};
1014 };
1015
1016 inline void initialize(){m=vdupq_n_s8(0);}
1017
1018 inline void set(std::size_t pos,std::size_t hash)
1019 {
1020 BOOST_ASSERT(pos<N);
1021 at(pos)=reduced_hash(hash);
1022 }
1023
1024 inline void set_sentinel()
1025 {
1026 at(N-1)=sentinel_;
1027 }
1028
1029 inline bool is_sentinel(std::size_t pos)const
1030 {
1031 BOOST_ASSERT(pos<N);
1032 return pos==N-1&&at(N-1)==sentinel_;
1033 }
1034
1035 inline void reset(std::size_t pos)
1036 {
1037 BOOST_ASSERT(pos<N);
1038 at(pos)=available_;
1039 }
1040
1041 static inline void reset(unsigned char* pc)
1042 {
1043 *pc=available_;
1044 }
1045
1046 inline int match(std::size_t hash)const
1047 {
1048 return simde_mm_movemask_epi8(vceqq_s8(
1049 m,vdupq_n_s8(static_cast<signed char>(reduced_hash(hash)))))&0x7FFF;
1050 }
1051
1052 inline bool is_not_overflowed(std::size_t hash)const
1053 {
1054 static constexpr unsigned char shift[]={1,2,4,8,16,32,64,128};
1055
1056 return !(overflow()&shift[hash%8]);
1057 }
1058
1059 inline void mark_overflow(std::size_t hash)
1060 {
1061 overflow()|=static_cast<unsigned char>(1<<(hash%8));
1062 }
1063
1064 static inline bool maybe_caused_overflow(unsigned char* pc)
1065 {
1066 std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
1067 group15 *pg=reinterpret_cast<group15*>(pc-pos);
1068 return !pg->is_not_overflowed(*pc);
1069 };
1070
1071 inline int match_available()const
1072 {
1073 return simde_mm_movemask_epi8(vceqq_s8(m,vdupq_n_s8(0)))&0x7FFF;
1074 }
1075
1076 inline int match_occupied()const
1077 {
1078 return simde_mm_movemask_epi8(
1079 vcgtq_u8(vreinterpretq_u8_s8(m),vdupq_n_u8(0)))&0x7FFF;
1080 }
1081
1082 inline int match_really_occupied()const
1083 {
1084 return at(N-1)==sentinel_?match_occupied()&0x3FFF:match_occupied();
1085 }
1086
1087private:
1088 static constexpr unsigned char available_=0,
1089 sentinel_=1;
1090
1091 inline static unsigned char reduced_hash(std::size_t hash)
1092 {
1093 static constexpr unsigned char table[]={
1094 8,9,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
1095 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
1096 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
1097 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
1098 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
1099 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
1100 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
1101 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
1102 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
1103 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
1104 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
1105 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
1106 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
1107 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
1108 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
1109 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
1110 };
1111
1112 return table[(unsigned char)hash];
1113 }
1114
1115 /* Copied from
1116 * https://github.com/simd-everywhere/simde/blob/master/simde/x86/sse2.h#L3763
1117 */
1118
1119 static inline int simde_mm_movemask_epi8(uint8x16_t a)
1120 {
1121 static constexpr uint8_t md[16]={
1122 1 << 0, 1 << 1, 1 << 2, 1 << 3,
1123 1 << 4, 1 << 5, 1 << 6, 1 << 7,
1124 1 << 0, 1 << 1, 1 << 2, 1 << 3,
1125 1 << 4, 1 << 5, 1 << 6, 1 << 7,
1126 };
1127
1128 uint8x16_t masked=vandq_u8(vld1q_u8(md),a);
1129 uint8x8x2_t tmp=vzip_u8(vget_low_u8(masked),vget_high_u8(masked));
1130 uint16x8_t x=vreinterpretq_u16_u8(vcombine_u8(tmp.val[0],tmp.val[1]));
1131
1132#ifdef __ARM_ARCH_ISA_A64
1133 return vaddvq_u16(x);
1134#else
1135 uint64x2_t t64=vpaddlq_u32(vpaddlq_u16(x));
1136 return int(vgetq_lane_u64(t64,0))+int(vgetq_lane_u64(t64,1));
1137#endif
1138 }
1139
1140 inline unsigned char& at(std::size_t pos)
1141 {
1142 return reinterpret_cast<unsigned char*>(&m)[pos];
1143 }
1144
1145 inline unsigned char at(std::size_t pos)const
1146 {
1147 return reinterpret_cast<const unsigned char*>(&m)[pos];
1148 }
1149
1150 inline unsigned char& overflow()
1151 {
1152 return at(N);
1153 }
1154
1155 inline unsigned char overflow()const
1156 {
1157 return at(N);
1158 }
1159
1160 alignas(16) int8x16_t m;
1161};
1162
1163#else /* non-SIMD */
1164
1165struct group15
1166{
1167 static constexpr int N=15;
1168
1169 struct dummy_group_type
1170 {
1171 alignas(16) std::uint64_t m[2]=
1172 {0x0000000000004000ull,0x0000000000000000ull};
1173 };
1174
1175 inline void initialize(){m[0]=0;m[1]=0;}
1176
1177 inline void set(std::size_t pos,std::size_t hash)
1178 {
1179 BOOST_ASSERT(pos<N);
1180 set_impl(pos,reduced_hash(hash));
1181 }
1182
1183 inline void set_sentinel()
1184 {
1185 set_impl(N-1,sentinel_);
1186 }
1187
1188 inline bool is_sentinel(std::size_t pos)const
1189 {
1190 BOOST_ASSERT(pos<N);
1191 return
1192 pos==N-1&&
1193 (m[0] & std::uint64_t(0x4000400040004000ull))==std::uint64_t(0x4000ull)&&
1194 (m[1] & std::uint64_t(0x4000400040004000ull))==0;
1195 }
1196
1197 inline void reset(std::size_t pos)
1198 {
1199 BOOST_ASSERT(pos<N);
1200 set_impl(pos,available_);
1201 }
1202
1203 static inline void reset(unsigned char* pc)
1204 {
1205 std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
1206 pc-=pos;
1207 reinterpret_cast<group15*>(pc)->reset(pos);
1208 }
1209
1210 inline int match(std::size_t hash)const
1211 {
1212 return match_impl(reduced_hash(hash));
1213 }
1214
1215 inline bool is_not_overflowed(std::size_t hash)const
1216 {
1217 return !(reinterpret_cast<const std::uint16_t*>(m)[hash%8] & 0x8000u);
1218 }
1219
1220 inline void mark_overflow(std::size_t hash)
1221 {
1222 reinterpret_cast<std::uint16_t*>(m)[hash%8]|=0x8000u;
1223 }
1224
1225 static inline bool maybe_caused_overflow(unsigned char* pc)
1226 {
1227 std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
1228 group15 *pg=reinterpret_cast<group15*>(pc-pos);
1229 std::uint64_t x=((pg->m[0])>>pos)&0x000100010001ull;
1230 std::uint32_t y=static_cast<std::uint32_t>(x|(x>>15)|(x>>30));
1231 return !pg->is_not_overflowed(y);
1232 };
1233
1234 inline int match_available()const
1235 {
1236 std::uint64_t x=~(m[0]|m[1]);
1237 std::uint32_t y=static_cast<std::uint32_t>(x&(x>>32));
1238 y&=y>>16;
1239 return y&0x7FFF;
1240 }
1241
1242 inline int match_occupied()const
1243 {
1244 std::uint64_t x=m[0]|m[1];
1245 std::uint32_t y=static_cast<std::uint32_t>(x|(x>>32));
1246 y|=y>>16;
1247 return y&0x7FFF;
1248 }
1249
1250 inline int match_really_occupied()const
1251 {
1252 return ~(match_impl(0)|match_impl(1))&0x7FFF;
1253 }
1254
1255private:
1256 static constexpr unsigned char available_=0,
1257 sentinel_=1;
1258
1259 inline static unsigned char reduced_hash(std::size_t hash)
1260 {
1261 static constexpr unsigned char table[]={
1262 8,9,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
1263 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
1264 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
1265 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
1266 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
1267 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
1268 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
1269 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
1270 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
1271 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
1272 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
1273 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
1274 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
1275 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
1276 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
1277 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
1278 };
1279
1280 return table[static_cast<unsigned char>(hash)];
1281 }
1282
1283 inline void set_impl(std::size_t pos,std::size_t n)
1284 {
1285 BOOST_ASSERT(n<256);
1286 set_impl(m[0],pos,n&0xFu);
1287 set_impl(m[1],pos,n>>4);
1288 }
1289
1290 static inline void set_impl(std::uint64_t& x,std::size_t pos,std::size_t n)
1291 {
1292 static constexpr std::uint64_t mask[]=
1293 {
1294 0x0000000000000000ull,0x0000000000000001ull,0x0000000000010000ull,
1295 0x0000000000010001ull,0x0000000100000000ull,0x0000000100000001ull,
1296 0x0000000100010000ull,0x0000000100010001ull,0x0001000000000000ull,
1297 0x0001000000000001ull,0x0001000000010000ull,0x0001000000010001ull,
1298 0x0001000100000000ull,0x0001000100000001ull,0x0001000100010000ull,
1299 0x0001000100010001ull,
1300 };
1301 static constexpr std::uint64_t imask[]=
1302 {
1303 0x0001000100010001ull,0x0001000100010000ull,0x0001000100000001ull,
1304 0x0001000100000000ull,0x0001000000010001ull,0x0001000000010000ull,
1305 0x0001000000000001ull,0x0001000000000000ull,0x0000000100010001ull,
1306 0x0000000100010000ull,0x0000000100000001ull,0x0000000100000000ull,
1307 0x0000000000010001ull,0x0000000000010000ull,0x0000000000000001ull,
1308 0x0000000000000000ull,
1309 };
1310
1311 BOOST_ASSERT(pos<16&&n<16);
1312 x|= mask[n]<<pos;
1313 x&=~(imask[n]<<pos);
1314 }
1315
1316 inline int match_impl(std::size_t n)const
1317 {
1318 static constexpr std::uint64_t mask[]=
1319 {
1320 0x0000000000000000ull,0x000000000000ffffull,0x00000000ffff0000ull,
1321 0x00000000ffffffffull,0x0000ffff00000000ull,0x0000ffff0000ffffull,
1322 0x0000ffffffff0000ull,0x0000ffffffffffffull,0xffff000000000000ull,
1323 0xffff00000000ffffull,0xffff0000ffff0000ull,0xffff0000ffffffffull,
1324 0xffffffff00000000ull,0xffffffff0000ffffull,0xffffffffffff0000ull,
1325 0xffffffffffffffffull,
1326 };
1327
1328 BOOST_ASSERT(n<256);
1329 std::uint64_t x=m[0]^mask[n&0xFu];
1330 x=~((m[1]^mask[n>>4])|x);
1331 std::uint32_t y=static_cast<std::uint32_t>(x&(x>>32));
1332 y&=y>>16;
1333 return y&0x7FFF;
1334 }
1335
1336 alignas(16) std::uint64_t m[2];
1337};
1338
1339#endif
1340
1341/* foa::table uses a size policy to obtain the permissible sizes of the group
1342 * array (and, by implication, the element array) and to do the hash->group
1343 * mapping.
1344 *
1345 * - size_index(n) returns an unspecified "index" number used in other policy
1346 * operations.
1347 * - size(size_index_) returns the number of groups for the given index. It is
1348 * guaranteed that size(size_index(n)) >= n.
1349 * - min_size() is the minimum number of groups permissible, i.e.
1350 * size(size_index(0)).
1351 * - position(hash,size_index_) maps hash to a position in the range
1352 * [0,size(size_index_)).
1353 *
1354 * The reason we're introducing the intermediate index value for calculating
1355 * sizes and positions is that it allows us to optimize the implementation of
1356 * position, which is in the hot path of lookup and insertion operations:
1357 * pow2_size_policy, the actual size policy used by foa::table, returns 2^n
1358 * (n>0) as permissible sizes and returns the n most significant bits
1359 * of the hash value as the position in the group array; using a size index
1360 * defined as i = (bits in std::size_t) - n, we have an unbeatable
1361 * implementation of position(hash) as hash>>i.
1362 * There's a twofold reason for choosing the high bits of hash for positioning:
1363 * - Multiplication-based mixing tends to yield better entropy in the high
1364 * part of its result.
1365 * - group15 reduced-hash values take the *low* bits of hash, and we want
1366 * these values and positioning to be as uncorrelated as possible.
1367 */
1368
1369struct pow2_size_policy
1370{
1371 static inline std::size_t size_index(std::size_t n)
1372 {
1373 // TODO: min size is 2, see if we can bring it down to 1 without loss
1374 // of performance
1375
1376 return sizeof(std::size_t)*CHAR_BIT-
1377 (n<=2?1:((std::size_t)(std::bit_width(n-1))));
1378 }
1379
1380 static inline std::size_t size(std::size_t size_index_)
1381 {
1382 return std::size_t(1)<<(sizeof(std::size_t)*CHAR_BIT-size_index_);
1383 }
1384
1385 static constexpr std::size_t min_size(){return 2;}
1386
1387 static inline std::size_t position(std::size_t hash,std::size_t size_index_)
1388 {
1389 return hash>>size_index_;
1390 }
1391};
1392
1393/* size index of a group array for a given *element* capacity */
1394
1395template<typename Group,typename SizePolicy>
1396static inline std::size_t size_index_for(std::size_t n)
1397{
1398 /* n/N+1 == ceil((n+1)/N) (extra +1 for the sentinel) */
1399 return SizePolicy::size_index(n/Group::N+1);
1400}
1401
1402/* Quadratic prober over a power-of-two range using triangular numbers.
1403 * mask in next(mask) must be the range size minus one (and since size is 2^n,
1404 * mask has exactly its n first bits set to 1).
1405 */
1406
1407struct pow2_quadratic_prober
1408{
1409 pow2_quadratic_prober(std::size_t pos_):pos{pos_}{}
1410
1411 inline std::size_t get()const{return pos;}
1412
1413 /* next returns false when the whole array has been traversed, which ends
1414 * probing (in practice, full-table probing will only happen with very small
1415 * arrays).
1416 */
1417
1418 inline bool next(std::size_t mask)
1419 {
1420 step+=1;
1421 pos=(pos+step)&mask;
1422 return step<=mask;
1423 }
1424
1425private:
1426 std::size_t pos,step=0;
1427};
1428
1429/* Mixing policies: no_mix is the identity function, xmx_mix uses the
1430 * xmx function defined in <boost/unordered/detail/xmx.hpp>, and mulx_mix
1431 * uses the mulx function from <boost/unordered/detail/mulx.hpp>.
1432 *
1433 * foa::table mixes hash results with mulx_mix unless the hash is marked as
1434 * avalanching, i.e. of good quality (see <boost/unordered/hash_traits.hpp>).
1435 */
1436
1437struct no_mix
1438{
1439 template<typename Hash,typename T>
1440 static inline std::size_t mix(const Hash& h,const T& x)
1441 {
1442 return h(x);
1443 }
1444};
1445
1446struct xmx_mix
1447{
1448 template<typename Hash,typename T>
1449 static inline std::size_t mix(const Hash& h,const T& x)
1450 {
1451 return xmx(h(x));
1452 }
1453};
1454
1455struct mulx_mix
1456{
1457 template<typename Hash,typename T>
1458 static inline std::size_t mix(const Hash& h,const T& x)
1459 {
1460 return mulx(h(x));
1461 }
1462};
1463
1464/* boost::core::countr_zero has a potentially costly check for
1465 * the case x==0.
1466 */
1467
1468inline unsigned int unchecked_countr_zero(int x)
1469{
1470#ifdef BOOST_MSVC
1471 unsigned long r;
1472 _BitScanForward(&r,(unsigned long)x);
1473 return (unsigned int)r;
1474#else
1476 return (unsigned int)std::countr_zero((unsigned int)x);
1477#endif
1478}
1479
1480template<typename,typename,typename,typename>
1481class table;
1482
1483/* table_iterator keeps two pointers:
1484 *
1485 * - A pointer p to the element slot.
1486 * - A pointer pc to the n-th byte of the associated group metadata, where n
1487 * is the position of the element in the group.
1488 *
1489 * A simpler solution would have been to keep a pointer p to the element, a
1490 * pointer pg to the group, and the position n, but that would increase
1491 * sizeof(table_iterator) by 4/8 bytes. In order to make this compact
1492 * representation feasible, it is required that group objects are aligned
1493 * to their size, so that we can recover pg and n as
1494 *
1495 * - n = pc%sizeof(group)
1496 * - pg = pc-n
1497 *
1498 * (for explanatory purposes pg and pc are treated above as if they were memory
1499 * addresses rather than pointers).The main drawback of this two-pointer
1500 * representation is that iterator increment is relatively slow.
1501 *
1502 * p = nullptr is conventionally used to mark end() iterators.
1503 */
1504
1505/* internal conversion from const_iterator to iterator */
1506class const_iterator_cast_tag {};
1507
1508template<typename TypePolicy,typename Group,bool Const>
1509class table_iterator
1510{
1511 using type_policy=TypePolicy;
1512 using table_element_type=typename type_policy::element_type;
1513
1514public:
1515 using difference_type=std::ptrdiff_t;
1516 using value_type=typename type_policy::value_type;
1517 using pointer=
1518 typename std::conditional<Const,value_type const*,value_type*>::type;
1519 using reference=
1520 typename std::conditional<Const,value_type const&,value_type&>::type;
1521 using iterator_category=std::forward_iterator_tag;
1522 using element_type=
1523 typename std::conditional<Const,value_type const,value_type>::type;
1524
1525 table_iterator()=default;
1526 template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
1527 table_iterator(const table_iterator<TypePolicy,Group,Const2>& x):
1528 pc{x.pc},p{x.p}{}
1529 table_iterator(
1530 const_iterator_cast_tag, const table_iterator<TypePolicy,Group,true>& x):
1531 pc{x.pc},p{x.p}{}
1532
1533 inline reference operator*()const noexcept{return type_policy::value_from(*p);}
1534 inline pointer operator->()const noexcept
1535 {return std::addressof(type_policy::value_from(*p));}
1536 inline table_iterator& operator++()noexcept{increment();return *this;}
1537 inline table_iterator operator++(int)noexcept
1538 {auto x=*this;increment();return x;}
1539 friend inline bool operator==(
1540 const table_iterator& x,const table_iterator& y)
1541 {return x.p==y.p;}
1542 friend inline bool operator!=(
1543 const table_iterator& x,const table_iterator& y)
1544 {return !(x==y);}
1545
1546private:
1547 template<typename,typename,bool> friend class table_iterator;
1548 template<typename,typename,typename,typename> friend class table;
1549
1550 table_iterator(Group* pg,std::size_t n,const table_element_type* p_):
1551 pc{reinterpret_cast<unsigned char*>(const_cast<Group*>(pg))+n},
1552 p{const_cast<table_element_type*>(p_)}
1553 {}
1554
1555 inline std::size_t rebase() noexcept
1556 {
1557 std::size_t off=reinterpret_cast<uintptr_t>(pc)%sizeof(Group);
1558 pc-=off;
1559 return off;
1560 }
1561
1562 inline void increment()noexcept
1563 {
1564 std::size_t n0=rebase();
1565
1566 int mask=(reinterpret_cast<Group*>(pc)->match_occupied()>>(n0+1))<<(n0+1);
1567 if(!mask){
1568 do{
1569 pc+=sizeof(Group);
1570 p+=Group::N;
1571 }
1572 while((mask=reinterpret_cast<Group*>(pc)->match_occupied())==0);
1573 }
1574
1575 auto n=unchecked_countr_zero(mask);
1576 if(BOOST_UNLIKELY(reinterpret_cast<Group*>(pc)->is_sentinel(n))){
1577 p=nullptr;
1578 }
1579 else{
1580 pc+=n;
1581 p-=n0;
1582 p+=n;
1583 }
1584 }
1585
1586 unsigned char *pc=nullptr;
1587 table_element_type *p=nullptr;
1588};
1589
1590/* table_arrays controls allocation, initialization and deallocation of
1591 * paired arrays of groups and element slots. Only one chunk of memory is
1592 * allocated to place both arrays: this is not done for efficiency reasons,
1593 * but in order to be able to properly align the group array without storing
1594 * additional offset information --the alignment required (16B) is usually
1595 * greater than alignof(std::max_align_t) and thus not guaranteed by
1596 * allocators.
1597 */
1598
1599template<typename Group,std::size_t Size>
1600Group* dummy_groups()
1601{
1602 /* Dummy storage initialized as if in an empty container (actually, each
1603 * of its groups is initialized like a separate empty container).
1604 * We make table_arrays::groups point to this when capacity()==0, so that
1605 * we are not allocating any dynamic memory and yet lookup can be implemented
1606 * without checking for groups==nullptr. This space won't ever be used for
1607 * insertion as the container's capacity is precisely zero.
1608 */
1609
1610 static constexpr typename Group::dummy_group_type
1611 storage[Size]={typename Group::dummy_group_type(),};
1612
1613 return reinterpret_cast<Group*>(
1614 const_cast<typename Group::dummy_group_type*>(storage));
1615}
1616
1617template<typename Value,typename Group,typename SizePolicy>
1618struct table_arrays
1619{
1620 using value_type=Value;
1621 using group_type=Group;
1622 static constexpr auto N=group_type::N;
1623 using size_policy=SizePolicy;
1624
1625 template<typename Allocator>
1626 static table_arrays new_(Allocator& al,std::size_t n)
1627 {
1628 using storage_allocator=
1629 typename std::allocator_traits<Allocator>::template rebind_alloc<Value>;
1630 using storage_traits=std::allocator_traits<storage_allocator>;
1631
1632 auto groups_size_index=size_index_for<group_type,size_policy>(n);
1633 auto groups_size=size_policy::size(groups_size_index);
1634 table_arrays arrays{groups_size_index,groups_size-1,nullptr,nullptr};
1635
1636 if(!n){
1637 arrays.groups=dummy_groups<group_type,size_policy::min_size()>();
1638 }
1639 else{
1640 auto sal=storage_allocator(al);
1641 arrays.elements=std::to_address(
1642 storage_traits::allocate(sal,buffer_size(groups_size)));
1643
1644 /* Align arrays.groups to sizeof(group_type). table_iterator critically
1645 * depends on such alignment for its increment operation.
1646 */
1647
1648 auto p=reinterpret_cast<unsigned char*>(arrays.elements+groups_size*N-1);
1649 p+=(uintptr_t(sizeof(group_type))-
1650 reinterpret_cast<uintptr_t>(p))%sizeof(group_type);
1651 arrays.groups=reinterpret_cast<group_type*>(p);
1652
1653 /* memset is faster/not slower than initializing groups individually.
1654 * This assumes all zeros is group_type's default layout.
1655 */
1656
1657 std::memset(arrays.groups,0,sizeof(group_type)*groups_size);
1658 arrays.groups[groups_size-1].set_sentinel();
1659 }
1660 return arrays;
1661 }
1662
1663 template<typename Allocator>
1664 static void delete_(Allocator& al,table_arrays& arrays)noexcept
1665 {
1666 using storage_alloc=typename std::allocator_traits<Allocator>::template rebind_alloc<Value>;
1667 using storage_traits=std::allocator_traits<storage_alloc>;
1668 using pointer=typename storage_traits::pointer;
1669 using pointer_traits=std::pointer_traits<pointer>;
1670
1671 auto sal=storage_alloc(al);
1672 if(arrays.elements){
1673 storage_traits::deallocate(
1674 sal,pointer_traits::pointer_to(*arrays.elements),
1675 buffer_size(arrays.groups_size_mask+1));
1676 }
1677 }
1678
1679 /* combined space for elements and groups measured in sizeof(value_type)s */
1680
1681 static std::size_t buffer_size(std::size_t groups_size)
1682 {
1683 auto buffer_bytes=
1684 /* space for elements (we subtract 1 because of the sentinel) */
1685 sizeof(value_type)*(groups_size*N-1)+
1686 /* space for groups + padding for group alignment */
1687 sizeof(group_type)*(groups_size+1)-1;
1688
1689 /* ceil(buffer_bytes/sizeof(value_type)) */
1690 return (buffer_bytes+sizeof(value_type)-1)/sizeof(value_type);
1691 }
1692
1693 std::size_t groups_size_index;
1694 std::size_t groups_size_mask;
1695 group_type *groups;
1696 value_type *elements;
1697};
1698
1699struct if_constexpr_void_else{void operator()()const{}};
1700
1701template<bool B,typename F,typename G=if_constexpr_void_else>
1702void if_constexpr(F f,G g={})
1703{
1704 std::get<B?0:1>(std::forward_as_tuple(f,g))();
1705}
1706
1707template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
1708void copy_assign_if(T& x,const T& y){x=y;}
1709
1710template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
1711void copy_assign_if(T&,const T&){}
1712
1713template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
1714void move_assign_if(T& x,T& y){x=std::move(y);}
1715
1716template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
1717void move_assign_if(T&,T&){}
1718
1719template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
1720void swap_if(T& x,T& y){using std::swap; swap(x,y);}
1721
1722template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
1723void swap_if(T&,T&){}
1724
1725inline void prefetch(const void* p)
1726{
1727 (void) p;
1728#if defined(BOOST_GCC)||defined(BOOST_CLANG)
1729 __builtin_prefetch((const char*)p);
1730#elif defined(BOOST_UNORDERED_SSE2)
1731 _mm_prefetch((const char*)p,_MM_HINT_T0);
1732#endif
1733}
1734
1735struct try_emplace_args_t{};
1736
1737template<typename Allocator>
1738struct is_std_allocator:std::false_type{};
1739
1740template<typename T>
1741struct is_std_allocator<std::allocator<T>>:std::true_type{};
1742
1743/* std::allocator::construct marked as deprecated */
1744#ifdef _LIBCPP_SUPPRESS_DEPRECATED_PUSH
1745_LIBCPP_SUPPRESS_DEPRECATED_PUSH
1746#elif defined(_STL_DISABLE_DEPRECATED_WARNING)
1747_STL_DISABLE_DEPRECATED_WARNING
1748#elif defined(_MSC_VER)
1749#pragma warning(push)
1750#pragma warning(disable:4996)
1751#endif
1752
1753template<typename Allocator,typename Ptr,typename... Args>
1754struct alloc_has_construct
1755{
1756private:
1757 template<typename Allocator2>
1758 static decltype(
1759 std::declval<Allocator2&>().construct(
1760 std::declval<Ptr>(),std::declval<Args&&>()...),
1761 std::true_type{}
1762 ) check(int);
1763
1764 template<typename> static std::false_type check(...);
1765
1766public:
1767 static constexpr bool value=decltype(check<Allocator>(0))::value;
1768};
1769
1770#ifdef _LIBCPP_SUPPRESS_DEPRECATED_POP
1771_LIBCPP_SUPPRESS_DEPRECATED_POP
1772#elif defined(_STL_RESTORE_DEPRECATED_WARNING)
1773_STL_RESTORE_DEPRECATED_WARNING
1774#elif defined(_MSC_VER)
1775#pragma warning(pop)
1776#endif
1777
1778#ifdef BOOST_GCC
1779/* GCC's -Wshadow triggers at scenarios like this:
1780 *
1781 * struct foo{};
1782 * template<typename Base>
1783 * struct derived:Base
1784 * {
1785 * void f(){int foo;}
1786 * };
1787 *
1788 * derived<foo>x;
1789 * x.f(); // declaration of "foo" in derived::f shadows base type "foo"
1790 *
1791 * This makes shadowing warnings unavoidable in general when a class template
1792 * derives from user-provided classes, as is the case with table and
1793 * empty_value's below.
1794 */
1795
1796#pragma GCC diagnostic push
1797#pragma GCC diagnostic ignored "-Wshadow"
1798#endif
1799
1800#ifdef BOOST_MSVC
1801#pragma warning(push)
1802#pragma warning(disable:4714)
1803#endif
1804
1805/* We expose the hard-coded max load factor so that tests can use it without
1806 * needing to pull it from an instantiated class template such as the table
1807 * class
1808 */
1809constexpr static float const mlf = 0.875f;
1810
1811template <class T>
1812union uninitialized_storage
1813{
1814 T t_;
1815 uninitialized_storage(){}
1816 ~uninitialized_storage(){}
1817};
1818
1819/* foa::table interface departs in a number of ways from that of C++ unordered
1820 * associative containers because it's not for end-user consumption
1821 * (boost::unordered_[flat|node]_[map|set]) wrappers complete it as
1822 * appropriate).
1823 *
1824 * The table supports two main modes of operation: node-based and flat. In the
1825 * node-based case, buckets store pointers to individually heap-allocated
1826 * elements. For flat, buckets directly store elements.
1827 *
1828 * For both tables:
1829 *
1830 * - begin() is not O(1).
1831 * - No bucket API.
1832 * - Load factor is fixed and can't be set by the user.
1833 *
1834 * For the inline table:
1835 *
1836 * - value_type must be moveable.
1837 * - Pointer stability is not kept under rehashing.
1838 * - No extract API.
1839 *
1840 * The TypePolicy template parameter is used to generate instantiations
1841 * suitable for either maps or sets, and introduces non-standard init_type:
1842 *
1843 * - TypePolicy::key_type and TypePolicy::value_type have the obvious
1844 * meaning.
1845 *
1846 * - TypePolicy::init_type is the type implicitly converted to when
1847 * writing x.insert({...}). For maps, this is std::pair<Key,T> rather
1848 * than std::pair<const Key,T> so that, for instance, x.insert({"hello",0})
1849 * produces a cheaply moveable std::string&& ("hello") rather than
1850 * a copyable const std::string&&. foa::table::insert is extended to accept
1851 * both init_type and value_type references.
1852 *
1853 * - TypePolicy::construct and TypePolicy::destroy are used for the
1854 * construction and destruction of the internal types: value_type, init_type
1855 * and element_type.
1856 *
1857 * - TypePolicy::move is used to provide move semantics for the internal
1858 * types used by the container during rehashing and emplace. These types
1859 * are init_type, value_type and emplace_type. During insertion, a
1860 * stack-local type will be created based on the constructibility of the
1861 * value_type and the supplied arguments. TypePolicy::move is used here
1862 * for transfer of ownership. Similarly, TypePolicy::move is also used
1863 * during rehashing when elements are moved to the new table.
1864 *
1865 * - TypePolicy::extract returns a const reference to the key part of
1866 * a value of type value_type, init_type, element_type or
1867 * decltype(TypePolicy::move(...)).
1868 *
1869 * - TypePolicy::element_type is the type that table_arrays uses when
1870 * allocating buckets. For flat containers, this is value_type. For node
1871 * containers, this is a strong typedef to value_type*.
1872 *
1873 * - TypePolicy::value_from returns a mutable reference to value_type from
1874 * a given element_type. This is used when elements of the table themselves
1875 * need to be moved, such as during move construction/assignment when
1876 * allocators are unequal and there is no propagation. For all other cases,
1877 * the element_type itself is moved.
1878 *
1879 * try_emplace, erase and find support heterogenous lookup by default, that is,
1880 * without checking for any ::is_transparent typedefs --the checking is done by
1881 * boost::unordered_[flat|node]_[map|set].
1882 */
1883
1884template<typename TypePolicy,typename Hash,typename Pred,typename Allocator>
1885class
1886
1887#if defined(_MSC_VER)&&_MSC_FULL_VER>=190023918
1888__declspec(empty_bases)
1889#endif
1890
1891table:empty_value<Hash,0>,empty_value<Pred,1>,empty_value<Allocator,2>
1892{
1893 using hash_base=empty_value<Hash,0>;
1894 using pred_base=empty_value<Pred,1>;
1895 using allocator_base=empty_value<Allocator,2>;
1896 using type_policy=TypePolicy;
1897 using group_type=group15;
1898 static constexpr auto N=group_type::N;
1899 using size_policy=pow2_size_policy;
1900 using prober=pow2_quadratic_prober;
1901 using mix_policy=typename std::conditional<
1902 hash_is_avalanching<Hash>::value,
1903 no_mix,
1904 mulx_mix
1905 >::type;
1906 using alloc_traits=std::allocator_traits<Allocator>;
1907
1908public:
1909 using key_type=typename type_policy::key_type;
1910 using init_type=typename type_policy::init_type;
1911 using value_type=typename type_policy::value_type;
1912 using element_type=typename type_policy::element_type;
1913
1914private:
1915 static constexpr bool has_mutable_iterator=
1916 !std::is_same<key_type,value_type>::value;
1917
1918public:
1919 using hasher=Hash;
1920 using key_equal=Pred;
1921 using allocator_type=Allocator;
1922 using pointer=value_type*;
1923 using const_pointer=const value_type*;
1924 using reference=value_type&;
1925 using const_reference=const value_type&;
1926 using size_type=std::size_t;
1927 using difference_type=std::ptrdiff_t;
1928 using const_iterator=table_iterator<type_policy,group_type,true>;
1929 using iterator=typename std::conditional<
1930 has_mutable_iterator,
1931 table_iterator<type_policy,group_type,false>,
1932 const_iterator>::type;
1933
1934 table(
1935 std::size_t n=0,const Hash& h_=Hash(),const Pred& pred_=Pred(),
1936 const Allocator& al_=Allocator()):
1937 hash_base{empty_init,h_},pred_base{empty_init,pred_},
1938 allocator_base{empty_init,al_},size_{0},arrays(new_arrays(n)),
1939 ml{initial_max_load()}
1940 {}
1941
1942 table(const table& x):
1943 table{x,alloc_traits::select_on_container_copy_construction(x.al())}{}
1944
1945 table(table&& x)
1946 noexcept(
1947 std::is_nothrow_move_constructible<Hash>::value&&
1948 std::is_nothrow_move_constructible<Pred>::value&&
1949 std::is_nothrow_move_constructible<Allocator>::value):
1950 hash_base{empty_init,std::move(x.h())},
1951 pred_base{empty_init,std::move(x.pred())},
1952 allocator_base{empty_init,std::move(x.al())},
1953 size_{x.size_},arrays(x.arrays),ml{x.ml}
1954 {
1955 x.size_=0;
1956 x.arrays=x.new_arrays(0);
1957 x.ml=x.initial_max_load();
1958 }
1959
1960 table(const table& x,const Allocator& al_):
1961 table{std::size_t(std::ceil(float(x.size())/mlf)),x.h(),x.pred(),al_}
1962 {
1963 copy_elements_from(x);
1964 }
1965
1966 table(table&& x,const Allocator& al_):
1967 table{0,std::move(x.h()),std::move(x.pred()),al_}
1968 {
1969 if(al()==x.al()){
1970 std::swap(size_,x.size_);
1971 std::swap(arrays,x.arrays);
1972 std::swap(ml,x.ml);
1973 }
1974 else{
1975 reserve(x.size());
1976 clear_on_exit c{x};
1977 (void)c;
1978
1979 /* This works because subsequent x.clear() does not depend on the
1980 * elements' values.
1981 */
1982 x.for_all_elements([this](element_type* p){
1983 unchecked_insert(type_policy::move(type_policy::value_from(*p)));
1984 });
1985 }
1986 }
1987
1988 ~table()noexcept
1989 {
1990 for_all_elements([this](element_type* p){
1991 destroy_element(p);
1992 });
1993 delete_arrays(arrays);
1994 }
1995
1996 table& operator=(const table& x)
1997 {
1999
2000 static constexpr auto pocca=
2001 alloc_traits::propagate_on_container_copy_assignment::value;
2002
2003 if(this!=std::addressof(x)){
2004 // if copy construction here winds up throwing, the container is still
2005 // left intact so we perform these operations first
2006 hasher tmp_h=x.h();
2007 key_equal tmp_p=x.pred();
2008
2009 // already noexcept, clear() before we swap the Hash, Pred just in case
2010 // the clear() impl relies on them at some point in the future
2011 clear();
2012
2013 // because we've asserted at compile-time that Hash and Pred are nothrow
2014 // swappable, we can safely mutate our source container and maintain
2015 // consistency between the Hash, Pred compatibility
2016 using std::swap;
2017 swap(h(),tmp_h);
2018 swap(pred(),tmp_p);
2019
2020 if_constexpr<pocca>([&,this]{
2021 if(al()!=x.al())reserve(0);
2022 copy_assign_if<pocca>(al(),x.al());
2023 });
2024 /* noshrink: favor memory reuse over tightness */
2025 noshrink_reserve(x.size());
2026 copy_elements_from(x);
2027 }
2028 return *this;
2029 }
2030
2031#ifdef BOOST_MSVC
2032#pragma warning(push)
2033#pragma warning(disable:4127)
2034#endif
2035
2036 table& operator=(table&& x)
2037 noexcept(
2038 alloc_traits::propagate_on_container_move_assignment::value||
2039 alloc_traits::is_always_equal::value)
2040 {
2042
2043 static constexpr auto pocma=
2044 alloc_traits::propagate_on_container_move_assignment::value;
2045
2046 if(this!=std::addressof(x)){
2047 /* Given ambiguity in implementation strategies briefly discussed here:
2048 * https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2227
2049 *
2050 * we opt into requiring nothrow swappability and eschew the move
2051 * operations associated with Hash, Pred.
2052 *
2053 * To this end, we ensure that the user never has to consider the
2054 * moved-from state of their Hash, Pred objects
2055 */
2056
2057 using std::swap;
2058
2059 clear();
2060 swap(h(),x.h());
2061 swap(pred(),x.pred());
2062
2063 if(pocma||al()==x.al()){
2064 reserve(0);
2065 move_assign_if<pocma>(al(),x.al());
2066 swap(size_,x.size_);
2067 swap(arrays,x.arrays);
2068 swap(ml,x.ml);
2069 }
2070 else{
2071 /* noshrink: favor memory reuse over tightness */
2072 noshrink_reserve(x.size());
2073 clear_on_exit c{x};
2074 (void)c;
2075
2076 /* This works because subsequent x.clear() does not depend on the
2077 * elements' values.
2078 */
2079 x.for_all_elements([this](element_type* p){
2080 unchecked_insert(type_policy::move(type_policy::value_from(*p)));
2081 });
2082 }
2083 }
2084 return *this;
2085 }
2086
2087#ifdef BOOST_MSVC
2088#pragma warning(pop)
2089#endif
2090
2091 allocator_type get_allocator()const noexcept{return al();}
2092
2093 iterator begin()noexcept
2094 {
2095 iterator it{arrays.groups,0,arrays.elements};
2096 if(!(arrays.groups[0].match_occupied()&0x1))++it;
2097 return it;
2098 }
2099
2100 const_iterator begin()const noexcept
2101 {return const_cast<table*>(this)->begin();}
2102 iterator end()noexcept{return {};}
2103 const_iterator end()const noexcept{return const_cast<table*>(this)->end();}
2104 const_iterator cbegin()const noexcept{return begin();}
2105 const_iterator cend()const noexcept{return end();}
2106
2107 bool empty()const noexcept{return size()==0;}
2108 std::size_t size()const noexcept{return size_;}
2109 std::size_t max_size()const noexcept{return SIZE_MAX;}
2110
2111 template<typename... Args>
2112 BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
2113 {
2114 using emplace_type=typename std::conditional<
2115 std::is_constructible<init_type,Args...>::value,
2116 init_type,
2117 value_type
2118 >::type;
2119
2120 using insert_type=typename std::conditional<
2121 std::is_constructible<
2122 value_type,emplace_type>::value,
2123 emplace_type,element_type
2124 >::type;
2125
2126 uninitialized_storage<insert_type> s;
2127 auto *p=std::addressof(s.t_);
2128
2129 type_policy::construct(al(),p,std::forward<Args>(args)...);
2130
2131 destroy_on_exit<insert_type> guard{al(),p};
2132 return emplace_impl(type_policy::move(*p));
2133 }
2134
2135 template<typename Key,typename... Args>
2136 BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
2137 Key&& x,Args&&... args)
2138 {
2139 return emplace_impl(
2140 try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
2141 }
2142
2143 BOOST_FORCEINLINE std::pair<iterator,bool>
2144 insert(const init_type& x){return emplace_impl(x);}
2145
2146 BOOST_FORCEINLINE std::pair<iterator,bool>
2147 insert(init_type&& x){return emplace_impl(std::move(x));}
2148
2149 /* template<typename=void> tilts call ambiguities in favor of init_type */
2150
2151 template<typename=void>
2152 BOOST_FORCEINLINE std::pair<iterator,bool>
2153 insert(const value_type& x){return emplace_impl(x);}
2154
2155 template<typename=void>
2156 BOOST_FORCEINLINE std::pair<iterator,bool>
2157 insert(value_type&& x){return emplace_impl(std::move(x));}
2158
2159 template<typename T=element_type>
2161 typename std::enable_if<
2162 !std::is_same<T,value_type>::value,
2163 std::pair<iterator,bool>
2164 >::type
2165 insert(element_type&& x){return emplace_impl(std::move(x));}
2166
2167 template<
2168 bool dependent_value=false,
2169 typename std::enable_if<
2170 has_mutable_iterator||dependent_value>::type* =nullptr
2171 >
2172 void erase(iterator pos)noexcept{return erase(const_iterator(pos));}
2173
2175 void erase(const_iterator pos)noexcept
2176 {
2177 destroy_element(pos.p);
2178 recover_slot(pos.pc);
2179 }
2180
2181 template<typename Key>
2183 auto erase(Key&& x) -> typename std::enable_if<
2184 !std::is_convertible<Key,iterator>::value&&
2185 !std::is_convertible<Key,const_iterator>::value, std::size_t>::type
2186 {
2187 auto it=find(x);
2188 if(it!=end()){
2189 erase(it);
2190 return 1;
2191 }
2192 else return 0;
2193 }
2194
2195 void swap(table& x)
2196 noexcept(
2197 alloc_traits::propagate_on_container_swap::value||
2198 alloc_traits::is_always_equal::value)
2199 {
2201
2202 static constexpr auto pocs=
2203 alloc_traits::propagate_on_container_swap::value;
2204
2205 using std::swap;
2206 if_constexpr<pocs>([&,this]{
2207 swap_if<pocs>(al(),x.al());
2208 },
2209 [&,this]{
2210 BOOST_ASSERT(al()==x.al());
2211 (void)this;
2212 });
2213
2214 swap(h(),x.h());
2215 swap(pred(),x.pred());
2216 swap(size_,x.size_);
2217 swap(arrays,x.arrays);
2218 swap(ml,x.ml);
2219 }
2220
2221 void clear()noexcept
2222 {
2223 auto p=arrays.elements;
2224 if(p){
2225 for(auto pg=arrays.groups,last=pg+arrays.groups_size_mask+1;
2226 pg!=last;++pg,p+=N){
2227 auto mask=pg->match_really_occupied();
2228 while(mask){
2229 destroy_element(p+unchecked_countr_zero(mask));
2230 mask&=mask-1;
2231 }
2232 /* we wipe the entire metadata to reset the overflow byte as well */
2233 pg->initialize();
2234 }
2235 arrays.groups[arrays.groups_size_mask].set_sentinel();
2236 size_=0;
2237 ml=initial_max_load();
2238 }
2239 }
2240
2241 element_type extract(const_iterator pos)
2242 {
2243 BOOST_ASSERT(pos!=end());
2244 erase_on_exit e{*this,pos};
2245 (void)e;
2246 return std::move(*pos.p);
2247 }
2248
2249 // TODO: should we accept different allocator too?
2250 template<typename Hash2,typename Pred2>
2251 void merge(table<TypePolicy,Hash2,Pred2,Allocator>& x)
2252 {
2253 x.for_all_elements([&,this](group_type* pg,unsigned int n,element_type* p){
2254 erase_on_exit e{x,{pg,n,p}};
2255 if(!emplace_impl(type_policy::move(*p)).second)e.rollback();
2256 });
2257 }
2258
2259 template<typename Hash2,typename Pred2>
2260 void merge(table<TypePolicy,Hash2,Pred2,Allocator>&& x){merge(x);}
2261
2262 hasher hash_function()const{return h();}
2263 key_equal key_eq()const{return pred();}
2264
2265 template<typename Key>
2266 BOOST_FORCEINLINE iterator find(const Key& x)
2267 {
2268 auto hash=hash_for(x);
2269 return find_impl(x,position_for(hash),hash);
2270 }
2271
2272 template<typename Key>
2273 BOOST_FORCEINLINE const_iterator find(const Key& x)const
2274 {
2275 return const_cast<table*>(this)->find(x);
2276 }
2277
2278 std::size_t capacity()const noexcept
2279 {
2280 return arrays.elements?(arrays.groups_size_mask+1)*N-1:0;
2281 }
2282
2283 float load_factor()const noexcept
2284 {
2285 if (capacity() == 0) { return 0; }
2286 return float(size())/float(capacity());
2287 }
2288
2289 float max_load_factor()const noexcept{return mlf;}
2290
2291 std::size_t max_load()const noexcept{return ml;}
2292
2293 void rehash(std::size_t n)
2294 {
2295 auto m=size_t(std::ceil(float(size())/mlf));
2296 if(m>n)n=m;
2297 if(n)n=capacity_for(n);
2298
2299 if(n!=capacity())unchecked_rehash(n);
2300 }
2301
2302 void reserve(std::size_t n)
2303 {
2304 rehash(std::size_t(std::ceil(float(n)/mlf)));
2305 }
2306
2307 template<typename Predicate>
2308 friend std::size_t erase_if(table& x,Predicate pr)
2309 {
2310 return x.erase_if_impl(pr);
2311 }
2312
2313private:
2314 template<typename,typename,typename,typename> friend class table;
2315 using arrays_type=table_arrays<element_type,group_type,size_policy>;
2316
2317 struct clear_on_exit
2318 {
2319 ~clear_on_exit(){x.clear();}
2320 table& x;
2321 };
2322
2323 struct erase_on_exit
2324 {
2325 erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{}
2326 ~erase_on_exit(){if(!rollback_)x.erase(it);}
2327
2328 void rollback(){rollback_=true;}
2329
2330 table& x;
2331 const_iterator it;
2332 bool rollback_=false;
2333 };
2334
2335 template <class T>
2336 struct destroy_on_exit
2337 {
2338 Allocator &a;
2339 T *p;
2340 ~destroy_on_exit(){type_policy::destroy(a,p);};
2341 };
2342
2343 Hash& h(){return hash_base::get();}
2344 const Hash& h()const{return hash_base::get();}
2345 Pred& pred(){return pred_base::get();}
2346 const Pred& pred()const{return pred_base::get();}
2347 Allocator& al(){return allocator_base::get();}
2348 const Allocator& al()const{return allocator_base::get();}
2349
2350 arrays_type new_arrays(std::size_t n)
2351 {
2352 return arrays_type::new_(al(),n);
2353 }
2354
2355 void delete_arrays(arrays_type& arrays_)noexcept
2356 {
2357 arrays_type::delete_(al(),arrays_);
2358 }
2359
2360 template<typename... Args>
2361 void construct_element(element_type* p,Args&&... args)
2362 {
2363 type_policy::construct(al(),p,std::forward<Args>(args)...);
2364 }
2365
2366 template<typename... Args>
2367 void construct_element(element_type* p,try_emplace_args_t,Args&&... args)
2368 {
2369 construct_element_from_try_emplace_args(
2370 p,
2371 std::integral_constant<bool,std::is_same<key_type,value_type>::value>{},
2372 std::forward<Args>(args)...);
2373 }
2374
2375 template<typename Key,typename... Args>
2376 void construct_element_from_try_emplace_args(
2377 element_type* p,std::false_type,Key&& x,Args&&... args)
2378 {
2379 type_policy::construct(
2380 al(),p,
2381 std::piecewise_construct,
2382 std::forward_as_tuple(std::forward<Key>(x)),
2383 std::forward_as_tuple(std::forward<Args>(args)...));
2384 }
2385
2386 /* This overload allows boost::unordered_flat_set to internally use
2387 * try_emplace to implement heterogeneous insert (P2363).
2388 */
2389
2390 template<typename Key>
2391 void construct_element_from_try_emplace_args(
2392 element_type* p,std::true_type,Key&& x)
2393 {
2394 type_policy::construct(al(),p,std::forward<Key>(x));
2395 }
2396
2397 void destroy_element(element_type* p)noexcept
2398 {
2399 type_policy::destroy(al(),p);
2400 }
2401
2402 struct destroy_element_on_exit
2403 {
2404 ~destroy_element_on_exit(){this_->destroy_element(p);}
2405 table *this_;
2406 element_type *p;
2407 };
2408
2409 void copy_elements_from(const table& x)
2410 {
2411 BOOST_ASSERT(empty());
2412 BOOST_ASSERT(this!=std::addressof(x));
2413 if(arrays.groups_size_mask==x.arrays.groups_size_mask){
2414 fast_copy_elements_from(x);
2415 }
2416 else{
2417 x.for_all_elements([this](const element_type* p){
2418 unchecked_insert(*p);
2419 });
2420 }
2421 }
2422
2423 void fast_copy_elements_from(const table& x)
2424 {
2425 if(arrays.elements){
2426 copy_elements_array_from(x);
2427 std::memcpy(
2428 arrays.groups,x.arrays.groups,
2429 (arrays.groups_size_mask+1)*sizeof(group_type));
2430 size_=x.size();
2431 }
2432 }
2433
2434 void copy_elements_array_from(const table& x)
2435 {
2436 copy_elements_array_from(
2437 x,
2438 std::integral_constant<
2439 bool,
2440 std::is_trivially_copy_constructible<element_type>::value
2441 &&(
2442 is_std_allocator<Allocator>::value||
2443 !alloc_has_construct<Allocator,value_type*,const value_type&>::value)
2444 >{}
2445 );
2446 }
2447
2448 void copy_elements_array_from(const table& x,std::true_type /* -> memcpy */)
2449 {
2450 /* reinterpret_cast: GCC may complain about value_type not being trivially
2451 * copy-assignable when we're relying on trivial copy constructibility.
2452 */
2453 std::memcpy(
2454 reinterpret_cast<unsigned char*>(arrays.elements),
2455 reinterpret_cast<unsigned char*>(x.arrays.elements),
2456 x.capacity()*sizeof(value_type));
2457 }
2458
2459 void copy_elements_array_from(const table& x,std::false_type /* -> manual */)
2460 {
2461 std::size_t num_constructed=0;
2462 BOOST_TRY{
2463 x.for_all_elements([&,this](const element_type* p){
2464 construct_element(arrays.elements+(p-x.arrays.elements),*p);
2465 ++num_constructed;
2466 });
2467 }
2468 BOOST_CATCH(...){
2469 if(num_constructed){
2470 x.for_all_elements_while([&,this](const element_type* p){
2471 destroy_element(arrays.elements+(p-x.arrays.elements));
2472 return --num_constructed!=0;
2473 });
2474 }
2476 }
2478 }
2479
2480 void recover_slot(unsigned char* pc)
2481 {
2482 /* If this slot potentially caused overflow, we decrease the maximum load so
2483 * that average probe length won't increase unboundedly in repeated
2484 * insert/erase cycles (drift).
2485 */
2486 ml-=group_type::maybe_caused_overflow(pc);
2487 group_type::reset(pc);
2488 --size_;
2489 }
2490
2491 void recover_slot(group_type* pg,std::size_t pos)
2492 {
2493 recover_slot(reinterpret_cast<unsigned char*>(pg)+pos);
2494 }
2495
2496 std::size_t initial_max_load()const
2497 {
2498 static constexpr std::size_t small_capacity=2*N-1;
2499
2500 auto capacity_=capacity();
2501 if(capacity_<=small_capacity){
2502 return capacity_;
2503 }
2504 else{
2505 return (std::size_t)(mlf*(float)(capacity_));
2506 }
2507 }
2508
2509 template<typename T>
2510 static inline auto key_from(const T& x)
2511 ->decltype(type_policy::extract(x))
2512 {
2513 return type_policy::extract(x);
2514 }
2515
2516 template<typename Key,typename... Args>
2517 static inline const Key& key_from(
2518 try_emplace_args_t,const Key& x,const Args&...)
2519 {
2520 return x;
2521 }
2522
2523 template<typename Key>
2524 inline std::size_t hash_for(const Key& x)const
2525 {
2526 return mix_policy::mix(h(),x);
2527 }
2528
2529 inline std::size_t position_for(std::size_t hash)const
2530 {
2531 return position_for(hash,arrays);
2532 }
2533
2534 static inline std::size_t position_for(
2535 std::size_t hash,const arrays_type& arrays_)
2536 {
2537 return size_policy::position(hash,arrays_.groups_size_index);
2538 }
2539
2540 static inline void prefetch_elements(const element_type* p)
2541 {
2542 /* We have experimentally confirmed that ARM architectures get a higher
2543 * speedup when around the first half of the element slots in a group are
2544 * prefetched, whereas for Intel just the first cache line is best.
2545 * Please report back if you find better tunings for some particular
2546 * architectures.
2547 */
2548
2550 /* Cache line size can't be known at compile time, so we settle on
2551 * the very frequent value of 64B.
2552 */
2553 constexpr int cache_line=64;
2554 const char *p0=reinterpret_cast<const char*>(p),
2555 *p1=p0+sizeof(value_type)*N/2;
2556 for(;p0<p1;p0+=cache_line)prefetch(p0);
2557#else
2558 prefetch(p);
2559#endif
2560 }
2561
2562#ifdef BOOST_MSVC
2563/* warning: forcing value to bool 'true' or 'false' in bool(pred()...) */
2564#pragma warning(push)
2565#pragma warning(disable:4800)
2566#endif
2567
2568 template<typename Key>
2569 BOOST_FORCEINLINE iterator find_impl(
2570 const Key& x,std::size_t pos0,std::size_t hash)const
2571 {
2572 prober pb(pos0);
2573 do{
2574 auto pos=pb.get();
2575 auto pg=arrays.groups+pos;
2576 auto mask=pg->match(hash);
2577 if(mask){
2578 BOOST_UNORDERED_ASSUME(arrays.elements != nullptr);
2579 auto p=arrays.elements+pos*N;
2580 prefetch_elements(p);
2581 do{
2582 auto n=unchecked_countr_zero(mask);
2583 if(BOOST_LIKELY(bool(pred()(x,key_from(p[n]))))){
2584 return {pg,n,p+n};
2585 }
2586 mask&=mask-1;
2587 }while(mask);
2588 }
2589 if(BOOST_LIKELY(pg->is_not_overflowed(hash))){
2590 return {};
2591 }
2592 }
2593 while(BOOST_LIKELY(pb.next(arrays.groups_size_mask)));
2594 return {};
2595 }
2596
2597#ifdef BOOST_MSVC
2598#pragma warning(pop)
2599#endif
2600
2601 template<typename... Args>
2602 BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
2603 {
2604 const auto &k=key_from(std::forward<Args>(args)...);
2605 auto hash=hash_for(k);
2606 auto pos0=position_for(hash);
2607 auto it=find_impl(k,pos0,hash);
2608
2609 if(it!=end()){
2610 return {it,false};
2611 }
2612 if(BOOST_LIKELY(size_<ml)){
2613 return {
2614 unchecked_emplace_at(pos0,hash,std::forward<Args>(args)...),
2615 true
2616 };
2617 }
2618 else{
2619 return {
2620 unchecked_emplace_with_rehash(hash,std::forward<Args>(args)...),
2621 true
2622 };
2623 }
2624 }
2625
2626 static std::size_t capacity_for(std::size_t n)
2627 {
2628 return size_policy::size(size_index_for<group_type,size_policy>(n))*N-1;
2629 }
2630
2631 template<typename... Args>
2632 BOOST_NOINLINE iterator
2633 unchecked_emplace_with_rehash(std::size_t hash,Args&&... args)
2634 {
2635 /* Due to the anti-drift mechanism (see recover_slot), new_arrays_ may be
2636 * of the same size as the old arrays; in the limit, erasing one element at
2637 * full load and then inserting could bring us back to the same capacity
2638 * after a costly rehash. To avoid this, we jump to the next capacity level
2639 * when the number of erased elements is <= 10% of total elements at full
2640 * load, which is implemented by requesting additional F*size elements,
2641 * with F = P * 10% / (1 - P * 10%), where P is the probability of an
2642 * element having caused overflow; P has been measured as ~0.162 under
2643 * ideal conditions, yielding F ~ 0.0165 ~ 1/61.
2644 */
2645 auto new_arrays_=new_arrays(std::size_t(
2646 std::ceil(static_cast<float>(size_+size_/61+1)/mlf)));
2647 iterator it;
2648 BOOST_TRY{
2649 /* strong exception guarantee -> try insertion before rehash */
2650 it=nosize_unchecked_emplace_at(
2651 new_arrays_,position_for(hash,new_arrays_),
2652 hash,std::forward<Args>(args)...);
2653 }
2654 BOOST_CATCH(...){
2655 delete_arrays(new_arrays_);
2657 }
2659
2660 /* new_arrays_ lifetime taken care of by unchecked_rehash */
2661 unchecked_rehash(new_arrays_);
2662 ++size_;
2663 return it;
2664 }
2665
2666 BOOST_NOINLINE void unchecked_rehash(std::size_t n)
2667 {
2668 auto new_arrays_=new_arrays(n);
2669 unchecked_rehash(new_arrays_);
2670 }
2671
2672 BOOST_NOINLINE void unchecked_rehash(arrays_type& new_arrays_)
2673 {
2674 std::size_t num_destroyed=0;
2675 BOOST_TRY{
2676 for_all_elements([&,this](element_type* p){
2677 nosize_transfer_element(p,new_arrays_,num_destroyed);
2678 });
2679 }
2680 BOOST_CATCH(...){
2681 if(num_destroyed){
2682 for_all_elements_while(
2683 [&,this](group_type* pg,unsigned int n,element_type*){
2684 recover_slot(pg,n);
2685 return --num_destroyed!=0;
2686 }
2687 );
2688 }
2689 for_all_elements(new_arrays_,[this](element_type* p){
2690 destroy_element(p);
2691 });
2692 delete_arrays(new_arrays_);
2694 }
2696
2697 /* either all moved and destroyed or all copied */
2698 BOOST_ASSERT(num_destroyed==size()||num_destroyed==0);
2699 if(num_destroyed!=size()){
2700 for_all_elements([this](element_type* p){
2701 destroy_element(p);
2702 });
2703 }
2704 delete_arrays(arrays);
2705 arrays=new_arrays_;
2706 ml=initial_max_load();
2707 }
2708
2709 void noshrink_reserve(std::size_t n)
2710 {
2711 /* used only on assignment after element clearance */
2712 BOOST_ASSERT(empty());
2713
2714 if(n){
2715 n=std::size_t(std::ceil(float(n)/mlf));
2716 n=capacity_for(n);
2717
2718 if(n>capacity()){
2719 auto new_arrays_=new_arrays(n);
2720 delete_arrays(arrays);
2721 arrays=new_arrays_;
2722 ml=initial_max_load();
2723 }
2724 }
2725 }
2726
2727 template<typename Value>
2728 void unchecked_insert(Value&& x)
2729 {
2730 auto hash=hash_for(key_from(x));
2731 unchecked_emplace_at(position_for(hash),hash,std::forward<Value>(x));
2732 }
2733
2734 void nosize_transfer_element(
2735 element_type* p,const arrays_type& arrays_,std::size_t& num_destroyed)
2736 {
2737 nosize_transfer_element(
2738 p,hash_for(key_from(*p)),arrays_,num_destroyed,
2739 std::integral_constant<
2740 bool,
2741 std::is_nothrow_move_constructible<init_type>::value||
2742 !std::is_same<element_type,value_type>::value||
2743 !std::is_copy_constructible<element_type>::value>{});
2744 }
2745
2746 void nosize_transfer_element(
2747 element_type* p,std::size_t hash,const arrays_type& arrays_,
2748 std::size_t& num_destroyed,std::true_type /* ->move */)
2749 {
2750 /* Destroy p even if an an exception is thrown in the middle of move
2751 * construction, which could leave the source half-moved.
2752 */
2753 ++num_destroyed;
2754 destroy_element_on_exit d{this,p};
2755 (void)d;
2756 nosize_unchecked_emplace_at(
2757 arrays_,position_for(hash,arrays_),hash,type_policy::move(*p));
2758 }
2759
2760 void nosize_transfer_element(
2761 element_type* p,std::size_t hash,const arrays_type& arrays_,
2762 std::size_t& /*num_destroyed*/,std::false_type /* ->copy */)
2763 {
2764 nosize_unchecked_emplace_at(
2765 arrays_,position_for(hash,arrays_),hash,
2766 const_cast<const element_type&>(*p));
2767 }
2768
2769 template<typename... Args>
2770 iterator unchecked_emplace_at(
2771 std::size_t pos0,std::size_t hash,Args&&... args)
2772 {
2773 auto res=nosize_unchecked_emplace_at(
2774 arrays,pos0,hash,std::forward<Args>(args)...);
2775 ++size_;
2776 return res;
2777 }
2778
2779 template<typename... Args>
2780 iterator nosize_unchecked_emplace_at(
2781 const arrays_type& arrays_,std::size_t pos0,std::size_t hash,
2782 Args&&... args)
2783 {
2784 for(prober pb(pos0);;pb.next(arrays_.groups_size_mask)){
2785 auto pos=pb.get();
2786 auto pg=arrays_.groups+pos;
2787 auto mask=pg->match_available();
2788 if(BOOST_LIKELY(mask!=0)){
2789 auto n=unchecked_countr_zero(mask);
2790 auto p=arrays_.elements+pos*N+n;
2791 construct_element(p,std::forward<Args>(args)...);
2792 pg->set(n,hash);
2793 return {pg,n,p};
2794 }
2795 else pg->mark_overflow(hash);
2796 }
2797 }
2798
2799 template<typename Predicate>
2800 std::size_t erase_if_impl(Predicate pr)
2801 {
2802 std::size_t s=size();
2803 for_all_elements([&,this](group_type* pg,unsigned int n,element_type* p){
2804 if(pr(type_policy::value_from(*p))) erase(iterator{pg,n,p});
2805 });
2806 return std::size_t(s-size());
2807 }
2808
2809 template<typename F>
2810 void for_all_elements(F f)const
2811 {
2812 for_all_elements(arrays,f);
2813 }
2814
2815 template<typename F>
2816 static auto for_all_elements(const arrays_type& arrays_,F f)
2817 ->decltype(f(nullptr),void())
2818 {
2819 for_all_elements_while(arrays_,[&](element_type* p){f(p);return true;});
2820 }
2821
2822 template<typename F>
2823 static auto for_all_elements(const arrays_type& arrays_,F f)
2824 ->decltype(f(nullptr,0,nullptr),void())
2825 {
2826 for_all_elements_while(
2827 arrays_,[&](group_type* pg,unsigned int n,element_type* p)
2828 {f(pg,n,p);return true;});
2829 }
2830
2831 template<typename F>
2832 void for_all_elements_while(F f)const
2833 {
2834 for_all_elements_while(arrays,f);
2835 }
2836
2837 template<typename F>
2838 static auto for_all_elements_while(const arrays_type& arrays_,F f)
2839 ->decltype(f(nullptr),void())
2840 {
2841 for_all_elements_while(
2842 arrays_,[&](group_type*,unsigned int,element_type* p){return f(p);});
2843 }
2844
2845 template<typename F>
2846 static auto for_all_elements_while(const arrays_type& arrays_,F f)
2847 ->decltype(f(nullptr,0,nullptr),void())
2848 {
2849 auto p=arrays_.elements;
2850 if(!p){return;}
2851 for(auto pg=arrays_.groups,last=pg+arrays_.groups_size_mask+1;
2852 pg!=last;++pg,p+=N){
2853 auto mask=pg->match_really_occupied();
2854 while(mask){
2855 auto n=unchecked_countr_zero(mask);
2856 if(!f(pg,n,p+n))return;
2857 mask&=mask-1;
2858 }
2859 }
2860 }
2861
2862 std::size_t size_;
2863 arrays_type arrays;
2864 std::size_t ml;
2865};
2866
2867#ifdef BOOST_MSVC
2868#pragma warning(pop)
2869#endif
2870
2871#ifdef BOOST_GCC
2872#pragma GCC diagnostic pop
2873#endif
2874
2875}
2876}
2877}
2878}
2879
2880#undef BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED
2881#undef BOOST_UNORDERED_ASSUME
2882#undef BOOST_UNORDERED_HAS_BUILTIN
2883#endif
2884// Copyright (C) 2022 Christian Mazakas
2885//
2886// Distributed under the Boost Software License, Version 1.0. (See accompanying
2887// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
2888
2889#ifndef BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP
2890#define BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP
2891
2892#pragma once
2893
2894// BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
2895
2896#ifndef BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
2897#define BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES 1
2898#endif
2899
2900namespace boost {
2901 namespace unordered {
2902 namespace detail {
2903 ////////////////////////////////////////////////////////////////////////////
2904 // Type checkers used for the transparent member functions added by C++20
2905 // and up
2906
2907 template <class, class = void>
2908 struct is_transparent : public std::false_type
2909 {
2910 };
2911
2912 template <class T>
2913 struct is_transparent<T,
2914 typename std::void_t<typename T::is_transparent>>
2915 : public std::true_type
2916 {
2917 };
2918
2919 template <class, class Hash, class KeyEqual> struct are_transparent
2920 {
2921 static bool const value =
2922 is_transparent<Hash>::value && is_transparent<KeyEqual>::value;
2923 };
2924
2925 template <class Key, class UnorderedMap> struct transparent_non_iterable
2926 {
2927 typedef typename UnorderedMap::hasher hash;
2928 typedef typename UnorderedMap::key_equal key_equal;
2929 typedef typename UnorderedMap::iterator iterator;
2930 typedef typename UnorderedMap::const_iterator const_iterator;
2931
2932 static bool const value =
2933 are_transparent<Key, hash, key_equal>::value &&
2934 !std::is_convertible<Key, iterator>::value &&
2935 !std::is_convertible<Key, const_iterator>::value;
2936 };
2937
2938 // https://eel.is/c++draft/container.requirements#container.alloc.reqmts-34
2939 // https://eel.is/c++draft/container.requirements#unord.req.general-243
2940
2941 template <class InputIterator>
2942 constexpr bool const is_input_iterator_v =
2943 !std::is_integral<InputIterator>::value;
2944
2945 template <class A, class = void> struct is_allocator
2946 {
2947 constexpr static bool const value = false;
2948 };
2949
2950 template <class A>
2951 struct is_allocator<A,
2952 std::void_t<typename A::value_type,
2953 decltype(std::declval<A&>().allocate(std::size_t{}))> >
2954 {
2955 constexpr static bool const value = true;
2956 };
2957
2958 template <class A>
2959 constexpr bool const is_allocator_v = is_allocator<A>::value;
2960
2961 template <class H>
2962 constexpr bool const is_hash_v =
2963 !std::is_integral<H>::value && !is_allocator_v<H>;
2964
2965 template <class P> constexpr bool const is_pred_v = !is_allocator_v<P>;
2966 } // namespace detail
2967 } // namespace unordered
2968} // namespace boost
2969
2970#endif // BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP
2971// Copyright 2005-2009 Daniel James.
2972// Copyright 2021, 2022 Peter Dimov.
2973// Distributed under the Boost Software License, Version 1.0.
2974// https://www.boost.org/LICENSE_1_0.txt
2975
2976#ifndef BOOST_FUNCTIONAL_HASH_FWD_HPP
2977#define BOOST_FUNCTIONAL_HASH_FWD_HPP
2978
2979namespace boost
2980{
2981
2982namespace container_hash
2983{
2984
2985template<class T> struct is_range;
2986template<class T> struct is_contiguous_range;
2987template<class T> struct is_unordered_range;
2988template<class T> struct is_tuple_like;
2989
2990} // namespace container_hash
2991
2992template<class T> struct hash;
2993
2994template<class T> void hash_combine( std::size_t& seed, T const& v );
2995
2996template<class It> void hash_range( std::size_t&, It, It );
2997template<class It> std::size_t hash_range( It, It );
2998
2999template<class It> void hash_unordered_range( std::size_t&, It, It );
3000template<class It> std::size_t hash_unordered_range( It, It );
3001
3002} // namespace boost
3003
3004#endif // #ifndef BOOST_FUNCTIONAL_HASH_FWD_HPP
3005// Copyright (C) 2008-2016 Daniel James.
3006// Copyright (C) 2022 Christian Mazakas
3007// Distributed under the Boost Software License, Version 1.0. (See accompanying
3008// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
3009
3010#ifndef BOOST_UNORDERED_FWD_HPP_INCLUDED
3011#define BOOST_UNORDERED_FWD_HPP_INCLUDED
3012
3013#pragma once
3014
3015namespace boost {
3016 namespace unordered {
3017 using std::piecewise_construct_t;
3018 using std::piecewise_construct;
3019 }
3020}
3021
3022#endif
3023// Copyright (C) 2022 Christian Mazakas
3024// Distributed under the Boost Software License, Version 1.0. (See accompanying
3025// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
3026
3027#ifndef BOOST_UNORDERED_FLAT_SET_FWD_HPP_INCLUDED
3028#define BOOST_UNORDERED_FLAT_SET_FWD_HPP_INCLUDED
3029
3030#pragma once
3031
3032namespace boost {
3033 namespace unordered {
3034 template <class Key, class Hash = boost::hash<Key>,
3035 class KeyEqual = std::equal_to<Key>,
3036 class Allocator = std::allocator<Key> >
3037 class unordered_flat_set;
3038
3039 template <class Key, class Hash, class KeyEqual, class Allocator>
3040 bool operator==(
3041 unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
3042 unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
3043
3044 template <class Key, class Hash, class KeyEqual, class Allocator>
3045 bool operator!=(
3046 unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
3047 unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
3048
3049 template <class Key, class Hash, class KeyEqual, class Allocator>
3050 void swap(unordered_flat_set<Key, Hash, KeyEqual, Allocator>& lhs,
3051 unordered_flat_set<Key, Hash, KeyEqual, Allocator>& rhs)
3052 noexcept(noexcept(lhs.swap(rhs)));
3053 } // namespace unordered
3054
3055 using boost::unordered::unordered_flat_set;
3056
3057 using boost::unordered::swap;
3058 using boost::unordered::operator==;
3059 using boost::unordered::operator!=;
3060} // namespace boost
3061
3062#endif
3063// Copyright 2017 Peter Dimov.
3064// Distributed under the Boost Software License, Version 1.0.
3065// https://www.boost.org/LICENSE_1_0.txt
3066
3067#ifndef BOOST_HASH_IS_RANGE_HPP_INCLUDED
3068#define BOOST_HASH_IS_RANGE_HPP_INCLUDED
3069
3070namespace boost
3071{
3072
3073namespace hash_detail
3074{
3075
3076template<class T, class It>
3077 std::integral_constant< bool, !std::is_same<typename std::remove_cv<T>::type, typename std::iterator_traits<It>::value_type>::value >
3078 is_range_check( It first, It last );
3079
3080template<class T> decltype( is_range_check<T>( std::declval<T const&>().begin(), std::declval<T const&>().end() ) ) is_range_( int );
3081template<class T> std::false_type is_range_( ... );
3082
3083} // namespace hash_detail
3084
3085namespace container_hash
3086{
3087
3088template<class T> struct is_range: decltype( hash_detail::is_range_<T>( 0 ) )
3089{
3090};
3091
3092} // namespace container_hash
3093
3094} // namespace boost
3095
3096#endif // #ifndef BOOST_HASH_IS_RANGE_HPP_INCLUDED
3097// Copyright 2017, 2018 Peter Dimov.
3098// Distributed under the Boost Software License, Version 1.0.
3099// https://www.boost.org/LICENSE_1_0.txt
3100
3101#ifndef BOOST_HASH_IS_CONTIGUOUS_RANGE_HPP_INCLUDED
3102#define BOOST_HASH_IS_CONTIGUOUS_RANGE_HPP_INCLUDED
3103
3104namespace boost
3105{
3106namespace hash_detail
3107{
3108
3109template<class It, class T, class S>
3110 std::integral_constant< bool, std::is_same<typename std::iterator_traits<It>::value_type, T>::value && std::is_integral<S>::value >
3111 is_contiguous_range_check( It first, It last, T const*, T const*, S );
3112
3113template<class T> decltype( is_contiguous_range_check( std::declval<T const&>().begin(), std::declval<T const&>().end(), std::declval<T const&>().data(), std::declval<T const&>().data() + std::declval<T const&>().size(), std::declval<T const&>().size() ) ) is_contiguous_range_( int );
3114template<class T> std::false_type is_contiguous_range_( ... );
3115
3116template<class T> struct is_contiguous_range: decltype( hash_detail::is_contiguous_range_<T>( 0 ) )
3117{
3118};
3119
3120} // namespace hash_detail
3121
3122namespace container_hash
3123{
3124
3125template<class T> struct is_contiguous_range: std::integral_constant< bool, is_range<T>::value && hash_detail::is_contiguous_range<T>::value >
3126{
3127};
3128
3129} // namespace container_hash
3130} // namespace boost
3131
3132#endif // #ifndef BOOST_HASH_IS_CONTIGUOUS_RANGE_HPP_INCLUDED
3133// Copyright 2017 Peter Dimov.
3134// Distributed under the Boost Software License, Version 1.0.
3135// https://www.boost.org/LICENSE_1_0.txt
3136
3137#ifndef BOOST_HASH_IS_UNORDERED_RANGE_HPP_INCLUDED
3138#define BOOST_HASH_IS_UNORDERED_RANGE_HPP_INCLUDED
3139
3140namespace boost
3141{
3142namespace hash_detail
3143{
3144
3145template<class T, class E = std::true_type> struct has_hasher_: std::false_type
3146{
3147};
3148
3149template<class T> struct has_hasher_< T, std::integral_constant< bool,
3150 std::is_same<typename T::hasher, typename T::hasher>::value
3151 > >: std::true_type
3152{
3153};
3154
3155} // namespace hash_detail
3156
3157namespace container_hash
3158{
3159
3160template<class T> struct is_unordered_range: std::integral_constant< bool, is_range<T>::value && hash_detail::has_hasher_<T>::value >
3161{
3162};
3163
3164} // namespace container_hash
3165} // namespace boost
3166
3167#endif // #ifndef BOOST_HASH_IS_UNORDERED_RANGE_HPP_INCLUDED
3168#ifndef BOOST_HASH_IS_TUPLE_LIKE_HPP_INCLUDED
3169#define BOOST_HASH_IS_TUPLE_LIKE_HPP_INCLUDED
3170
3171// Copyright 2017, 2022 Peter Dimov.
3172// Distributed under the Boost Software License, Version 1.0.
3173// https://www.boost.org/LICENSE_1_0.txt
3174
3175namespace boost
3176{
3177namespace hash_detail
3178{
3179
3180template<class T, class E = std::true_type> struct is_tuple_like_: std::false_type
3181{
3182};
3183
3184template<class T> struct is_tuple_like_<T, std::integral_constant<bool, std::tuple_size<T>::value == std::tuple_size<T>::value> >: std::true_type
3185{
3186};
3187
3188} // namespace hash_detail
3189
3190namespace container_hash
3191{
3192
3193template<class T> struct is_tuple_like: hash_detail::is_tuple_like_<T>
3194{
3195};
3196
3197} // namespace container_hash
3198} // namespace boost
3199
3200#endif // #ifndef BOOST_HASH_IS_TUPLE_LIKE_HPP_INCLUDED
3201// Copyright 2005-2009 Daniel James.
3202// Copyright 2021 Peter Dimov.
3203// Distributed under the Boost Software License, Version 1.0.
3204// https://www.boost.org/LICENSE_1_0.txt
3205
3206#ifndef BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
3207#define BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
3208
3209namespace boost
3210{
3211namespace hash_detail
3212{
3213
3214template <std::size_t I, typename T>
3215inline
3216typename std::enable_if<(I == std::tuple_size<T>::value), void>::type
3217 hash_combine_tuple_like( std::size_t&, T const& )
3218{
3219}
3220
3221template <std::size_t I, typename T>
3222inline
3223typename std::enable_if<(I < std::tuple_size<T>::value), void>::type
3224 hash_combine_tuple_like( std::size_t& seed, T const& v )
3225{
3226 using std::get;
3227 boost::hash_combine( seed, get<I>( v ) );
3228
3229 boost::hash_detail::hash_combine_tuple_like<I + 1>( seed, v );
3230}
3231
3232template <typename T>
3233inline std::size_t hash_tuple_like( T const& v )
3234{
3235 std::size_t seed = 0;
3236
3237 boost::hash_detail::hash_combine_tuple_like<0>( seed, v );
3238
3239 return seed;
3240}
3241
3242} // namespace hash_detail
3243
3244template <class T>
3245inline
3246typename std::enable_if<
3247 container_hash::is_tuple_like<T>::value && !container_hash::is_range<T>::value,
3248std::size_t>::type
3249 hash_value( T const& v )
3250{
3251 return boost::hash_detail::hash_tuple_like( v );
3252}
3253
3254} // namespace boost
3255
3256#endif // #ifndef BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
3257// Copyright 2022 Peter Dimov
3258// Distributed under the Boost Software License, Version 1.0.
3259// https://www.boost.org/LICENSE_1_0.txt
3260
3261#ifndef BOOST_HASH_DETAIL_HASH_MIX_HPP
3262#define BOOST_HASH_DETAIL_HASH_MIX_HPP
3263
3264namespace boost
3265{
3266namespace hash_detail
3267{
3268
3269template<std::size_t Bits> struct hash_mix_impl;
3270
3271// hash_mix for 64 bit size_t
3272//
3273// The general "xmxmx" form of state of the art 64 bit mixers originates
3274// from Murmur3 by Austin Appleby, which uses the following function as
3275// its "final mix":
3276//
3277// k ^= k >> 33;
3278// k *= 0xff51afd7ed558ccd;
3279// k ^= k >> 33;
3280// k *= 0xc4ceb9fe1a85ec53;
3281// k ^= k >> 33;
3282//
3283// (https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp)
3284//
3285// It has subsequently been improved multiple times by different authors
3286// by changing the constants. The most well known improvement is the
3287// so-called "variant 13" function by David Stafford:
3288//
3289// k ^= k >> 30;
3290// k *= 0xbf58476d1ce4e5b9;
3291// k ^= k >> 27;
3292// k *= 0x94d049bb133111eb;
3293// k ^= k >> 31;
3294//
3295// (https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html)
3296//
3297// This mixing function is used in the splitmix64 RNG:
3298// http://xorshift.di.unimi.it/splitmix64.c
3299//
3300// We use Jon Maiga's implementation from
3301// http://jonkagstrom.com/mx3/mx3_rev2.html
3302//
3303// x ^= x >> 32;
3304// x *= 0xe9846af9b1a615d;
3305// x ^= x >> 32;
3306// x *= 0xe9846af9b1a615d;
3307// x ^= x >> 28;
3308//
3309// An equally good alternative is Pelle Evensen's Moremur:
3310//
3311// x ^= x >> 27;
3312// x *= 0x3C79AC492BA7B653;
3313// x ^= x >> 33;
3314// x *= 0x1C69B3F74AC4AE35;
3315// x ^= x >> 27;
3316//
3317// (https://mostlymangling.blogspot.com/2019/12/stronger-better-morer-moremur-better.html)
3318
3319template<> struct hash_mix_impl<64>
3320{
3321 inline static std::uint64_t fn( std::uint64_t x )
3322 {
3323 std::uint64_t const m = (std::uint64_t(0xe9846af) << 32) + 0x9b1a615d;
3324
3325 x ^= x >> 32;
3326 x *= m;
3327 x ^= x >> 32;
3328 x *= m;
3329 x ^= x >> 28;
3330
3331 return x;
3332 }
3333};
3334
3335// hash_mix for 32 bit size_t
3336//
3337// We use the "best xmxmx" implementation from
3338// https://github.com/skeeto/hash-prospector/issues/19
3339
3340template<> struct hash_mix_impl<32>
3341{
3342 inline static std::uint32_t fn( std::uint32_t x )
3343 {
3344 std::uint32_t const m1 = 0x21f0aaad;
3345 std::uint32_t const m2 = 0x735a2d97;
3346
3347 x ^= x >> 16;
3348 x *= m1;
3349 x ^= x >> 15;
3350 x *= m2;
3351 x ^= x >> 15;
3352
3353 return x;
3354 }
3355};
3356
3357inline std::size_t hash_mix( std::size_t v )
3358{
3359 return hash_mix_impl<sizeof(std::size_t) * CHAR_BIT>::fn( v );
3360}
3361
3362} // namespace hash_detail
3363} // namespace boost
3364
3365#endif // #ifndef BOOST_HASH_DETAIL_HASH_MIX_HPP
3366// Copyright 2022, 2023 Peter Dimov
3367// Distributed under the Boost Software License, Version 1.0.
3368// https://www.boost.org/LICENSE_1_0.txt
3369
3370#ifndef BOOST_HASH_DETAIL_MULX_HPP
3371#define BOOST_HASH_DETAIL_MULX_HPP
3372
3373#ifdef _MSC_VER
3374# include <intrin.h>
3375#endif
3376
3377namespace boost
3378{
3379namespace hash_detail
3380{
3381
3382#if defined(_MSC_VER) && defined(_M_X64) && !defined(__clang__)
3383
3384__forceinline std::uint64_t mulx( std::uint64_t x, std::uint64_t y )
3385{
3386 std::uint64_t r2;
3387 std::uint64_t r = _umul128( x, y, &r2 );
3388 return r ^ r2;
3389}
3390
3391#elif defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
3392
3393__forceinline std::uint64_t mulx( std::uint64_t x, std::uint64_t y )
3394{
3395 std::uint64_t r = x * y;
3396 std::uint64_t r2 = __umulh( x, y );
3397 return r ^ r2;
3398}
3399
3400#elif defined(__SIZEOF_INT128__)
3401
3402inline std::uint64_t mulx( std::uint64_t x, std::uint64_t y )
3403{
3404 __uint128_t r = static_cast<__uint128_t>( x ) * y;
3405 return static_cast<std::uint64_t>( r ) ^ static_cast<std::uint64_t>( r >> 64 );
3406}
3407
3408#else
3409
3410inline std::uint64_t mulx( std::uint64_t x, std::uint64_t y )
3411{
3412 std::uint64_t x1 = static_cast<std::uint32_t>( x );
3413 std::uint64_t x2 = x >> 32;
3414
3415 std::uint64_t y1 = static_cast<std::uint32_t>( y );
3416 std::uint64_t y2 = y >> 32;
3417
3418 std::uint64_t r3 = x2 * y2;
3419
3420 std::uint64_t r2a = x1 * y2;
3421
3422 r3 += r2a >> 32;
3423
3424 std::uint64_t r2b = x2 * y1;
3425
3426 r3 += r2b >> 32;
3427
3428 std::uint64_t r1 = x1 * y1;
3429
3430 std::uint64_t r2 = (r1 >> 32) + static_cast<std::uint32_t>( r2a ) + static_cast<std::uint32_t>( r2b );
3431
3432 r1 = (r2 << 32) + static_cast<std::uint32_t>( r1 );
3433 r3 += r2 >> 32;
3434
3435 return r1 ^ r3;
3436}
3437
3438#endif
3439
3440} // namespace hash_detail
3441} // namespace boost
3442
3443#endif // #ifndef BOOST_HASH_DETAIL_MULX_HPP
3444// Copyright 2022 Peter Dimov
3445// Distributed under the Boost Software License, Version 1.0.
3446// https://www.boost.org/LICENSE_1_0.txt
3447
3448#ifndef BOOST_HASH_DETAIL_HASH_RANGE_HPP
3449#define BOOST_HASH_DETAIL_HASH_RANGE_HPP
3450
3451namespace boost
3452{
3453namespace hash_detail
3454{
3455
3456template<class T> struct is_char_type: public std::false_type {};
3457
3458#if CHAR_BIT == 8
3459
3460template<> struct is_char_type<char>: public std::true_type {};
3461template<> struct is_char_type<signed char>: public std::true_type {};
3462template<> struct is_char_type<unsigned char>: public std::true_type {};
3463
3464#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
3465template<> struct is_char_type<char8_t>: public std::true_type {};
3466#endif
3467
3468#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
3469template<> struct is_char_type<std::byte>: public std::true_type {};
3470#endif
3471
3472#endif
3473
3474// generic version
3475
3476template<class It>
3477inline typename std::enable_if<
3478 !is_char_type<typename std::iterator_traits<It>::value_type>::value,
3479std::size_t >::type
3480 hash_range( std::size_t seed, It first, It last )
3481{
3482 for( ; first != last; ++first )
3483 {
3484 hash_combine<typename std::iterator_traits<It>::value_type>( seed, *first );
3485 }
3486
3487 return seed;
3488}
3489
3490// specialized char[] version, 32 bit
3491
3492template<class It> inline std::uint32_t read32le( It p )
3493{
3494 // clang 5+, gcc 5+ figure out this pattern and use a single mov on x86
3495 // gcc on s390x and power BE even knows how to use load-reverse
3496
3497 std::uint32_t w =
3498 static_cast<std::uint32_t>( static_cast<unsigned char>( p[0] ) ) |
3499 static_cast<std::uint32_t>( static_cast<unsigned char>( p[1] ) ) << 8 |
3500 static_cast<std::uint32_t>( static_cast<unsigned char>( p[2] ) ) << 16 |
3501 static_cast<std::uint32_t>( static_cast<unsigned char>( p[3] ) ) << 24;
3502
3503 return w;
3504}
3505
3506#if defined(_MSC_VER) && !defined(__clang__)
3507
3508template<class T> inline std::uint32_t read32le( T* p )
3509{
3510 std::uint32_t w;
3511
3512 std::memcpy( &w, p, 4 );
3513 return w;
3514}
3515
3516#endif
3517
3518inline std::uint64_t mul32( std::uint32_t x, std::uint32_t y )
3519{
3520 return static_cast<std::uint64_t>( x ) * y;
3521}
3522
3523template<class It>
3524inline typename std::enable_if<
3525 is_char_type<typename std::iterator_traits<It>::value_type>::value &&
3526 std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
3527 std::numeric_limits<std::size_t>::digits <= 32,
3528std::size_t>::type
3529 hash_range( std::size_t seed, It first, It last )
3530{
3531 It p = first;
3532 std::size_t n = static_cast<std::size_t>( last - first );
3533
3534 std::uint32_t const q = 0x9e3779b9U;
3535 std::uint32_t const k = 0xe35e67b1U; // q * q
3536
3537 std::uint64_t h = mul32( static_cast<std::uint32_t>( seed ) + q, k );
3538 std::uint32_t w = static_cast<std::uint32_t>( h & 0xFFFFFFFF );
3539
3540 h ^= n;
3541
3542 while( n >= 4 )
3543 {
3544 std::uint32_t v1 = read32le( p );
3545
3546 w += q;
3547 h ^= mul32( v1 + w, k );
3548
3549 p += 4;
3550 n -= 4;
3551 }
3552
3553 {
3554 std::uint32_t v1 = 0;
3555
3556 if( n >= 1 )
3557 {
3558 std::size_t const x1 = ( n - 1 ) & 2; // 1: 0, 2: 0, 3: 2
3559 std::size_t const x2 = n >> 1; // 1: 0, 2: 1, 3: 1
3560
3561 v1 =
3562 static_cast<std::uint32_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x1 ) ] ) ) << x1 * 8 |
3563 static_cast<std::uint32_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x2 ) ] ) ) << x2 * 8 |
3564 static_cast<std::uint32_t>( static_cast<unsigned char>( p[ 0 ] ) );
3565 }
3566
3567 w += q;
3568 h ^= mul32( v1 + w, k );
3569 }
3570
3571 w += q;
3572 h ^= mul32( static_cast<std::uint32_t>( h & 0xFFFFFFFF ) + w, static_cast<std::uint32_t>( h >> 32 ) + w + k );
3573
3574 return static_cast<std::uint32_t>( h & 0xFFFFFFFF ) ^ static_cast<std::uint32_t>( h >> 32 );
3575}
3576
3577template<class It>
3578inline typename std::enable_if<
3579 is_char_type<typename std::iterator_traits<It>::value_type>::value &&
3580 !std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
3581 std::numeric_limits<std::size_t>::digits <= 32,
3582std::size_t>::type
3583 hash_range( std::size_t seed, It first, It last )
3584{
3585 std::size_t n = 0;
3586
3587 std::uint32_t const q = 0x9e3779b9U;
3588 std::uint32_t const k = 0xe35e67b1U; // q * q
3589
3590 std::uint64_t h = mul32( static_cast<std::uint32_t>( seed ) + q, k );
3591 std::uint32_t w = static_cast<std::uint32_t>( h & 0xFFFFFFFF );
3592
3593 std::uint32_t v1 = 0;
3594
3595 for( ;; )
3596 {
3597 v1 = 0;
3598
3599 if( first == last )
3600 {
3601 break;
3602 }
3603
3604 v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) );
3605 ++first;
3606 ++n;
3607
3608 if( first == last )
3609 {
3610 break;
3611 }
3612
3613 v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 8;
3614 ++first;
3615 ++n;
3616
3617 if( first == last )
3618 {
3619 break;
3620 }
3621
3622 v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 16;
3623 ++first;
3624 ++n;
3625
3626 if( first == last )
3627 {
3628 break;
3629 }
3630
3631 v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 24;
3632 ++first;
3633 ++n;
3634
3635 w += q;
3636 h ^= mul32( v1 + w, k );
3637 }
3638
3639 h ^= n;
3640
3641 w += q;
3642 h ^= mul32( v1 + w, k );
3643
3644 w += q;
3645 h ^= mul32( static_cast<std::uint32_t>( h & 0xFFFFFFFF ) + w, static_cast<std::uint32_t>( h >> 32 ) + w + k );
3646
3647 return static_cast<std::uint32_t>( h & 0xFFFFFFFF ) ^ static_cast<std::uint32_t>( h >> 32 );
3648}
3649
3650// specialized char[] version, 64 bit
3651
3652template<class It> inline std::uint64_t read64le( It p )
3653{
3654 std::uint64_t w =
3655 static_cast<std::uint64_t>( static_cast<unsigned char>( p[0] ) ) |
3656 static_cast<std::uint64_t>( static_cast<unsigned char>( p[1] ) ) << 8 |
3657 static_cast<std::uint64_t>( static_cast<unsigned char>( p[2] ) ) << 16 |
3658 static_cast<std::uint64_t>( static_cast<unsigned char>( p[3] ) ) << 24 |
3659 static_cast<std::uint64_t>( static_cast<unsigned char>( p[4] ) ) << 32 |
3660 static_cast<std::uint64_t>( static_cast<unsigned char>( p[5] ) ) << 40 |
3661 static_cast<std::uint64_t>( static_cast<unsigned char>( p[6] ) ) << 48 |
3662 static_cast<std::uint64_t>( static_cast<unsigned char>( p[7] ) ) << 56;
3663
3664 return w;
3665}
3666
3667#if defined(_MSC_VER) && !defined(__clang__)
3668
3669template<class T> inline std::uint64_t read64le( T* p )
3670{
3671 std::uint64_t w;
3672
3673 std::memcpy( &w, p, 8 );
3674 return w;
3675}
3676
3677#endif
3678
3679template<class It>
3680inline typename std::enable_if<
3681 is_char_type<typename std::iterator_traits<It>::value_type>::value &&
3682 std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
3683 (std::numeric_limits<std::size_t>::digits > 32),
3684std::size_t>::type
3685 hash_range( std::size_t seed, It first, It last )
3686{
3687 It p = first;
3688 std::size_t n = static_cast<std::size_t>( last - first );
3689
3690 std::uint64_t const q = static_cast<std::uint64_t>( 0x9e3779b9 ) << 32 | 0x7f4a7c15;
3691 std::uint64_t const k = static_cast<std::uint64_t>( 0xdf442d22 ) << 32 | 0xce4859b9; // q * q
3692
3693 std::uint64_t w = mulx( seed + q, k );
3694 std::uint64_t h = w ^ n;
3695
3696 while( n >= 8 )
3697 {
3698 std::uint64_t v1 = read64le( p );
3699
3700 w += q;
3701 h ^= mulx( v1 + w, k );
3702
3703 p += 8;
3704 n -= 8;
3705 }
3706
3707 {
3708 std::uint64_t v1 = 0;
3709
3710 if( n >= 4 )
3711 {
3712 v1 = static_cast<std::uint64_t>( read32le( p + static_cast<std::ptrdiff_t>( n - 4 ) ) ) << ( n - 4 ) * 8 | read32le( p );
3713 }
3714 else if( n >= 1 )
3715 {
3716 std::size_t const x1 = ( n - 1 ) & 2; // 1: 0, 2: 0, 3: 2
3717 std::size_t const x2 = n >> 1; // 1: 0, 2: 1, 3: 1
3718
3719 v1 =
3720 static_cast<std::uint64_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x1 ) ] ) ) << x1 * 8 |
3721 static_cast<std::uint64_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x2 ) ] ) ) << x2 * 8 |
3722 static_cast<std::uint64_t>( static_cast<unsigned char>( p[ 0 ] ) );
3723 }
3724
3725 w += q;
3726 h ^= mulx( v1 + w, k );
3727 }
3728
3729 return mulx( h + w, k );
3730}
3731
3732template<class It>
3733inline typename std::enable_if<
3734 is_char_type<typename std::iterator_traits<It>::value_type>::value &&
3735 !std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
3736 (std::numeric_limits<std::size_t>::digits > 32),
3737std::size_t>::type
3738 hash_range( std::size_t seed, It first, It last )
3739{
3740 std::size_t n = 0;
3741
3742 std::uint64_t const q = static_cast<std::uint64_t>( 0x9e3779b9 ) << 32 | 0x7f4a7c15;
3743 std::uint64_t const k = static_cast<std::uint64_t>( 0xdf442d22 ) << 32 | 0xce4859b9; // q * q
3744
3745 std::uint64_t w = mulx( seed + q, k );
3746 std::uint64_t h = w;
3747
3748 std::uint64_t v1 = 0;
3749
3750 for( ;; )
3751 {
3752 v1 = 0;
3753
3754 if( first == last )
3755 {
3756 break;
3757 }
3758
3759 v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) );
3760 ++first;
3761 ++n;
3762
3763 if( first == last )
3764 {
3765 break;
3766 }
3767
3768 v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 8;
3769 ++first;
3770 ++n;
3771
3772 if( first == last )
3773 {
3774 break;
3775 }
3776
3777 v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 16;
3778 ++first;
3779 ++n;
3780
3781 if( first == last )
3782 {
3783 break;
3784 }
3785
3786 v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 24;
3787 ++first;
3788 ++n;
3789
3790 if( first == last )
3791 {
3792 break;
3793 }
3794
3795 v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 32;
3796 ++first;
3797 ++n;
3798
3799 if( first == last )
3800 {
3801 break;
3802 }
3803
3804 v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 40;
3805 ++first;
3806 ++n;
3807
3808 if( first == last )
3809 {
3810 break;
3811 }
3812
3813 v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 48;
3814 ++first;
3815 ++n;
3816
3817 if( first == last )
3818 {
3819 break;
3820 }
3821
3822 v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 56;
3823 ++first;
3824 ++n;
3825
3826 w += q;
3827 h ^= mulx( v1 + w, k );
3828 }
3829
3830 h ^= n;
3831
3832 w += q;
3833 h ^= mulx( v1 + w, k );
3834
3835 return mulx( h + w, k );
3836}
3837
3838} // namespace hash_detail
3839} // namespace boost
3840
3841#endif // #ifndef BOOST_HASH_DETAIL_HASH_RANGE_HPP
3842// Copyright 2005-2014 Daniel James.
3843// Copyright 2021, 2022 Peter Dimov.
3844// Distributed under the Boost Software License, Version 1.0.
3845// https://www.boost.org/LICENSE_1_0.txt
3846
3847// Based on Peter Dimov's proposal
3848// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf
3849// issue 6.18.
3850
3851#ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP
3852#define BOOST_FUNCTIONAL_HASH_HASH_HPP
3853
3854# include <memory>
3855
3856# include <string_view>
3857
3858namespace boost
3859{
3860
3861 //
3862 // boost::hash_value
3863 //
3864
3865 // integral types
3866
3867 namespace hash_detail
3868 {
3869 template<class T,
3870 bool bigger_than_size_t = (sizeof(T) > sizeof(std::size_t)),
3871 bool is_unsigned = std::is_unsigned<T>::value,
3872 std::size_t size_t_bits = sizeof(std::size_t) * CHAR_BIT,
3873 std::size_t type_bits = sizeof(T) * CHAR_BIT>
3874 struct hash_integral_impl;
3875
3876 template<class T, bool is_unsigned, std::size_t size_t_bits, std::size_t type_bits> struct hash_integral_impl<T, false, is_unsigned, size_t_bits, type_bits>
3877 {
3878 static std::size_t fn( T v )
3879 {
3880 return static_cast<std::size_t>( v );
3881 }
3882 };
3883
3884 template<class T, std::size_t size_t_bits, std::size_t type_bits> struct hash_integral_impl<T, true, false, size_t_bits, type_bits>
3885 {
3886 static std::size_t fn( T v )
3887 {
3888 typedef typename std::make_unsigned<T>::type U;
3889
3890 if( v >= 0 )
3891 {
3892 return hash_integral_impl<U>::fn( static_cast<U>( v ) );
3893 }
3894 else
3895 {
3896 return ~hash_integral_impl<U>::fn( static_cast<U>( ~static_cast<U>( v ) ) );
3897 }
3898 }
3899 };
3900
3901 template<class T> struct hash_integral_impl<T, true, true, 32, 64>
3902 {
3903 static std::size_t fn( T v )
3904 {
3905 std::size_t seed = 0;
3906
3907 seed = static_cast<std::size_t>( v >> 32 ) + hash_detail::hash_mix( seed );
3908 seed = static_cast<std::size_t>( v & 0xFFFFFFFF ) + hash_detail::hash_mix( seed );
3909
3910 return seed;
3911 }
3912 };
3913
3914 template<class T> struct hash_integral_impl<T, true, true, 32, 128>
3915 {
3916 static std::size_t fn( T v )
3917 {
3918 std::size_t seed = 0;
3919
3920 seed = static_cast<std::size_t>( v >> 96 ) + hash_detail::hash_mix( seed );
3921 seed = static_cast<std::size_t>( v >> 64 ) + hash_detail::hash_mix( seed );
3922 seed = static_cast<std::size_t>( v >> 32 ) + hash_detail::hash_mix( seed );
3923 seed = static_cast<std::size_t>( v ) + hash_detail::hash_mix( seed );
3924
3925 return seed;
3926 }
3927 };
3928
3929 template<class T> struct hash_integral_impl<T, true, true, 64, 128>
3930 {
3931 static std::size_t fn( T v )
3932 {
3933 std::size_t seed = 0;
3934
3935 seed = static_cast<std::size_t>( v >> 64 ) + hash_detail::hash_mix( seed );
3936 seed = static_cast<std::size_t>( v ) + hash_detail::hash_mix( seed );
3937
3938 return seed;
3939 }
3940 };
3941
3942 } // namespace hash_detail
3943
3944 template <typename T>
3945 typename std::enable_if<std::is_integral<T>::value, std::size_t>::type
3946 hash_value( T v )
3947 {
3948 return hash_detail::hash_integral_impl<T>::fn( v );
3949 }
3950
3951 // enumeration types
3952
3953 template <typename T>
3954 typename std::enable_if<std::is_enum<T>::value, std::size_t>::type
3955 hash_value( T v )
3956 {
3957 // This should in principle return the equivalent of
3958 //
3959 // boost::hash_value( to_underlying(v) );
3960 //
3961 // However, the C++03 implementation of underlying_type,
3962 //
3963 // conditional<is_signed<T>, make_signed<T>, make_unsigned<T>>::type::type
3964 //
3965 // generates a legitimate -Wconversion warning in is_signed,
3966 // because -1 is not a valid enum value when all the enumerators
3967 // are nonnegative.
3968 //
3969 // So the legacy implementation will have to do for now.
3970
3971 return static_cast<std::size_t>( v );
3972 }
3973
3974 // floating point types
3975
3976 namespace hash_detail
3977 {
3978 template<class T,
3979 std::size_t Bits = sizeof(T) * CHAR_BIT,
3980 int Digits = std::numeric_limits<T>::digits>
3981 struct hash_float_impl;
3982
3983 // float
3984 template<class T, int Digits> struct hash_float_impl<T, 32, Digits>
3985 {
3986 static std::size_t fn( T v )
3987 {
3988 std::uint32_t w;
3989 std::memcpy( &w, &v, sizeof( v ) );
3990
3991 return w;
3992 }
3993 };
3994
3995 // double
3996 template<class T, int Digits> struct hash_float_impl<T, 64, Digits>
3997 {
3998 static std::size_t fn( T v )
3999 {
4000 std::uint64_t w;
4001 std::memcpy( &w, &v, sizeof( v ) );
4002
4003 return hash_value( w );
4004 }
4005 };
4006
4007 // 80 bit long double in 12 bytes
4008 template<class T> struct hash_float_impl<T, 96, 64>
4009 {
4010 static std::size_t fn( T v )
4011 {
4012 std::uint64_t w[ 2 ] = {};
4013 std::memcpy( &w, &v, 80 / CHAR_BIT );
4014
4015 std::size_t seed = 0;
4016
4017 seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
4018 seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
4019
4020 return seed;
4021 }
4022 };
4023
4024 // 80 bit long double in 16 bytes
4025 template<class T> struct hash_float_impl<T, 128, 64>
4026 {
4027 static std::size_t fn( T v )
4028 {
4029 std::uint64_t w[ 2 ] = {};
4030 std::memcpy( &w, &v, 80 / CHAR_BIT );
4031
4032 std::size_t seed = 0;
4033
4034 seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
4035 seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
4036
4037 return seed;
4038 }
4039 };
4040
4041 // 128 bit long double
4042 template<class T, int Digits> struct hash_float_impl<T, 128, Digits>
4043 {
4044 static std::size_t fn( T v )
4045 {
4046 std::uint64_t w[ 2 ];
4047 std::memcpy( &w, &v, sizeof( v ) );
4048
4049 std::size_t seed = 0;
4050
4051#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__
4052
4053 seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
4054 seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
4055
4056#else
4057
4058 seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
4059 seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
4060
4061#endif
4062 return seed;
4063 }
4064 };
4065
4066 } // namespace hash_detail
4067
4068 template <typename T>
4069 typename std::enable_if<std::is_floating_point<T>::value, std::size_t>::type
4070 hash_value( T v )
4071 {
4072 return boost::hash_detail::hash_float_impl<T>::fn( v + 0 );
4073 }
4074
4075 // pointer types
4076
4077 // `x + (x >> 3)` adjustment by Alberto Barbati and Dave Harris.
4078 template <class T> std::size_t hash_value( T* const& v )
4079 {
4080 std::uintptr_t x = reinterpret_cast<std::uintptr_t>( v );
4081 return boost::hash_value( x + (x >> 3) );
4082 }
4083
4084 // array types
4085
4086 template<class T, std::size_t N>
4087 inline std::size_t hash_value( T const (&x)[ N ] )
4088 {
4089 return boost::hash_range( x, x + N );
4090 }
4091
4092 template<class T, std::size_t N>
4093 inline std::size_t hash_value( T (&x)[ N ] )
4094 {
4095 return boost::hash_range( x, x + N );
4096 }
4097
4098 // complex
4099
4100 template <class T>
4101 std::size_t hash_value( std::complex<T> const& v )
4102 {
4103 std::size_t re = boost::hash<T>()( v.real() );
4104 std::size_t im = boost::hash<T>()( v.imag() );
4105
4106 return re + hash_detail::hash_mix( im );
4107 }
4108
4109 // pair
4110
4111 template <class A, class B>
4112 std::size_t hash_value( std::pair<A, B> const& v )
4113 {
4114 std::size_t seed = 0;
4115
4116 boost::hash_combine( seed, v.first );
4117 boost::hash_combine( seed, v.second );
4118
4119 return seed;
4120 }
4121
4122 // ranges (list, set, deque...)
4123
4124 template <typename T>
4125 typename std::enable_if<container_hash::is_range<T>::value && !container_hash::is_contiguous_range<T>::value && !container_hash::is_unordered_range<T>::value, std::size_t>::type
4126 hash_value( T const& v )
4127 {
4128 return boost::hash_range( v.begin(), v.end() );
4129 }
4130
4131 // contiguous ranges (string, vector, array)
4132
4133 template <typename T>
4134 typename std::enable_if<container_hash::is_contiguous_range<T>::value, std::size_t>::type
4135 hash_value( T const& v )
4136 {
4137 return boost::hash_range( v.data(), v.data() + v.size() );
4138 }
4139
4140 // unordered ranges (unordered_set, unordered_map)
4141
4142 template <typename T>
4143 typename std::enable_if<container_hash::is_unordered_range<T>::value, std::size_t>::type
4144 hash_value( T const& v )
4145 {
4146 return boost::hash_unordered_range( v.begin(), v.end() );
4147 }
4148
4149#if ( ( defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142 ) || ( !defined(_MSVC_STL_VERSION) && defined(_CPPLIB_VER) && _CPPLIB_VER >= 520 ) )
4150
4151 // resolve ambiguity with unconstrained stdext::hash_value in <xhash> :-/
4152
4153 template<template<class...> class L, class... T>
4154 typename std::enable_if<container_hash::is_range<L<T...>>::value && !container_hash::is_contiguous_range<L<T...>>::value && !container_hash::is_unordered_range<L<T...>>::value, std::size_t>::type
4155 hash_value( L<T...> const& v )
4156 {
4157 return boost::hash_range( v.begin(), v.end() );
4158 }
4159
4160 // contiguous ranges (string, vector, array)
4161
4162 template<template<class...> class L, class... T>
4163 typename std::enable_if<container_hash::is_contiguous_range<L<T...>>::value, std::size_t>::type
4164 hash_value( L<T...> const& v )
4165 {
4166 return boost::hash_range( v.data(), v.data() + v.size() );
4167 }
4168
4169 template<template<class, std::size_t> class L, class T, std::size_t N>
4170 typename std::enable_if<container_hash::is_contiguous_range<L<T, N>>::value, std::size_t>::type
4171 hash_value( L<T, N> const& v )
4172 {
4173 return boost::hash_range( v.data(), v.data() + v.size() );
4174 }
4175
4176 // unordered ranges (unordered_set, unordered_map)
4177
4178 template<template<class...> class L, class... T>
4179 typename std::enable_if<container_hash::is_unordered_range<L<T...>>::value, std::size_t>::type
4180 hash_value( L<T...> const& v )
4181 {
4182 return boost::hash_unordered_range( v.begin(), v.end() );
4183 }
4184
4185#endif
4186
4187 // std::unique_ptr, std::shared_ptr
4188
4189 template <typename T>
4190 std::size_t hash_value( std::shared_ptr<T> const& x )
4191 {
4192 return boost::hash_value( x.get() );
4193 }
4194
4195 template <typename T, typename Deleter>
4196 std::size_t hash_value( std::unique_ptr<T, Deleter> const& x )
4197 {
4198 return boost::hash_value( x.get() );
4199 }
4200
4201 // std::type_index
4202
4203 inline std::size_t hash_value( std::type_index const& v )
4204 {
4205 return v.hash_code();
4206 }
4207
4208 // std::error_code, std::error_condition
4209
4210 inline std::size_t hash_value( std::error_code const& v )
4211 {
4212 std::size_t seed = 0;
4213
4214 boost::hash_combine( seed, v.value() );
4215 boost::hash_combine( seed, &v.category() );
4216
4217 return seed;
4218 }
4219
4220 inline std::size_t hash_value( std::error_condition const& v )
4221 {
4222 std::size_t seed = 0;
4223
4224 boost::hash_combine( seed, v.value() );
4225 boost::hash_combine( seed, &v.category() );
4226
4227 return seed;
4228 }
4229
4230 // std::nullptr_t
4231
4232 template <typename T>
4233 typename std::enable_if<std::is_same<T, std::nullptr_t>::value, std::size_t>::type
4234 hash_value( T const& /*v*/ )
4235 {
4236 return boost::hash_value( static_cast<void*>( nullptr ) );
4237 }
4238
4239 // std::optional
4240
4241 template <typename T>
4242 std::size_t hash_value( std::optional<T> const& v )
4243 {
4244 if( !v )
4245 {
4246 // Arbitray value for empty optional.
4247 return 0x12345678;
4248 }
4249 else
4250 {
4251 return boost::hash<T>()(*v);
4252 }
4253 }
4254
4255 // std::variant
4256
4257 inline std::size_t hash_value( std::monostate )
4258 {
4259 return 0x87654321;
4260 }
4261
4262 template <typename... Types>
4263 std::size_t hash_value( std::variant<Types...> const& v )
4264 {
4265 std::size_t seed = 0;
4266
4267 hash_combine( seed, v.index() );
4268 std::visit( [&seed](auto&& x) { hash_combine(seed, x); }, v );
4269
4270 return seed;
4271 }
4272
4273 //
4274 // boost::hash_combine
4275 //
4276
4277 template <class T>
4278 inline void hash_combine( std::size_t& seed, T const& v )
4279 {
4280 seed = boost::hash_detail::hash_mix( seed + 0x9e3779b9 + boost::hash<T>()( v ) );
4281 }
4282
4283 //
4284 // boost::hash_range
4285 //
4286
4287 template <class It>
4288 inline void hash_range( std::size_t& seed, It first, It last )
4289 {
4290 seed = hash_detail::hash_range( seed, first, last );
4291 }
4292
4293 template <class It>
4294 inline std::size_t hash_range( It first, It last )
4295 {
4296 std::size_t seed = 0;
4297
4298 hash_range( seed, first, last );
4299
4300 return seed;
4301 }
4302
4303 //
4304 // boost::hash_unordered_range
4305 //
4306
4307 template <class It>
4308 inline void hash_unordered_range( std::size_t& seed, It first, It last )
4309 {
4310 std::size_t r = 0;
4311 std::size_t const s2( seed );
4312
4313 for( ; first != last; ++first )
4314 {
4315 std::size_t s3( s2 );
4316
4317 hash_combine<typename std::iterator_traits<It>::value_type>( s3, *first );
4318
4319 r += s3;
4320 }
4321
4322 seed += r;
4323 }
4324
4325 template <class It>
4326 inline std::size_t hash_unordered_range( It first, It last )
4327 {
4328 std::size_t seed = 0;
4329
4330 hash_unordered_range( seed, first, last );
4331
4332 return seed;
4333 }
4334
4335 //
4336 // boost::hash
4337 //
4338
4339 template <class T> struct hash
4340 {
4341 typedef T argument_type;
4342 typedef std::size_t result_type;
4343
4344 std::size_t operator()( T const& val ) const
4345 {
4346 return hash_value( val );
4347 }
4348 };
4349
4350#if ( ( defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142 ) || ( !defined(_MSVC_STL_VERSION) && defined(_CPPLIB_VER) && _CPPLIB_VER >= 520 ) )
4351
4352 // Dinkumware has stdext::hash_value for basic_string in <xhash> :-/
4353
4354 template<class E, class T, class A> struct hash< std::basic_string<E, T, A> >
4355 {
4356 typedef std::basic_string<E, T, A> argument_type;
4357 typedef std::size_t result_type;
4358
4359 std::size_t operator()( std::basic_string<E, T, A> const& val ) const
4360 {
4361 return boost::hash_value( val );
4362 }
4363 };
4364
4365#endif
4366
4367 // boost::unordered::hash_is_avalanching
4368
4369 namespace unordered
4370 {
4371 template<class T> struct hash_is_avalanching;
4372 template<class Ch> struct hash_is_avalanching< boost::hash< std::basic_string<Ch> > >: std::is_integral<Ch> {};
4373
4374#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
4375 template<> struct hash_is_avalanching< boost::hash< std::basic_string<char8_t> > >: std::true_type {};
4376#endif
4377
4378 template<class Ch> struct hash_is_avalanching< boost::hash< std::basic_string_view<Ch> > >: std::is_integral<Ch> {};
4379
4380#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
4381 template<> struct hash_is_avalanching< boost::hash< std::basic_string_view<char8_t> > >: std::true_type {};
4382#endif
4383
4384 } // namespace unordered
4385
4386} // namespace boost
4387
4388#endif // #ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP
4389// Copyright (C) 2022 Christian Mazakas
4390// Distributed under the Boost Software License, Version 1.0. (See accompanying
4391// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
4392
4393#ifndef BOOST_UNORDERED_UNORDERED_FLAT_SET_HPP_INCLUDED
4394#define BOOST_UNORDERED_UNORDERED_FLAT_SET_HPP_INCLUDED
4395
4396#pragma once
4397
4398namespace boost {
4399 namespace unordered {
4400
4401#ifdef BOOST_MSVC
4402#pragma warning(push)
4403#pragma warning(disable : 4714)
4404#endif
4405
4406 namespace detail {
4407 template <class Key> struct flat_set_types
4408 {
4409 using key_type = Key;
4410 using init_type = Key;
4411 using value_type = Key;
4412
4413 static Key const& extract(value_type const& key) { return key; }
4414
4415 using element_type = value_type;
4416
4417 static Key& value_from(element_type& x) { return x; }
4418
4419 static element_type&& move(element_type& x) { return std::move(x); }
4420
4421 template <class A, class... Args>
4422 static void construct(A& al, value_type* p, Args&&... args)
4423 {
4424 std::allocator_traits<A>::construct(al, p, std::forward<Args>(args)...);
4425 }
4426
4427 template <class A> static void destroy(A& al, value_type* p) noexcept
4428 {
4429 std::allocator_traits<A>::destroy(al, p);
4430 }
4431 };
4432 } // namespace detail
4433
4434 template <class Key, class Hash, class KeyEqual, class Allocator>
4435 class unordered_flat_set
4436 {
4437 using set_types = detail::flat_set_types<Key>;
4438
4439 using table_type = detail::foa::table<set_types, Hash, KeyEqual,
4440 typename std::allocator_traits<Allocator>::template rebind_alloc<
4441 typename set_types::value_type>>;
4442
4443 table_type table_;
4444
4445 template <class K, class H, class KE, class A, class Pred>
4446 typename unordered_flat_set<K, H, KE, A>::size_type friend erase_if(
4447 unordered_flat_set<K, H, KE, A>& set, Pred pred);
4448
4449 public:
4450 using key_type = Key;
4451 using value_type = typename set_types::value_type;
4452 using init_type = typename set_types::init_type;
4453 using size_type = std::size_t;
4454 using difference_type = std::ptrdiff_t;
4455 using hasher = Hash;
4456 using key_equal = KeyEqual;
4457 using allocator_type = Allocator;
4458 using reference = value_type&;
4459 using const_reference = value_type const&;
4460 using pointer = typename std::allocator_traits<allocator_type>::pointer;
4461 using const_pointer =
4462 typename std::allocator_traits<allocator_type>::const_pointer;
4463 using iterator = typename table_type::iterator;
4464 using const_iterator = typename table_type::const_iterator;
4465
4466 unordered_flat_set() : unordered_flat_set(0) {}
4467
4468 explicit unordered_flat_set(size_type n, hasher const& h = hasher(),
4469 key_equal const& pred = key_equal(),
4470 allocator_type const& a = allocator_type())
4471 : table_(n, h, pred, a)
4472 {
4473 }
4474
4475 unordered_flat_set(size_type n, allocator_type const& a)
4476 : unordered_flat_set(n, hasher(), key_equal(), a)
4477 {
4478 }
4479
4480 unordered_flat_set(size_type n, hasher const& h, allocator_type const& a)
4481 : unordered_flat_set(n, h, key_equal(), a)
4482 {
4483 }
4484
4485 template <class InputIterator>
4486 unordered_flat_set(
4487 InputIterator f, InputIterator l, allocator_type const& a)
4488 : unordered_flat_set(f, l, size_type(0), hasher(), key_equal(), a)
4489 {
4490 }
4491
4492 explicit unordered_flat_set(allocator_type const& a)
4493 : unordered_flat_set(0, a)
4494 {
4495 }
4496
4497 template <class Iterator>
4498 unordered_flat_set(Iterator first, Iterator last, size_type n = 0,
4499 hasher const& h = hasher(), key_equal const& pred = key_equal(),
4500 allocator_type const& a = allocator_type())
4501 : unordered_flat_set(n, h, pred, a)
4502 {
4503 this->insert(first, last);
4504 }
4505
4506 template <class InputIt>
4507 unordered_flat_set(
4508 InputIt first, InputIt last, size_type n, allocator_type const& a)
4509 : unordered_flat_set(first, last, n, hasher(), key_equal(), a)
4510 {
4511 }
4512
4513 template <class Iterator>
4514 unordered_flat_set(Iterator first, Iterator last, size_type n,
4515 hasher const& h, allocator_type const& a)
4516 : unordered_flat_set(first, last, n, h, key_equal(), a)
4517 {
4518 }
4519
4520 unordered_flat_set(unordered_flat_set const& other) : table_(other.table_)
4521 {
4522 }
4523
4524 unordered_flat_set(
4525 unordered_flat_set const& other, allocator_type const& a)
4526 : table_(other.table_, a)
4527 {
4528 }
4529
4530 unordered_flat_set(unordered_flat_set&& other)
4531 noexcept(std::is_nothrow_move_constructible<hasher>::value&&
4532 std::is_nothrow_move_constructible<key_equal>::value&&
4533 std::is_nothrow_move_constructible<allocator_type>::value)
4534 : table_(std::move(other.table_))
4535 {
4536 }
4537
4538 unordered_flat_set(unordered_flat_set&& other, allocator_type const& al)
4539 : table_(std::move(other.table_), al)
4540 {
4541 }
4542
4543 unordered_flat_set(std::initializer_list<value_type> ilist,
4544 size_type n = 0, hasher const& h = hasher(),
4545 key_equal const& pred = key_equal(),
4546 allocator_type const& a = allocator_type())
4547 : unordered_flat_set(ilist.begin(), ilist.end(), n, h, pred, a)
4548 {
4549 }
4550
4551 unordered_flat_set(
4552 std::initializer_list<value_type> il, allocator_type const& a)
4553 : unordered_flat_set(il, size_type(0), hasher(), key_equal(), a)
4554 {
4555 }
4556
4557 unordered_flat_set(std::initializer_list<value_type> init, size_type n,
4558 allocator_type const& a)
4559 : unordered_flat_set(init, n, hasher(), key_equal(), a)
4560 {
4561 }
4562
4563 unordered_flat_set(std::initializer_list<value_type> init, size_type n,
4564 hasher const& h, allocator_type const& a)
4565 : unordered_flat_set(init, n, h, key_equal(), a)
4566 {
4567 }
4568
4569 ~unordered_flat_set() = default;
4570
4571 unordered_flat_set& operator=(unordered_flat_set const& other)
4572 {
4573 table_ = other.table_;
4574 return *this;
4575 }
4576
4577 unordered_flat_set& operator=(unordered_flat_set&& other) noexcept(
4578 noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
4579 {
4580 table_ = std::move(other.table_);
4581 return *this;
4582 }
4583
4584 allocator_type get_allocator() const noexcept
4585 {
4586 return table_.get_allocator();
4587 }
4588
4589 /// Iterators
4590 ///
4591
4592 iterator begin() noexcept { return table_.begin(); }
4593 const_iterator begin() const noexcept { return table_.begin(); }
4594 const_iterator cbegin() const noexcept { return table_.cbegin(); }
4595
4596 iterator end() noexcept { return table_.end(); }
4597 const_iterator end() const noexcept { return table_.end(); }
4598 const_iterator cend() const noexcept { return table_.cend(); }
4599
4600 /// Capacity
4601 ///
4602
4603 [[nodiscard]] bool empty() const noexcept
4604 {
4605 return table_.empty();
4606 }
4607
4608 size_type size() const noexcept { return table_.size(); }
4609
4610 size_type max_size() const noexcept { return table_.max_size(); }
4611
4612 /// Modifiers
4613 ///
4614
4615 void clear() noexcept { table_.clear(); }
4616
4617 BOOST_FORCEINLINE std::pair<iterator, bool> insert(
4618 value_type const& value)
4619 {
4620 return table_.insert(value);
4621 }
4622
4623 BOOST_FORCEINLINE std::pair<iterator, bool> insert(value_type&& value)
4624 {
4625 return table_.insert(std::move(value));
4626 }
4627
4628 template <class K>
4629 BOOST_FORCEINLINE typename std::enable_if<
4630 detail::transparent_non_iterable<K, unordered_flat_set>::value,
4631 std::pair<iterator, bool> >::type
4632 insert(K&& k)
4633 {
4634 return table_.try_emplace(std::forward<K>(k));
4635 }
4636
4637 BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value)
4638 {
4639 return table_.insert(value).first;
4640 }
4641
4642 BOOST_FORCEINLINE iterator insert(const_iterator, value_type&& value)
4643 {
4644 return table_.insert(std::move(value)).first;
4645 }
4646
4647 template <class K>
4648 BOOST_FORCEINLINE typename std::enable_if<
4649 detail::transparent_non_iterable<K, unordered_flat_set>::value,
4650 iterator>::type
4651 insert(const_iterator, K&& k)
4652 {
4653 return table_.try_emplace(std::forward<K>(k)).first;
4654 }
4655
4656 template <class InputIterator>
4657 void insert(InputIterator first, InputIterator last)
4658 {
4659 for (auto pos = first; pos != last; ++pos) {
4660 table_.emplace(*pos);
4661 }
4662 }
4663
4664 void insert(std::initializer_list<value_type> ilist)
4665 {
4666 this->insert(ilist.begin(), ilist.end());
4667 }
4668
4669 template <class... Args>
4670 BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
4671 {
4672 return table_.emplace(std::forward<Args>(args)...);
4673 }
4674
4675 template <class... Args>
4676 BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
4677 {
4678 return table_.emplace(std::forward<Args>(args)...).first;
4679 }
4680
4681 BOOST_FORCEINLINE void erase(const_iterator pos)
4682 {
4683 return table_.erase(pos);
4684 }
4685 iterator erase(const_iterator first, const_iterator last)
4686 {
4687 while (first != last) {
4688 this->erase(first++);
4689 }
4690 return iterator{detail::foa::const_iterator_cast_tag{}, last};
4691 }
4692
4693 BOOST_FORCEINLINE size_type erase(key_type const& key)
4694 {
4695 return table_.erase(key);
4696 }
4697
4698 template <class K>
4699 BOOST_FORCEINLINE typename std::enable_if<
4700 detail::transparent_non_iterable<K, unordered_flat_set>::value,
4701 size_type>::type
4702 erase(K const& key)
4703 {
4704 return table_.erase(key);
4705 }
4706
4707 void swap(unordered_flat_set& rhs) noexcept(
4708 noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
4709 {
4710 table_.swap(rhs.table_);
4711 }
4712
4713 template <class H2, class P2>
4714 void merge(unordered_flat_set<key_type, H2, P2, allocator_type>& source)
4715 {
4716 table_.merge(source.table_);
4717 }
4718
4719 template <class H2, class P2>
4720 void merge(unordered_flat_set<key_type, H2, P2, allocator_type>&& source)
4721 {
4722 table_.merge(std::move(source.table_));
4723 }
4724
4725 /// Lookup
4726 ///
4727
4728 BOOST_FORCEINLINE size_type count(key_type const& key) const
4729 {
4730 auto pos = table_.find(key);
4731 return pos != table_.end() ? 1 : 0;
4732 }
4733
4734 template <class K>
4735 BOOST_FORCEINLINE typename std::enable_if<
4736 detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
4737 count(K const& key) const
4738 {
4739 auto pos = table_.find(key);
4740 return pos != table_.end() ? 1 : 0;
4741 }
4742
4743 BOOST_FORCEINLINE iterator find(key_type const& key)
4744 {
4745 return table_.find(key);
4746 }
4747
4748 BOOST_FORCEINLINE const_iterator find(key_type const& key) const
4749 {
4750 return table_.find(key);
4751 }
4752
4753 template <class K>
4754 BOOST_FORCEINLINE typename std::enable_if<
4755 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
4756 iterator>::type
4757 find(K const& key)
4758 {
4759 return table_.find(key);
4760 }
4761
4762 template <class K>
4763 BOOST_FORCEINLINE typename std::enable_if<
4764 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
4765 const_iterator>::type
4766 find(K const& key) const
4767 {
4768 return table_.find(key);
4769 }
4770
4771 BOOST_FORCEINLINE bool contains(key_type const& key) const
4772 {
4773 return this->find(key) != this->end();
4774 }
4775
4776 template <class K>
4777 BOOST_FORCEINLINE typename std::enable_if<
4778 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
4779 bool>::type
4780 contains(K const& key) const
4781 {
4782 return this->find(key) != this->end();
4783 }
4784
4785 std::pair<iterator, iterator> equal_range(key_type const& key)
4786 {
4787 auto pos = table_.find(key);
4788 if (pos == table_.end()) {
4789 return {pos, pos};
4790 }
4791
4792 auto next = pos;
4793 ++next;
4794 return {pos, next};
4795 }
4796
4797 std::pair<const_iterator, const_iterator> equal_range(
4798 key_type const& key) const
4799 {
4800 auto pos = table_.find(key);
4801 if (pos == table_.end()) {
4802 return {pos, pos};
4803 }
4804
4805 auto next = pos;
4806 ++next;
4807 return {pos, next};
4808 }
4809
4810 template <class K>
4811 typename std::enable_if<
4812 detail::are_transparent<K, hasher, key_equal>::value,
4813 std::pair<iterator, iterator> >::type
4814 equal_range(K const& key)
4815 {
4816 auto pos = table_.find(key);
4817 if (pos == table_.end()) {
4818 return {pos, pos};
4819 }
4820
4821 auto next = pos;
4822 ++next;
4823 return {pos, next};
4824 }
4825
4826 template <class K>
4827 typename std::enable_if<
4828 detail::are_transparent<K, hasher, key_equal>::value,
4829 std::pair<const_iterator, const_iterator> >::type
4830 equal_range(K const& key) const
4831 {
4832 auto pos = table_.find(key);
4833 if (pos == table_.end()) {
4834 return {pos, pos};
4835 }
4836
4837 auto next = pos;
4838 ++next;
4839 return {pos, next};
4840 }
4841
4842 /// Hash Policy
4843 ///
4844
4845 size_type bucket_count() const noexcept { return table_.capacity(); }
4846
4847 float load_factor() const noexcept { return table_.load_factor(); }
4848
4849 float max_load_factor() const noexcept
4850 {
4851 return table_.max_load_factor();
4852 }
4853
4854 void max_load_factor(float) {}
4855
4856 size_type max_load() const noexcept { return table_.max_load(); }
4857
4858 void rehash(size_type n) { table_.rehash(n); }
4859
4860 void reserve(size_type n) { table_.reserve(n); }
4861
4862 /// Observers
4863 ///
4864
4865 hasher hash_function() const { return table_.hash_function(); }
4866
4867 key_equal key_eq() const { return table_.key_eq(); }
4868 };
4869
4870 template <class Key, class Hash, class KeyEqual, class Allocator>
4871 bool operator==(
4872 unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
4873 unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
4874 {
4875 if (&lhs == &rhs) {
4876 return true;
4877 }
4878
4879 return (lhs.size() == rhs.size()) && ([&] {
4880 for (auto const& key : lhs) {
4881 auto pos = rhs.find(key);
4882 if ((pos == rhs.end()) || (key != *pos)) {
4883 return false;
4884 }
4885 }
4886 return true;
4887 })();
4888 }
4889
4890 template <class Key, class Hash, class KeyEqual, class Allocator>
4891 bool operator!=(
4892 unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
4893 unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
4894 {
4895 return !(lhs == rhs);
4896 }
4897
4898 template <class Key, class Hash, class KeyEqual, class Allocator>
4899 void swap(unordered_flat_set<Key, Hash, KeyEqual, Allocator>& lhs,
4900 unordered_flat_set<Key, Hash, KeyEqual, Allocator>& rhs)
4901 noexcept(noexcept(lhs.swap(rhs)))
4902 {
4903 lhs.swap(rhs);
4904 }
4905
4906 template <class Key, class Hash, class KeyEqual, class Allocator,
4907 class Pred>
4908 typename unordered_flat_set<Key, Hash, KeyEqual, Allocator>::size_type
4909 erase_if(unordered_flat_set<Key, Hash, KeyEqual, Allocator>& set, Pred pred)
4910 {
4911 return erase_if(set.table_, pred);
4912 }
4913
4914#ifdef BOOST_MSVC
4915#pragma warning(pop)
4916#endif
4917
4919 template <class InputIterator,
4920 class Hash =
4921 boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
4922 class Pred =
4923 std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
4924 class Allocator = std::allocator<
4925 typename std::iterator_traits<InputIterator>::value_type>,
4926 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
4927 class = std::enable_if_t<detail::is_hash_v<Hash> >,
4928 class = std::enable_if_t<detail::is_pred_v<Pred> >,
4929 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
4930 unordered_flat_set(InputIterator, InputIterator,
4931 std::size_t = boost::unordered::detail::foa::default_bucket_count,
4932 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
4933 -> unordered_flat_set<
4934 typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
4935 Allocator>;
4936
4937 template <class T, class Hash = boost::hash<T>,
4938 class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
4939 class = std::enable_if_t<detail::is_hash_v<Hash> >,
4940 class = std::enable_if_t<detail::is_pred_v<Pred> >,
4941 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
4942 unordered_flat_set(std::initializer_list<T>,
4943 std::size_t = boost::unordered::detail::foa::default_bucket_count,
4944 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
4945 -> unordered_flat_set<T, Hash, Pred, Allocator>;
4946
4947 template <class InputIterator, class Allocator,
4948 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
4949 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
4950 unordered_flat_set(InputIterator, InputIterator, std::size_t, Allocator)
4951 -> unordered_flat_set<
4952 typename std::iterator_traits<InputIterator>::value_type,
4953 boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
4954 std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
4955 Allocator>;
4956
4957 template <class InputIterator, class Hash, class Allocator,
4958 class = std::enable_if_t<detail::is_hash_v<Hash> >,
4959 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
4960 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
4961 unordered_flat_set(
4962 InputIterator, InputIterator, std::size_t, Hash, Allocator)
4963 -> unordered_flat_set<
4964 typename std::iterator_traits<InputIterator>::value_type, Hash,
4965 std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
4966 Allocator>;
4967
4968 template <class T, class Allocator,
4969 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
4970 unordered_flat_set(std::initializer_list<T>, std::size_t, Allocator)
4971 -> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
4972
4973 template <class T, class Hash, class Allocator,
4974 class = std::enable_if_t<detail::is_hash_v<Hash> >,
4975 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
4976 unordered_flat_set(std::initializer_list<T>, std::size_t, Hash, Allocator)
4977 -> unordered_flat_set<T, Hash, std::equal_to<T>, Allocator>;
4978
4979 template <class InputIterator, class Allocator,
4980 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
4981 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
4982 unordered_flat_set(InputIterator, InputIterator, Allocator)
4983 -> unordered_flat_set<
4984 typename std::iterator_traits<InputIterator>::value_type,
4985 boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
4986 std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
4987 Allocator>;
4988
4989 template <class T, class Allocator,
4990 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
4991 unordered_flat_set(std::initializer_list<T>, Allocator)
4992 -> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
4993#endif
4994
4995 } // namespace unordered
4996} // namespace boost
4997
4998#endif
4999// Copyright (C) 2022 Christian Mazakas
5000// Distributed under the Boost Software License, Version 1.0. (See accompanying
5001// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5002
5003#ifndef BOOST_UNORDERED_FLAT_MAP_FWD_HPP_INCLUDED
5004#define BOOST_UNORDERED_FLAT_MAP_FWD_HPP_INCLUDED
5005
5006#pragma once
5007
5008namespace boost {
5009 namespace unordered {
5010 template <class Key, class T, class Hash = boost::hash<Key>,
5011 class KeyEqual = std::equal_to<Key>,
5012 class Allocator = std::allocator<std::pair<const Key, T> > >
5013 class unordered_flat_map;
5014
5015 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
5016 bool operator==(
5017 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
5018 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
5019
5020 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
5021 bool operator!=(
5022 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
5023 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
5024
5025 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
5026 void swap(unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
5027 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
5028 noexcept(noexcept(lhs.swap(rhs)));
5029 } // namespace unordered
5030
5031 using boost::unordered::unordered_flat_map;
5032
5033 using boost::unordered::swap;
5034 using boost::unordered::operator==;
5035 using boost::unordered::operator!=;
5036} // namespace boost
5037
5038#endif
5039#ifndef BOOST_ASSERT_SOURCE_LOCATION_HPP_INCLUDED
5040#define BOOST_ASSERT_SOURCE_LOCATION_HPP_INCLUDED
5041
5042// http://www.boost.org/libs/assert
5043//
5044// Copyright 2019, 2021 Peter Dimov
5045// Distributed under the Boost Software License, Version 1.0.
5046// http://www.boost.org/LICENSE_1_0.txt
5047
5048#if defined(__cpp_lib_source_location) && __cpp_lib_source_location >= 201907L
5049# include <source_location>
5050#endif
5051
5052namespace boost
5053{
5054
5055struct source_location
5056{
5057private:
5058
5059 char const * file_;
5060 char const * function_;
5061 std::uint_least32_t line_;
5062 std::uint_least32_t column_;
5063
5064public:
5065
5066 constexpr source_location() noexcept: file_( "" ), function_( "" ), line_( 0 ), column_( 0 )
5067 {
5068 }
5069
5070 constexpr source_location( char const * file, std::uint_least32_t ln, char const * function, std::uint_least32_t col = 0 ) noexcept: file_( file ), function_( function ), line_( ln ), column_( col )
5071 {
5072 }
5073
5074#if defined(__cpp_lib_source_location) && __cpp_lib_source_location >= 201907L
5075
5076 constexpr source_location( std::source_location const& loc ) noexcept: file_( loc.file_name() ), function_( loc.function_name() ), line_( loc.line() ), column_( loc.column() )
5077 {
5078 }
5079
5080#endif
5081
5082 constexpr char const * file_name() const noexcept
5083 {
5084 return file_;
5085 }
5086
5087 constexpr char const * function_name() const noexcept
5088 {
5089 return function_;
5090 }
5091
5092 constexpr std::uint_least32_t line() const noexcept
5093 {
5094 return line_;
5095 }
5096
5097 constexpr std::uint_least32_t column() const noexcept
5098 {
5099 return column_;
5100 }
5101
5102#ifdef BOOST_MSVC
5103# pragma warning( push )
5104# pragma warning( disable: 4996 )
5105#endif
5106
5107#if ( defined(_MSC_VER) && _MSC_VER < 1900 ) || ( defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) )
5108# define BOOST_ASSERT_SNPRINTF(buffer, format, arg) std::sprintf(buffer, format, arg)
5109#else
5110# define BOOST_ASSERT_SNPRINTF(buffer, format, arg) std::snprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), format, arg)
5111#endif
5112
5113 std::string to_string() const
5114 {
5115 unsigned long ln = line();
5116
5117 if( ln == 0 )
5118 {
5119 return "(unknown source location)";
5120 }
5121
5122 std::string r = file_name();
5123
5124 char buffer[ 16 ];
5125
5126 BOOST_ASSERT_SNPRINTF( buffer, ":%lu", ln );
5127 r += buffer;
5128
5129 unsigned long co = column();
5130
5131 if( co )
5132 {
5133 BOOST_ASSERT_SNPRINTF( buffer, ":%lu", co );
5134 r += buffer;
5135 }
5136
5137 char const* fn = function_name();
5138
5139 if( *fn != 0 )
5140 {
5141 r += " in function '";
5142 r += fn;
5143 r += '\'';
5144 }
5145
5146 return r;
5147 }
5148
5149#undef BOOST_ASSERT_SNPRINTF
5150
5151#ifdef BOOST_MSVC
5152# pragma warning( pop )
5153#endif
5154
5155 inline friend bool operator==( source_location const& s1, source_location const& s2 ) noexcept
5156 {
5157 return std::strcmp( s1.file_, s2.file_ ) == 0 && std::strcmp( s1.function_, s2.function_ ) == 0 && s1.line_ == s2.line_ && s1.column_ == s2.column_;
5158 }
5159
5160 inline friend bool operator!=( source_location const& s1, source_location const& s2 ) noexcept
5161 {
5162 return !( s1 == s2 );
5163 }
5164};
5165
5166template<class E, class T> std::basic_ostream<E, T> & operator<<( std::basic_ostream<E, T> & os, source_location const & loc )
5167{
5168 os << loc.to_string();
5169 return os;
5170}
5171
5172} // namespace boost
5173
5174#ifdef BOOST_DISABLE_CURRENT_LOCATION
5175
5176# define BOOST_CURRENT_LOCATION ::boost::source_location()
5177
5178#elif defined(BOOST_MSVC) && BOOST_MSVC >= 1926
5179
5180// std::source_location::current() is available in -std:c++20, but fails with consteval errors before 19.31, and doesn't produce
5181// the correct result under 19.31, so prefer the built-ins
5182# define BOOST_CURRENT_LOCATION ::boost::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN())
5183
5184#elif defined(BOOST_MSVC)
5185
5186// __LINE__ is not a constant expression under /ZI (edit and continue) for 1925 and before
5187
5188# define BOOST_CURRENT_LOCATION_IMPL_1(x) BOOST_CURRENT_LOCATION_IMPL_2(x)
5189# define BOOST_CURRENT_LOCATION_IMPL_2(x) (x##0 / 10)
5190
5191# define BOOST_CURRENT_LOCATION ::boost::source_location(__FILE__, BOOST_CURRENT_LOCATION_IMPL_1(__LINE__), "")
5192
5193#elif defined(__cpp_lib_source_location) && __cpp_lib_source_location >= 201907L
5194
5195# define BOOST_CURRENT_LOCATION ::boost::source_location(::std::source_location::current())
5196
5197#elif defined(BOOST_CLANG)
5198
5199# define BOOST_CURRENT_LOCATION ::boost::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN())
5200
5201#elif defined(BOOST_GCC) && BOOST_GCC >= 70000
5202
5203// The built-ins are available in 4.8+, but are not constant expressions until 7
5204# define BOOST_CURRENT_LOCATION ::boost::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())
5205
5206#elif defined(BOOST_GCC) && BOOST_GCC >= 50000
5207
5208// __PRETTY_FUNCTION__ is allowed outside functions under GCC, but 4.x suffers from codegen bugs
5209# define BOOST_CURRENT_LOCATION ::boost::source_location(__FILE__, __LINE__, __PRETTY_FUNCTION__)
5210
5211#else
5212
5213// __func__ macros aren't allowed outside functions, but BOOST_CURRENT_LOCATION is
5214# define BOOST_CURRENT_LOCATION ::boost::source_location(__FILE__, __LINE__, "")
5215
5216#endif
5217
5218#endif // #ifndef BOOST_ASSERT_SOURCE_LOCATION_HPP_INCLUDED
5219//Copyright (c) 2006-2009 Emil Dotchevski and Reverge Studios, Inc.
5220
5221//Distributed under the Boost Software License, Version 1.0. (See accompanying
5222//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5223
5224#ifndef BOOST_EXCEPTION_274DA366004E11DCB1DDFE2E56D89593
5225#define BOOST_EXCEPTION_274DA366004E11DCB1DDFE2E56D89593
5226
5227#ifdef BOOST_EXCEPTION_MINI_BOOST
5228#include <memory>
5229namespace boost { namespace exception_detail { using std::shared_ptr; } }
5230#else
5231namespace boost { template <class T> class shared_ptr; }
5232namespace boost { namespace exception_detail { using boost::shared_ptr; } }
5233#endif
5234
5235#ifndef BOOST_EXCEPTION_ENABLE_WARNINGS
5236#if __GNUC__*100+__GNUC_MINOR__>301
5237#pragma GCC system_header
5238#endif
5239#ifdef __clang__
5240#pragma clang system_header
5241#endif
5242#ifdef _MSC_VER
5243#pragma warning(push,1)
5244#pragma warning(disable: 4265)
5245#endif
5246#endif
5247
5248namespace
5249boost
5250 {
5251 namespace
5252 exception_detail
5253 {
5254 template <class T>
5255 class
5256 refcount_ptr
5257 {
5258 public:
5259
5260 refcount_ptr():
5261 px_(0)
5262 {
5263 }
5264
5265 ~refcount_ptr()
5266 {
5267 release();
5268 }
5269
5270 refcount_ptr( refcount_ptr const & x ):
5271 px_(x.px_)
5272 {
5273 add_ref();
5274 }
5275
5276 refcount_ptr &
5277 operator=( refcount_ptr const & x )
5278 {
5279 adopt(x.px_);
5280 return *this;
5281 }
5282
5283 void
5284 adopt( T * px )
5285 {
5286 release();
5287 px_=px;
5288 add_ref();
5289 }
5290
5291 T *
5292 get() const
5293 {
5294 return px_;
5295 }
5296
5297 private:
5298
5299 T * px_;
5300
5301 void
5302 add_ref()
5303 {
5304 if( px_ )
5305 px_->add_ref();
5306 }
5307
5308 void
5309 release()
5310 {
5311 if( px_ && px_->release() )
5312 px_=0;
5313 }
5314 };
5315 }
5316
5317 ////////////////////////////////////////////////////////////////////////
5318
5319 template <class Tag,class T>
5320 class error_info;
5321
5322 typedef error_info<struct throw_function_,char const *> throw_function;
5323 typedef error_info<struct throw_file_,char const *> throw_file;
5324 typedef error_info<struct throw_line_,int> throw_line;
5325 typedef error_info<struct throw_column_,int> throw_column;
5326
5327 template <>
5328 class
5329 error_info<throw_function_,char const *>
5330 {
5331 public:
5332 typedef char const * value_type;
5333 value_type v_;
5334 explicit
5335 error_info( value_type v ):
5336 v_(v)
5337 {
5338 }
5339 };
5340
5341 template <>
5342 class
5343 error_info<throw_file_,char const *>
5344 {
5345 public:
5346 typedef char const * value_type;
5347 value_type v_;
5348 explicit
5349 error_info( value_type v ):
5350 v_(v)
5351 {
5352 }
5353 };
5354
5355 template <>
5356 class
5357 error_info<throw_line_,int>
5358 {
5359 public:
5360 typedef int value_type;
5361 value_type v_;
5362 explicit
5363 error_info( value_type v ):
5364 v_(v)
5365 {
5366 }
5367 };
5368
5369 template <>
5370 class
5371 error_info<throw_column_,int>
5372 {
5373 public:
5374 typedef int value_type;
5375 value_type v_;
5376 explicit
5377 error_info( value_type v ):
5378 v_(v)
5379 {
5380 }
5381 };
5382
5383 class
5385 exception;
5386
5387 namespace
5388 exception_detail
5389 {
5390 class error_info_base;
5391 struct type_info_;
5392
5393 struct
5394 error_info_container
5395 {
5396 virtual char const * diagnostic_information( char const * ) const = 0;
5397 virtual shared_ptr<error_info_base> get( type_info_ const & ) const = 0;
5398 virtual void set( shared_ptr<error_info_base> const &, type_info_ const & ) = 0;
5399 virtual void add_ref() const = 0;
5400 virtual bool release() const = 0;
5401 virtual refcount_ptr<exception_detail::error_info_container> clone() const = 0;
5402
5403 protected:
5404
5405 ~error_info_container() noexcept
5406 {
5407 }
5408 };
5409
5410 template <class>
5411 struct get_info;
5412
5413 template <>
5414 struct get_info<throw_function>;
5415
5416 template <>
5417 struct get_info<throw_file>;
5418
5419 template <>
5420 struct get_info<throw_line>;
5421
5422 template <>
5423 struct get_info<throw_column>;
5424
5425 template <class>
5426 struct set_info_rv;
5427
5428 template <>
5429 struct set_info_rv<throw_function>;
5430
5431 template <>
5432 struct set_info_rv<throw_file>;
5433
5434 template <>
5435 struct set_info_rv<throw_line>;
5436
5437 template <>
5438 struct set_info_rv<throw_column>;
5439
5440 char const * get_diagnostic_information( exception const &, char const * );
5441
5442 void copy_boost_exception( exception *, exception const * );
5443
5444 template <class E,class Tag,class T>
5445 E const & set_info( E const &, error_info<Tag,T> const & );
5446
5447 template <class E>
5448 E const & set_info( E const &, throw_function const & );
5449
5450 template <class E>
5451 E const & set_info( E const &, throw_file const & );
5452
5453 template <class E>
5454 E const & set_info( E const &, throw_line const & );
5455
5456 template <class E>
5457 E const & set_info( E const &, throw_column const & );
5458
5459 boost::source_location get_exception_throw_location( exception const & );
5460 }
5461
5462 class
5464 exception
5465 {
5466 //<N3757>
5467 public:
5468 template <class Tag> void set( typename Tag::type const & );
5469 template <class Tag> typename Tag::type const * get() const;
5470 //</N3757>
5471
5472 protected:
5473
5474 exception():
5475 throw_function_(0),
5476 throw_file_(0),
5477 throw_line_(-1),
5478 throw_column_(-1)
5479 {
5480 }
5481
5482#ifdef __HP_aCC
5483 //On HP aCC, this protected copy constructor prevents throwing boost::exception.
5484 //On all other platforms, the same effect is achieved by the pure virtual destructor.
5485 exception( exception const & x ) noexcept:
5486 data_(x.data_),
5487 throw_function_(x.throw_function_),
5488 throw_file_(x.throw_file_),
5489 throw_line_(x.throw_line_),
5490 throw_column_(x.throw_column_)
5491 {
5492 }
5493#endif
5494
5495 virtual ~exception() noexcept
5496#ifndef __HP_aCC
5497 = 0 //Workaround for HP aCC, =0 incorrectly leads to link errors.
5498#endif
5499 ;
5500
5501#if (defined(__MWERKS__) && __MWERKS__<=0x3207) || (defined(_MSC_VER) && _MSC_VER<=1310)
5502 public:
5503#else
5504 private:
5505
5506 template <class E>
5507 friend E const & exception_detail::set_info( E const &, throw_function const & );
5508
5509 template <class E>
5510 friend E const & exception_detail::set_info( E const &, throw_file const & );
5511
5512 template <class E>
5513 friend E const & exception_detail::set_info( E const &, throw_line const & );
5514
5515 template <class E>
5516 friend E const & exception_detail::set_info( E const &, throw_column const & );
5517
5518 template <class E,class Tag,class T>
5519 friend E const & exception_detail::set_info( E const &, error_info<Tag,T> const & );
5520
5521 friend char const * exception_detail::get_diagnostic_information( exception const &, char const * );
5522
5523 friend boost::source_location exception_detail::get_exception_throw_location( exception const & );
5524
5525 template <class>
5526 friend struct exception_detail::get_info;
5527 friend struct exception_detail::get_info<throw_function>;
5528 friend struct exception_detail::get_info<throw_file>;
5529 friend struct exception_detail::get_info<throw_line>;
5530 friend struct exception_detail::get_info<throw_column>;
5531 template <class>
5532 friend struct exception_detail::set_info_rv;
5533 friend struct exception_detail::set_info_rv<throw_function>;
5534 friend struct exception_detail::set_info_rv<throw_file>;
5535 friend struct exception_detail::set_info_rv<throw_line>;
5536 friend struct exception_detail::set_info_rv<throw_column>;
5537 friend void exception_detail::copy_boost_exception( exception *, exception const * );
5538#endif
5539 mutable exception_detail::refcount_ptr<exception_detail::error_info_container> data_;
5540 mutable char const * throw_function_;
5541 mutable char const * throw_file_;
5542 mutable int throw_line_;
5543 mutable int throw_column_;
5544 };
5545
5546 inline
5547 exception::
5548 ~exception() noexcept
5549 {
5550 }
5551
5552 namespace
5553 exception_detail
5554 {
5555 template <class E>
5556 E const &
5557 set_info( E const & x, throw_function const & y )
5558 {
5559 x.throw_function_=y.v_;
5560 return x;
5561 }
5562
5563 template <class E>
5564 E const &
5565 set_info( E const & x, throw_file const & y )
5566 {
5567 x.throw_file_=y.v_;
5568 return x;
5569 }
5570
5571 template <class E>
5572 E const &
5573 set_info( E const & x, throw_line const & y )
5574 {
5575 x.throw_line_=y.v_;
5576 return x;
5577 }
5578
5579 template <class E>
5580 E const &
5581 set_info( E const & x, throw_column const & y )
5582 {
5583 x.throw_column_=y.v_;
5584 return x;
5585 }
5586
5587 template <>
5588 struct
5589 set_info_rv<throw_column>
5590 {
5591 template <class E>
5592 static
5593 E const &
5594 set( E const & x, throw_column && y )
5595 {
5596 x.throw_column_=y.v_;
5597 return x;
5598 }
5599 };
5600
5601 inline boost::source_location get_exception_throw_location( exception const & x )
5602 {
5603 return boost::source_location(
5604 x.throw_file_? x.throw_file_: "",
5605 x.throw_line_ >= 0? x.throw_line_: 0,
5606 x.throw_function_? x.throw_function_: "",
5607 x.throw_column_ >= 0? x.throw_column_: 0
5608 );
5609 }
5610 }
5611
5612 ////////////////////////////////////////////////////////////////////////
5613
5614 namespace
5615 exception_detail
5616 {
5617 template <class T>
5618 struct
5620 error_info_injector:
5621 public T,
5622 public exception
5623 {
5624 explicit
5625 error_info_injector( T const & x ):
5626 T(x)
5627 {
5628 }
5629
5630 ~error_info_injector() noexcept
5631 {
5632 }
5633 };
5634
5635 struct large_size { char c[256]; };
5636 large_size dispatch_boost_exception( exception const * );
5637
5638 struct small_size { };
5639 small_size dispatch_boost_exception( void const * );
5640
5641 template <class,int>
5642 struct enable_error_info_helper;
5643
5644 template <class T>
5645 struct
5646 enable_error_info_helper<T,sizeof(large_size)>
5647 {
5648 typedef T type;
5649 };
5650
5651 template <class T>
5652 struct
5653 enable_error_info_helper<T,sizeof(small_size)>
5654 {
5655 typedef error_info_injector<T> type;
5656 };
5657
5658 template <class T>
5659 struct
5660 enable_error_info_return_type
5661 {
5662 typedef typename enable_error_info_helper<T,sizeof(exception_detail::dispatch_boost_exception(static_cast<T *>(0)))>::type type;
5663 };
5664 }
5665
5666 template <class T>
5667 inline
5668 typename
5669 exception_detail::enable_error_info_return_type<T>::type
5670 enable_error_info( T const & x )
5671 {
5672 typedef typename exception_detail::enable_error_info_return_type<T>::type rt;
5673 return rt(x);
5674 }
5675
5676 ////////////////////////////////////////////////////////////////////////
5677#ifdef BOOST_NO_EXCEPTIONS
5678 BOOST_NORETURN void throw_exception(std::exception const & e); // user defined
5679#endif
5680
5681 namespace
5682 exception_detail
5683 {
5684 class
5686 clone_base
5687 {
5688 public:
5689
5690 virtual clone_base const * clone() const = 0;
5691 virtual void rethrow() const = 0;
5692
5693 virtual
5694 ~clone_base() noexcept
5695 {
5696 }
5697 };
5698
5699 inline
5700 void
5701 copy_boost_exception( exception * a, exception const * b )
5702 {
5703 refcount_ptr<error_info_container> data;
5704 if( error_info_container * d=b->data_.get() )
5705 data = d->clone();
5706 a->throw_file_ = b->throw_file_;
5707 a->throw_line_ = b->throw_line_;
5708 a->throw_function_ = b->throw_function_;
5709 a->throw_column_ = b->throw_column_;
5710 a->data_ = data;
5711 }
5712
5713 inline
5714 void
5715 copy_boost_exception( void *, void const * )
5716 {
5717 }
5718
5719 template <class T>
5720 class
5722 clone_impl:
5723 public T,
5724 public virtual clone_base
5725 {
5726 struct clone_tag { };
5727 clone_impl( clone_impl const & x, clone_tag ):
5728 T(x)
5729 {
5730 copy_boost_exception(this,&x);
5731 }
5732
5733 public:
5734
5735 explicit
5736 clone_impl( T const & x ):
5737 T(x)
5738 {
5739 copy_boost_exception(this,&x);
5740 }
5741
5742 ~clone_impl() noexcept
5743 {
5744 }
5745
5746 private:
5747
5748 clone_base const *
5749 clone() const
5750 {
5751 return new clone_impl(*this,clone_tag());
5752 }
5753
5754 void
5755 rethrow() const
5756 {
5757#ifdef BOOST_NO_EXCEPTIONS
5758 boost::throw_exception(*this);
5759#else
5760 throw*this;
5761#endif
5762 }
5763 };
5764 }
5765
5766 template <class T>
5767 inline
5768 exception_detail::clone_impl<T>
5769 enable_current_exception( T const & x )
5770 {
5771 return exception_detail::clone_impl<T>(x);
5772 }
5773 }
5774
5775#if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS)
5776#pragma warning(pop)
5777#endif
5778
5779#endif // #ifndef BOOST_EXCEPTION_274DA366004E11DCB1DDFE2E56D89593
5780#ifndef BOOST_THROW_EXCEPTION_HPP_INCLUDED
5781#define BOOST_THROW_EXCEPTION_HPP_INCLUDED
5782
5783// MS compatible compilers support #pragma once
5784
5785#if defined(_MSC_VER) && (_MSC_VER >= 1020)
5786# pragma once
5787#endif
5788
5789// boost/throw_exception.hpp
5790//
5791// Copyright (c) 2002, 2018-2022 Peter Dimov
5792// Copyright (c) 2008-2009 Emil Dotchevski and Reverge Studios, Inc.
5793//
5794// Distributed under the Boost Software License, Version 1.0. (See
5795// accompanying file LICENSE_1_0.txt or copy at
5796// http://www.boost.org/LICENSE_1_0.txt)
5797//
5798// http://www.boost.org/libs/throw_exception
5799
5800namespace boost
5801{
5802
5803#ifdef BOOST_NO_EXCEPTIONS
5804
5805BOOST_NORETURN void throw_exception( std::exception const & e ); // user defined
5806BOOST_NORETURN void throw_exception( std::exception const & e, boost::source_location const & loc ); // user defined
5807
5808#endif
5809
5810// boost::wrapexcept<E>
5811
5812namespace detail
5813{
5814
5815typedef char (&wrapexcept_s1)[ 1 ];
5816typedef char (&wrapexcept_s2)[ 2 ];
5817
5818template<class T> wrapexcept_s1 wrapexcept_is_convertible( T* );
5819template<class T> wrapexcept_s2 wrapexcept_is_convertible( void* );
5820
5821template<class E, class B, std::size_t I = sizeof( wrapexcept_is_convertible<B>( static_cast< E* >( nullptr ) ) ) > struct wrapexcept_add_base;
5822
5823template<class E, class B> struct wrapexcept_add_base<E, B, 1>
5824{
5825 struct type {};
5826};
5827
5828template<class E, class B> struct wrapexcept_add_base<E, B, 2>
5829{
5830 typedef B type;
5831};
5832
5833} // namespace detail
5834
5835template<class E> struct BOOST_SYMBOL_VISIBLE wrapexcept:
5836 public detail::wrapexcept_add_base<E, boost::exception_detail::clone_base>::type,
5837 public E,
5838 public detail::wrapexcept_add_base<E, boost::exception>::type
5839{
5840private:
5841
5842 struct deleter
5843 {
5844 wrapexcept * p_;
5845 ~deleter() { delete p_; }
5846 };
5847
5848private:
5849
5850 void copy_from( void const* )
5851 {
5852 }
5853
5854 void copy_from( boost::exception const* p )
5855 {
5856 static_cast<boost::exception&>( *this ) = *p;
5857 }
5858
5859public:
5860
5861 explicit wrapexcept( E const & e ): E( e )
5862 {
5863 copy_from( &e );
5864 }
5865
5866 explicit wrapexcept( E const & e, boost::source_location const & loc ): E( e )
5867 {
5868 copy_from( &e );
5869
5870 set_info( *this, throw_file( loc.file_name() ) );
5871 set_info( *this, throw_line( loc.line() ) );
5872 set_info( *this, throw_function( loc.function_name() ) );
5873 set_info( *this, throw_column( loc.column() ) );
5874 }
5875
5876 virtual boost::exception_detail::clone_base const * clone() const override
5877 {
5878 wrapexcept * p = new wrapexcept( *this );
5879 deleter del = { p };
5880
5881 boost::exception_detail::copy_boost_exception( p, this );
5882
5883 del.p_ = nullptr;
5884 return p;
5885 }
5886
5887 virtual void rethrow() const override
5888 {
5889#ifdef BOOST_NO_EXCEPTIONS
5890
5891 boost::throw_exception( *this );
5892
5893#else
5894
5895 throw *this;
5896
5897#endif
5898 }
5899};
5900
5901// All boost exceptions are required to derive from std::exception,
5902// to ensure compatibility with BOOST_NO_EXCEPTIONS.
5903
5904inline void throw_exception_assert_compatibility( std::exception const & ) {}
5905
5906// boost::throw_exception
5907
5908#ifndef BOOST_NO_EXCEPTIONS
5909
5910#ifdef BOOST_EXCEPTION_DISABLE
5911
5912template<class E> BOOST_NORETURN void throw_exception( E const & e )
5913{
5914 throw_exception_assert_compatibility( e );
5915 throw e;
5916}
5917
5918template<class E> BOOST_NORETURN void throw_exception( E const & e, boost::source_location const & )
5919{
5920 throw_exception_assert_compatibility( e );
5921 throw e;
5922}
5923
5924#else // defined( BOOST_EXCEPTION_DISABLE )
5925
5926template<class E> BOOST_NORETURN void throw_exception( E const & e )
5927{
5928 throw_exception_assert_compatibility( e );
5929 throw wrapexcept<E>( e );
5930}
5931
5932template<class E> BOOST_NORETURN void throw_exception( E const & e, boost::source_location const & loc )
5933{
5934 throw_exception_assert_compatibility( e );
5935 throw wrapexcept<E>( e, loc );
5936}
5937
5938#endif // defined( BOOST_EXCEPTION_DISABLE )
5939
5940#endif // !defined( BOOST_NO_EXCEPTIONS )
5941
5942} // namespace boost
5943
5944// BOOST_THROW_EXCEPTION
5945
5946#define BOOST_THROW_EXCEPTION(x) ::boost::throw_exception(x, BOOST_CURRENT_LOCATION)
5947
5948namespace boost
5949{
5950
5951// throw_with_location
5952
5953namespace detail
5954{
5955
5956struct BOOST_SYMBOL_VISIBLE throw_location
5957{
5958 boost::source_location location_;
5959
5960 explicit throw_location( boost::source_location const & loc ): location_( loc )
5961 {
5962 }
5963};
5964
5965template<class E> class BOOST_SYMBOL_VISIBLE with_throw_location: public E, public throw_location
5966{
5967public:
5968
5969 with_throw_location( E const & e, boost::source_location const & loc ): E( e ), throw_location( loc )
5970 {
5971 }
5972
5973 with_throw_location( E && e, boost::source_location const & loc ): E( std::move( e ) ), throw_location( loc )
5974 {
5975 }
5976
5977};
5978
5979} // namespace detail
5980
5981#ifndef BOOST_NO_EXCEPTIONS
5982
5983template<class E> BOOST_NORETURN void throw_with_location( E && e, boost::source_location const & loc = BOOST_CURRENT_LOCATION )
5984{
5985 throw_exception_assert_compatibility( e );
5986 throw detail::with_throw_location<typename std::decay<E>::type>( std::forward<E>( e ), loc );
5987}
5988
5989#else
5990
5991template<class E> BOOST_NORETURN void throw_with_location( E const & e, boost::source_location const & loc = BOOST_CURRENT_LOCATION )
5992{
5993 boost::throw_exception( e, loc );
5994}
5995
5996#endif
5997
5998// get_throw_location
5999
6000template<class E> boost::source_location get_throw_location( E const & e )
6001{
6002#ifdef BOOST_NO_RTTI
6003
6004 (void)e;
6005 return boost::source_location();
6006
6007#else
6008
6009 if( detail::throw_location const* pl = dynamic_cast< detail::throw_location const* >( &e ) )
6010 {
6011 return pl->location_;
6012 }
6013 else if( boost::exception const* px = dynamic_cast< boost::exception const* >( &e ) )
6014 {
6015 return exception_detail::get_exception_throw_location( *px );
6016 }
6017 else
6018 {
6019 return boost::source_location();
6020 }
6021
6022#endif
6023}
6024
6025} // namespace boost
6026
6027#endif // #ifndef BOOST_THROW_EXCEPTION_HPP_INCLUDED
6028// Copyright (C) 2022 Christian Mazakas
6029// Distributed under the Boost Software License, Version 1.0. (See accompanying
6030// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6031
6032#ifndef BOOST_UNORDERED_UNORDERED_FLAT_MAP_HPP_INCLUDED
6033#define BOOST_UNORDERED_UNORDERED_FLAT_MAP_HPP_INCLUDED
6034
6035#pragma once
6036
6037namespace boost {
6038 namespace unordered {
6039
6040#ifdef BOOST_MSVC
6041#pragma warning(push)
6042#pragma warning(disable : 4714)
6043#endif
6044
6045 namespace detail {
6046 template <class Key, class T> struct flat_map_types
6047 {
6048 using key_type = Key;
6049 using raw_key_type = typename std::remove_const<Key>::type;
6050 using raw_mapped_type = typename std::remove_const<T>::type;
6051
6052 using init_type = std::pair<raw_key_type, raw_mapped_type>;
6053 using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
6054 using value_type = std::pair<Key const, T>;
6055
6056 using element_type = value_type;
6057
6058 static value_type& value_from(element_type& x) { return x; }
6059
6060 template <class K, class V>
6061 static raw_key_type const& extract(std::pair<K, V> const& kv)
6062 {
6063 return kv.first;
6064 }
6065
6066 static moved_type move(init_type& x)
6067 {
6068 return {std::move(x.first), std::move(x.second)};
6069 }
6070
6071 static moved_type move(element_type& x)
6072 {
6073 // TODO: we probably need to launder here
6074 return {std::move(const_cast<raw_key_type&>(x.first)),
6075 std::move(const_cast<raw_mapped_type&>(x.second))};
6076 }
6077
6078 template <class A, class... Args>
6079 static void construct(A& al, init_type* p, Args&&... args)
6080 {
6081 std::allocator_traits<A>::construct(al, p, std::forward<Args>(args)...);
6082 }
6083
6084 template <class A, class... Args>
6085 static void construct(A& al, value_type* p, Args&&... args)
6086 {
6087 std::allocator_traits<A>::construct(al, p, std::forward<Args>(args)...);
6088 }
6089
6090 template <class A> static void destroy(A& al, init_type* p) noexcept
6091 {
6092 std::allocator_traits<A>::destroy(al, p);
6093 }
6094
6095 template <class A> static void destroy(A& al, value_type* p) noexcept
6096 {
6097 std::allocator_traits<A>::destroy(al, p);
6098 }
6099 };
6100 } // namespace detail
6101
6102 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
6103 class unordered_flat_map
6104 {
6105 using map_types = detail::flat_map_types<Key, T>;
6106
6107 using table_type = detail::foa::table<map_types, Hash, KeyEqual,
6108 typename std::allocator_traits<Allocator>::template rebind_alloc<
6109 typename map_types::value_type>>;
6110
6111 table_type table_;
6112
6113 template <class K, class V, class H, class KE, class A, class Pred>
6114 typename unordered_flat_map<K, V, H, KE, A>::size_type friend erase_if(
6115 unordered_flat_map<K, V, H, KE, A>& set, Pred pred);
6116
6117 public:
6118 using key_type = Key;
6119 using mapped_type = T;
6120 using value_type = typename map_types::value_type;
6121 using init_type = typename map_types::init_type;
6122 using size_type = std::size_t;
6123 using difference_type = std::ptrdiff_t;
6124 using hasher = typename std::type_identity<Hash>::type;
6125 using key_equal = typename std::type_identity<KeyEqual>::type;
6126 using allocator_type = typename std::type_identity<Allocator>::type;
6127 using reference = value_type&;
6128 using const_reference = value_type const&;
6129 using pointer = typename std::allocator_traits<allocator_type>::pointer;
6130 using const_pointer =
6131 typename std::allocator_traits<allocator_type>::const_pointer;
6132 using iterator = typename table_type::iterator;
6133 using const_iterator = typename table_type::const_iterator;
6134
6135 unordered_flat_map() : unordered_flat_map(0) {}
6136
6137 explicit unordered_flat_map(size_type n, hasher const& h = hasher(),
6138 key_equal const& pred = key_equal(),
6139 allocator_type const& a = allocator_type())
6140 : table_(n, h, pred, a)
6141 {
6142 }
6143
6144 unordered_flat_map(size_type n, allocator_type const& a)
6145 : unordered_flat_map(n, hasher(), key_equal(), a)
6146 {
6147 }
6148
6149 unordered_flat_map(size_type n, hasher const& h, allocator_type const& a)
6150 : unordered_flat_map(n, h, key_equal(), a)
6151 {
6152 }
6153
6154 template <class InputIterator>
6155 unordered_flat_map(
6156 InputIterator f, InputIterator l, allocator_type const& a)
6157 : unordered_flat_map(f, l, size_type(0), hasher(), key_equal(), a)
6158 {
6159 }
6160
6161 explicit unordered_flat_map(allocator_type const& a)
6162 : unordered_flat_map(0, a)
6163 {
6164 }
6165
6166 template <class Iterator>
6167 unordered_flat_map(Iterator first, Iterator last, size_type n = 0,
6168 hasher const& h = hasher(), key_equal const& pred = key_equal(),
6169 allocator_type const& a = allocator_type())
6170 : unordered_flat_map(n, h, pred, a)
6171 {
6172 this->insert(first, last);
6173 }
6174
6175 template <class Iterator>
6176 unordered_flat_map(
6177 Iterator first, Iterator last, size_type n, allocator_type const& a)
6178 : unordered_flat_map(first, last, n, hasher(), key_equal(), a)
6179 {
6180 }
6181
6182 template <class Iterator>
6183 unordered_flat_map(Iterator first, Iterator last, size_type n,
6184 hasher const& h, allocator_type const& a)
6185 : unordered_flat_map(first, last, n, h, key_equal(), a)
6186 {
6187 }
6188
6189 unordered_flat_map(unordered_flat_map const& other) : table_(other.table_)
6190 {
6191 }
6192
6193 unordered_flat_map(
6194 unordered_flat_map const& other, allocator_type const& a)
6195 : table_(other.table_, a)
6196 {
6197 }
6198
6199 unordered_flat_map(unordered_flat_map&& other)
6200 noexcept(std::is_nothrow_move_constructible<hasher>::value&&
6201 std::is_nothrow_move_constructible<key_equal>::value&&
6202 std::is_nothrow_move_constructible<allocator_type>::value)
6203 : table_(std::move(other.table_))
6204 {
6205 }
6206
6207 unordered_flat_map(unordered_flat_map&& other, allocator_type const& al)
6208 : table_(std::move(other.table_), al)
6209 {
6210 }
6211
6212 unordered_flat_map(std::initializer_list<value_type> ilist,
6213 size_type n = 0, hasher const& h = hasher(),
6214 key_equal const& pred = key_equal(),
6215 allocator_type const& a = allocator_type())
6216 : unordered_flat_map(ilist.begin(), ilist.end(), n, h, pred, a)
6217 {
6218 }
6219
6220 unordered_flat_map(
6221 std::initializer_list<value_type> il, allocator_type const& a)
6222 : unordered_flat_map(il, size_type(0), hasher(), key_equal(), a)
6223 {
6224 }
6225
6226 unordered_flat_map(std::initializer_list<value_type> init, size_type n,
6227 allocator_type const& a)
6228 : unordered_flat_map(init, n, hasher(), key_equal(), a)
6229 {
6230 }
6231
6232 unordered_flat_map(std::initializer_list<value_type> init, size_type n,
6233 hasher const& h, allocator_type const& a)
6234 : unordered_flat_map(init, n, h, key_equal(), a)
6235 {
6236 }
6237
6238 ~unordered_flat_map() = default;
6239
6240 unordered_flat_map& operator=(unordered_flat_map const& other)
6241 {
6242 table_ = other.table_;
6243 return *this;
6244 }
6245
6246 unordered_flat_map& operator=(unordered_flat_map&& other) noexcept(
6247 noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
6248 {
6249 table_ = std::move(other.table_);
6250 return *this;
6251 }
6252
6253 allocator_type get_allocator() const noexcept
6254 {
6255 return table_.get_allocator();
6256 }
6257
6258 /// Iterators
6259 ///
6260
6261 iterator begin() noexcept { return table_.begin(); }
6262 const_iterator begin() const noexcept { return table_.begin(); }
6263 const_iterator cbegin() const noexcept { return table_.cbegin(); }
6264
6265 iterator end() noexcept { return table_.end(); }
6266 const_iterator end() const noexcept { return table_.end(); }
6267 const_iterator cend() const noexcept { return table_.cend(); }
6268
6269 /// Capacity
6270 ///
6271
6272 [[nodiscard]] bool empty() const noexcept
6273 {
6274 return table_.empty();
6275 }
6276
6277 size_type size() const noexcept { return table_.size(); }
6278
6279 size_type max_size() const noexcept { return table_.max_size(); }
6280
6281 /// Modifiers
6282 ///
6283
6284 void clear() noexcept { table_.clear(); }
6285
6286 template <class Ty>
6287 BOOST_FORCEINLINE auto insert(Ty&& value)
6288 -> decltype(table_.insert(std::forward<Ty>(value)))
6289 {
6290 return table_.insert(std::forward<Ty>(value));
6291 }
6292
6293 BOOST_FORCEINLINE std::pair<iterator, bool> insert(init_type&& value)
6294 {
6295 return table_.insert(std::move(value));
6296 }
6297
6298 template <class Ty>
6299 BOOST_FORCEINLINE auto insert(const_iterator, Ty&& value)
6300 -> decltype(table_.insert(std::forward<Ty>(value)).first)
6301 {
6302 return table_.insert(std::forward<Ty>(value)).first;
6303 }
6304
6305 BOOST_FORCEINLINE iterator insert(const_iterator, init_type&& value)
6306 {
6307 return table_.insert(std::move(value)).first;
6308 }
6309
6310 template <class InputIterator>
6311 BOOST_FORCEINLINE void insert(InputIterator first, InputIterator last)
6312 {
6313 for (auto pos = first; pos != last; ++pos) {
6314 table_.emplace(*pos);
6315 }
6316 }
6317
6318 void insert(std::initializer_list<value_type> ilist)
6319 {
6320 this->insert(ilist.begin(), ilist.end());
6321 }
6322
6323 template <class M>
6324 std::pair<iterator, bool> insert_or_assign(key_type const& key, M&& obj)
6325 {
6326 auto ibp = table_.try_emplace(key, std::forward<M>(obj));
6327 if (ibp.second) {
6328 return ibp;
6329 }
6330 ibp.first->second = std::forward<M>(obj);
6331 return ibp;
6332 }
6333
6334 template <class M>
6335 std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj)
6336 {
6337 auto ibp = table_.try_emplace(std::move(key), std::forward<M>(obj));
6338 if (ibp.second) {
6339 return ibp;
6340 }
6341 ibp.first->second = std::forward<M>(obj);
6342 return ibp;
6343 }
6344
6345 template <class K, class M>
6346 typename std::enable_if<
6347 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
6348 std::pair<iterator, bool> >::type
6349 insert_or_assign(K&& k, M&& obj)
6350 {
6351 auto ibp = table_.try_emplace(std::forward<K>(k), std::forward<M>(obj));
6352 if (ibp.second) {
6353 return ibp;
6354 }
6355 ibp.first->second = std::forward<M>(obj);
6356 return ibp;
6357 }
6358
6359 template <class M>
6360 iterator insert_or_assign(const_iterator, key_type const& key, M&& obj)
6361 {
6362 return this->insert_or_assign(key, std::forward<M>(obj)).first;
6363 }
6364
6365 template <class M>
6366 iterator insert_or_assign(const_iterator, key_type&& key, M&& obj)
6367 {
6368 return this->insert_or_assign(std::move(key), std::forward<M>(obj))
6369 .first;
6370 }
6371
6372 template <class K, class M>
6373 typename std::enable_if<
6374 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
6375 iterator>::type
6376 insert_or_assign(const_iterator, K&& k, M&& obj)
6377 {
6378 return this->insert_or_assign(std::forward<K>(k), std::forward<M>(obj))
6379 .first;
6380 }
6381
6382 template <class... Args>
6383 BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
6384 {
6385 return table_.emplace(std::forward<Args>(args)...);
6386 }
6387
6388 template <class... Args>
6389 BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
6390 {
6391 return table_.emplace(std::forward<Args>(args)...).first;
6392 }
6393
6394 template <class... Args>
6395 BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
6396 key_type const& key, Args&&... args)
6397 {
6398 return table_.try_emplace(key, std::forward<Args>(args)...);
6399 }
6400
6401 template <class... Args>
6402 BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
6403 key_type&& key, Args&&... args)
6404 {
6405 return table_.try_emplace(std::move(key), std::forward<Args>(args)...);
6406 }
6407
6408 template <class K, class... Args>
6409 BOOST_FORCEINLINE typename std::enable_if<
6410 boost::unordered::detail::transparent_non_iterable<K,
6411 unordered_flat_map>::value,
6412 std::pair<iterator, bool> >::type
6413 try_emplace(K&& key, Args&&... args)
6414 {
6415 return table_.try_emplace(
6416 std::forward<K>(key), std::forward<Args>(args)...);
6417 }
6418
6419 template <class... Args>
6420 BOOST_FORCEINLINE iterator try_emplace(
6421 const_iterator, key_type const& key, Args&&... args)
6422 {
6423 return table_.try_emplace(key, std::forward<Args>(args)...).first;
6424 }
6425
6426 template <class... Args>
6427 BOOST_FORCEINLINE iterator try_emplace(
6428 const_iterator, key_type&& key, Args&&... args)
6429 {
6430 return table_.try_emplace(std::move(key), std::forward<Args>(args)...)
6431 .first;
6432 }
6433
6434 template <class K, class... Args>
6435 BOOST_FORCEINLINE typename std::enable_if<
6436 boost::unordered::detail::transparent_non_iterable<K,
6437 unordered_flat_map>::value,
6438 iterator>::type
6439 try_emplace(const_iterator, K&& key, Args&&... args)
6440 {
6441 return table_
6442 .try_emplace(std::forward<K>(key), std::forward<Args>(args)...)
6443 .first;
6444 }
6445
6446 BOOST_FORCEINLINE void erase(iterator pos) { table_.erase(pos); }
6447 BOOST_FORCEINLINE void erase(const_iterator pos)
6448 {
6449 return table_.erase(pos);
6450 }
6451 iterator erase(const_iterator first, const_iterator last)
6452 {
6453 while (first != last) {
6454 this->erase(first++);
6455 }
6456 return iterator{detail::foa::const_iterator_cast_tag{}, last};
6457 }
6458
6459 BOOST_FORCEINLINE size_type erase(key_type const& key)
6460 {
6461 return table_.erase(key);
6462 }
6463
6464 template <class K>
6465 BOOST_FORCEINLINE typename std::enable_if<
6466 detail::transparent_non_iterable<K, unordered_flat_map>::value,
6467 size_type>::type
6468 erase(K const& key)
6469 {
6470 return table_.erase(key);
6471 }
6472
6473 void swap(unordered_flat_map& rhs) noexcept(
6474 noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
6475 {
6476 table_.swap(rhs.table_);
6477 }
6478
6479 template <class H2, class P2>
6480 void merge(
6481 unordered_flat_map<key_type, mapped_type, H2, P2, allocator_type>&
6482 source)
6483 {
6484 table_.merge(source.table_);
6485 }
6486
6487 template <class H2, class P2>
6488 void merge(
6489 unordered_flat_map<key_type, mapped_type, H2, P2, allocator_type>&&
6490 source)
6491 {
6492 table_.merge(std::move(source.table_));
6493 }
6494
6495 /// Lookup
6496 ///
6497
6498 mapped_type& at(key_type const& key)
6499 {
6500 auto pos = table_.find(key);
6501 if (pos != table_.end()) {
6502 return pos->second;
6503 }
6504 // TODO: someday refactor this to conditionally serialize the key and
6505 // include it in the error message
6506 //
6507 boost::throw_exception(
6508 std::out_of_range("key was not found in unordered_flat_map"));
6509 }
6510
6511 mapped_type const& at(key_type const& key) const
6512 {
6513 auto pos = table_.find(key);
6514 if (pos != table_.end()) {
6515 return pos->second;
6516 }
6517 boost::throw_exception(
6518 std::out_of_range("key was not found in unordered_flat_map"));
6519 }
6520
6521 template <class K>
6522 typename std::enable_if<
6523 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
6524 mapped_type&>::type
6525 at(K&& key)
6526 {
6527 auto pos = table_.find(std::forward<K>(key));
6528 if (pos != table_.end()) {
6529 return pos->second;
6530 }
6531 boost::throw_exception(
6532 std::out_of_range("key was not found in unordered_flat_map"));
6533 }
6534
6535 template <class K>
6536 typename std::enable_if<
6537 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
6538 mapped_type const&>::type
6539 at(K&& key) const
6540 {
6541 auto pos = table_.find(std::forward<K>(key));
6542 if (pos != table_.end()) {
6543 return pos->second;
6544 }
6545 boost::throw_exception(
6546 std::out_of_range("key was not found in unordered_flat_map"));
6547 }
6548
6549 BOOST_FORCEINLINE mapped_type& operator[](key_type const& key)
6550 {
6551 return table_.try_emplace(key).first->second;
6552 }
6553
6554 BOOST_FORCEINLINE mapped_type& operator[](key_type&& key)
6555 {
6556 return table_.try_emplace(std::move(key)).first->second;
6557 }
6558
6559 template <class K>
6560 typename std::enable_if<
6561 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
6562 mapped_type&>::type
6563 operator[](K&& key)
6564 {
6565 return table_.try_emplace(std::forward<K>(key)).first->second;
6566 }
6567
6568 BOOST_FORCEINLINE size_type count(key_type const& key) const
6569 {
6570 auto pos = table_.find(key);
6571 return pos != table_.end() ? 1 : 0;
6572 }
6573
6574 template <class K>
6575 BOOST_FORCEINLINE typename std::enable_if<
6576 detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
6577 count(K const& key) const
6578 {
6579 auto pos = table_.find(key);
6580 return pos != table_.end() ? 1 : 0;
6581 }
6582
6583 BOOST_FORCEINLINE iterator find(key_type const& key)
6584 {
6585 return table_.find(key);
6586 }
6587
6588 BOOST_FORCEINLINE const_iterator find(key_type const& key) const
6589 {
6590 return table_.find(key);
6591 }
6592
6593 template <class K>
6594 BOOST_FORCEINLINE typename std::enable_if<
6595 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
6596 iterator>::type
6597 find(K const& key)
6598 {
6599 return table_.find(key);
6600 }
6601
6602 template <class K>
6603 BOOST_FORCEINLINE typename std::enable_if<
6604 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
6605 const_iterator>::type
6606 find(K const& key) const
6607 {
6608 return table_.find(key);
6609 }
6610
6611 BOOST_FORCEINLINE bool contains(key_type const& key) const
6612 {
6613 return this->find(key) != this->end();
6614 }
6615
6616 template <class K>
6617 BOOST_FORCEINLINE typename std::enable_if<
6618 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
6619 bool>::type
6620 contains(K const& key) const
6621 {
6622 return this->find(key) != this->end();
6623 }
6624
6625 std::pair<iterator, iterator> equal_range(key_type const& key)
6626 {
6627 auto pos = table_.find(key);
6628 if (pos == table_.end()) {
6629 return {pos, pos};
6630 }
6631
6632 auto next = pos;
6633 ++next;
6634 return {pos, next};
6635 }
6636
6637 std::pair<const_iterator, const_iterator> equal_range(
6638 key_type const& key) const
6639 {
6640 auto pos = table_.find(key);
6641 if (pos == table_.end()) {
6642 return {pos, pos};
6643 }
6644
6645 auto next = pos;
6646 ++next;
6647 return {pos, next};
6648 }
6649
6650 template <class K>
6651 typename std::enable_if<
6652 detail::are_transparent<K, hasher, key_equal>::value,
6653 std::pair<iterator, iterator> >::type
6654 equal_range(K const& key)
6655 {
6656 auto pos = table_.find(key);
6657 if (pos == table_.end()) {
6658 return {pos, pos};
6659 }
6660
6661 auto next = pos;
6662 ++next;
6663 return {pos, next};
6664 }
6665
6666 template <class K>
6667 typename std::enable_if<
6668 detail::are_transparent<K, hasher, key_equal>::value,
6669 std::pair<const_iterator, const_iterator> >::type
6670 equal_range(K const& key) const
6671 {
6672 auto pos = table_.find(key);
6673 if (pos == table_.end()) {
6674 return {pos, pos};
6675 }
6676
6677 auto next = pos;
6678 ++next;
6679 return {pos, next};
6680 }
6681
6682 /// Hash Policy
6683 ///
6684
6685 size_type bucket_count() const noexcept { return table_.capacity(); }
6686
6687 float load_factor() const noexcept { return table_.load_factor(); }
6688
6689 float max_load_factor() const noexcept
6690 {
6691 return table_.max_load_factor();
6692 }
6693
6694 void max_load_factor(float) {}
6695
6696 size_type max_load() const noexcept { return table_.max_load(); }
6697
6698 void rehash(size_type n) { table_.rehash(n); }
6699
6700 void reserve(size_type n) { table_.reserve(n); }
6701
6702 /// Observers
6703 ///
6704
6705 hasher hash_function() const { return table_.hash_function(); }
6706
6707 key_equal key_eq() const { return table_.key_eq(); }
6708 };
6709
6710 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
6711 bool operator==(
6712 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
6713 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
6714 {
6715 if (&lhs == &rhs) {
6716 return true;
6717 }
6718
6719 return (lhs.size() == rhs.size()) && ([&] {
6720 for (auto const& kvp : lhs) {
6721 auto pos = rhs.find(kvp.first);
6722 if ((pos == rhs.end()) || (*pos != kvp)) {
6723 return false;
6724 }
6725 }
6726 return true;
6727 })();
6728 }
6729
6730 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
6731 bool operator!=(
6732 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
6733 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
6734 {
6735 return !(lhs == rhs);
6736 }
6737
6738 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
6739 void swap(unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
6740 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
6741 noexcept(noexcept(lhs.swap(rhs)))
6742 {
6743 lhs.swap(rhs);
6744 }
6745
6746 template <class Key, class T, class Hash, class KeyEqual, class Allocator,
6747 class Pred>
6748 typename unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>::size_type
6749 erase_if(
6750 unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& map, Pred pred)
6751 {
6752 return erase_if(map.table_, pred);
6753 }
6754
6755#ifdef BOOST_MSVC
6756#pragma warning(pop)
6757#endif
6758
6760
6761 namespace detail {
6762 template <typename T>
6763 using iter_key_t =
6764 typename std::iterator_traits<T>::value_type::first_type;
6765 template <typename T>
6766 using iter_val_t =
6767 typename std::iterator_traits<T>::value_type::second_type;
6768 template <typename T>
6769 using iter_to_alloc_t =
6770 typename std::pair<iter_key_t<T> const, iter_val_t<T> >;
6771 } // namespace detail
6772
6773 template <class InputIterator,
6774 class Hash =
6775 boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
6776 class Pred =
6777 std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
6778 class Allocator = std::allocator<
6779 boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
6780 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
6781 class = std::enable_if_t<detail::is_hash_v<Hash> >,
6782 class = std::enable_if_t<detail::is_pred_v<Pred> >,
6783 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
6784 unordered_flat_map(InputIterator, InputIterator,
6785 std::size_t = boost::unordered::detail::foa::default_bucket_count,
6786 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
6787 -> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
6788 boost::unordered::detail::iter_val_t<InputIterator>, Hash, Pred,
6789 Allocator>;
6790
6791 template <class Key, class T,
6792 class Hash = boost::hash<std::remove_const_t<Key> >,
6793 class Pred = std::equal_to<std::remove_const_t<Key> >,
6794 class Allocator = std::allocator<std::pair<const Key, T> >,
6795 class = std::enable_if_t<detail::is_hash_v<Hash> >,
6796 class = std::enable_if_t<detail::is_pred_v<Pred> >,
6797 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
6798 unordered_flat_map(std::initializer_list<std::pair<Key, T> >,
6799 std::size_t = boost::unordered::detail::foa::default_bucket_count,
6800 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
6801 -> unordered_flat_map<std::remove_const_t<Key>, T, Hash, Pred,
6802 Allocator>;
6803
6804 template <class InputIterator, class Allocator,
6805 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
6806 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
6807 unordered_flat_map(InputIterator, InputIterator, std::size_t, Allocator)
6808 -> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
6809 boost::unordered::detail::iter_val_t<InputIterator>,
6810 boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
6811 std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
6812 Allocator>;
6813
6814 template <class InputIterator, class Allocator,
6815 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
6816 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
6817 unordered_flat_map(InputIterator, InputIterator, Allocator)
6818 -> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
6819 boost::unordered::detail::iter_val_t<InputIterator>,
6820 boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
6821 std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
6822 Allocator>;
6823
6824 template <class InputIterator, class Hash, class Allocator,
6825 class = std::enable_if_t<detail::is_hash_v<Hash> >,
6826 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
6827 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
6828 unordered_flat_map(
6829 InputIterator, InputIterator, std::size_t, Hash, Allocator)
6830 -> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
6831 boost::unordered::detail::iter_val_t<InputIterator>, Hash,
6832 std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
6833 Allocator>;
6834
6835 template <class Key, class T, class Allocator,
6836 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
6837 unordered_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
6838 Allocator) -> unordered_flat_map<std::remove_const_t<Key>, T,
6839 boost::hash<std::remove_const_t<Key> >,
6840 std::equal_to<std::remove_const_t<Key> >, Allocator>;
6841
6842 template <class Key, class T, class Allocator,
6843 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
6844 unordered_flat_map(std::initializer_list<std::pair<Key, T> >, Allocator)
6845 -> unordered_flat_map<std::remove_const_t<Key>, T,
6846 boost::hash<std::remove_const_t<Key> >,
6847 std::equal_to<std::remove_const_t<Key> >, Allocator>;
6848
6849 template <class Key, class T, class Hash, class Allocator,
6850 class = std::enable_if_t<detail::is_hash_v<Hash> >,
6851 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
6852 unordered_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
6853 Hash, Allocator) -> unordered_flat_map<std::remove_const_t<Key>, T,
6854 Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
6855#endif
6856
6857 } // namespace unordered
6858} // namespace boost
6859
6860#endif
6861/* Copyright 2023 Christian Mazakas.
6862 * Distributed under the Boost Software License, Version 1.0.
6863 * (See accompanying file LICENSE_1_0.txt or copy at
6864 * http://www.boost.org/LICENSE_1_0.txt)
6865 *
6866 * See https://www.boost.org/libs/unordered for library home page.
6867 */
6868
6869#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
6870#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
6871
6872namespace boost{
6873namespace unordered{
6874namespace detail{
6875namespace foa{
6876
6877template<class T>
6878struct element_type
6879{
6880 using value_type=T;
6881 value_type* p;
6882
6883 /*
6884 * we use a deleted copy constructor here so the type is no longer
6885 * trivially copy-constructible which inhibits our memcpy
6886 * optimizations when copying the tables
6887 */
6888 element_type() = default;
6889 element_type(value_type* p_):p(p_){}
6890 element_type(element_type const&) = delete;
6891 element_type(element_type&& rhs) noexcept
6892 {
6893 p = rhs.p;
6894 rhs.p = nullptr;
6895 }
6896
6897 element_type& operator=(element_type const&)=delete;
6898 element_type& operator=(element_type&& rhs)noexcept
6899 {
6900 if (this!=&rhs){
6901 p=rhs.p;
6902 rhs.p=nullptr;
6903 }
6904 return *this;
6905 }
6906
6907 void swap(element_type& rhs)noexcept
6908 {
6909 auto tmp=p;
6910 p=rhs.p;
6911 rhs.p=tmp;
6912 }
6913};
6914
6915}
6916}
6917}
6918}
6919
6920#endif // BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
6921/* Copyright 2023 Christian Mazakas.
6922 * Distributed under the Boost Software License, Version 1.0.
6923 * (See accompanying file LICENSE_1_0.txt or copy at
6924 * http://www.boost.org/LICENSE_1_0.txt)
6925 *
6926 * See https://www.boost.org/libs/unordered for library home page.
6927 */
6928
6929#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
6930#define BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
6931
6932namespace boost{
6933namespace unordered{
6934namespace detail{
6935namespace foa{
6936
6937template <class Iterator,class NodeType>
6938struct insert_return_type
6939{
6940 Iterator position;
6941 bool inserted;
6942 NodeType node;
6943};
6944
6945template <class T>
6946union opt_storage {
6947 [[no_unique_address]] T t_;
6948
6949 opt_storage(){}
6950 ~opt_storage(){}
6951};
6952
6953template <class TypePolicy,class Allocator>
6954struct node_handle_base
6955{
6956 protected:
6957 using type_policy=TypePolicy;
6958 using element_type=typename type_policy::element_type;
6959
6960 public:
6961 using allocator_type = Allocator;
6962
6963 private:
6964 using node_value_type=typename type_policy::value_type;
6965 element_type p_;
6966 [[no_unique_address]] opt_storage<Allocator> a_;
6967
6968 protected:
6969 node_value_type& data()noexcept
6970 {
6971 return *(p_.p);
6972 }
6973
6974 node_value_type const& data()const noexcept
6975 {
6976 return *(p_.p);
6977 }
6978
6979 element_type& element()noexcept
6980 {
6981 BOOST_ASSERT(!empty());
6982 return p_;
6983 }
6984
6985 element_type const& element()const noexcept
6986 {
6987 BOOST_ASSERT(!empty());
6988 return p_;
6989 }
6990
6991 Allocator& al()noexcept
6992 {
6993 BOOST_ASSERT(!empty());
6994 return a_.t_;
6995 }
6996
6997 Allocator const& al()const noexcept
6998 {
6999 BOOST_ASSERT(!empty());
7000 return a_.t_;
7001 }
7002
7003 void emplace(element_type&& x,Allocator a)
7004 {
7005 BOOST_ASSERT(empty());
7006 auto* p=x.p;
7007 p_.p=p;
7008 new(&a_.t_)Allocator(a);
7009 x.p=nullptr;
7010 }
7011
7012 void reset()
7013 {
7014 a_.t_.~Allocator();
7015 p_.p=nullptr;
7016 }
7017
7018 public:
7019 constexpr node_handle_base()noexcept:p_{nullptr}{}
7020
7021 node_handle_base(node_handle_base&& nh) noexcept
7022 {
7023 p_.p = nullptr;
7024 if (!nh.empty()){
7025 emplace(std::move(nh.p_),nh.al());
7026 nh.reset();
7027 }
7028 }
7029
7030 node_handle_base& operator=(node_handle_base&& nh)noexcept
7031 {
7032 if(this!=&nh){
7033 if(empty()){
7034 if(nh.empty()){
7035 /* nothing to do */
7036 }else{
7037 emplace(std::move(nh.p_),std::move(nh.al()));
7038 nh.reset();
7039 }
7040 }else{
7041 if(nh.empty()){
7042 type_policy::destroy(al(),&p_);
7043 reset();
7044 }else{
7045 bool const pocma=
7046 std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value;
7047
7048 BOOST_ASSERT(pocma||al()==nh.al());
7049
7050 type_policy::destroy(al(),&p_);
7051 if(pocma){
7052 al()=std::move(nh.al());
7053 }
7054
7055 p_=std::move(nh.p_);
7056 nh.reset();
7057 }
7058 }
7059 }else{
7060 if(empty()){
7061 /* nothing to do */
7062 }else{
7063 type_policy::destroy(al(),&p_);
7064 reset();
7065 }
7066 }
7067 return *this;
7068 }
7069
7070 ~node_handle_base()
7071 {
7072 if(!empty()){
7073 type_policy::destroy(al(),&p_);
7074 reset();
7075 }
7076 }
7077
7078 allocator_type get_allocator()const noexcept{return al();}
7079 explicit operator bool()const noexcept{ return !empty();}
7080 [[nodiscard]] bool empty()const noexcept{return p_.p==nullptr;}
7081
7082 void swap(node_handle_base& nh) noexcept(
7083 std::allocator_traits<Allocator>::is_always_equal::value||
7084 std::allocator_traits<Allocator>::propagate_on_container_swap::value)
7085 {
7086 if(this!=&nh){
7087 if(empty()){
7088 if(nh.empty()) {
7089 /* nothing to do here */
7090 } else {
7091 emplace(std::move(nh.p_), nh.al());
7092 nh.reset();
7093 }
7094 }else{
7095 if(nh.empty()){
7096 nh.emplace(std::move(p_),al());
7097 reset();
7098 }else{
7099 bool const pocs=
7100 std::allocator_traits<Allocator>::propagate_on_container_swap::value;
7101
7102 BOOST_ASSERT(pocs || al()==nh.al());
7103
7104 using std::swap;
7105 p_.swap(nh.p_);
7106 if(pocs)swap(al(),nh.al());
7107 }
7108 }
7109 }
7110 }
7111
7112 friend
7113 void swap(node_handle_base& lhs,node_handle_base& rhs)
7114 noexcept(noexcept(lhs.swap(rhs)))
7115 {
7116 return lhs.swap(rhs);
7117 }
7118};
7119
7120}
7121}
7122}
7123}
7124
7125#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
7126// Copyright (C) 2023 Christian Mazakas
7127// Distributed under the Boost Software License, Version 1.0. (See accompanying
7128// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7129
7130#ifndef BOOST_UNORDERED_NODE_SET_FWD_HPP_INCLUDED
7131#define BOOST_UNORDERED_NODE_SET_FWD_HPP_INCLUDED
7132
7133#pragma once
7134
7135namespace boost {
7136 namespace unordered {
7137 template <class Key, class Hash = boost::hash<Key>,
7138 class KeyEqual = std::equal_to<Key>,
7139 class Allocator = std::allocator<Key> >
7140 class unordered_node_set;
7141
7142 template <class Key, class Hash, class KeyEqual, class Allocator>
7143 bool operator==(
7144 unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
7145 unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
7146
7147 template <class Key, class Hash, class KeyEqual, class Allocator>
7148 bool operator!=(
7149 unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
7150 unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
7151
7152 template <class Key, class Hash, class KeyEqual, class Allocator>
7153 void swap(unordered_node_set<Key, Hash, KeyEqual, Allocator>& lhs,
7154 unordered_node_set<Key, Hash, KeyEqual, Allocator>& rhs)
7155 noexcept(noexcept(lhs.swap(rhs)));
7156 } // namespace unordered
7157
7158 using boost::unordered::unordered_node_set;
7159
7160 using boost::unordered::swap;
7161 using boost::unordered::operator==;
7162 using boost::unordered::operator!=;
7163} // namespace boost
7164
7165#endif
7166// Copyright (C) 2022 Christian Mazakas
7167// Distributed under the Boost Software License, Version 1.0. (See accompanying
7168// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7169
7170#ifndef BOOST_UNORDERED_UNORDERED_NODE_SET_HPP_INCLUDED
7171#define BOOST_UNORDERED_UNORDERED_NODE_SET_HPP_INCLUDED
7172
7173#pragma once
7174
7175namespace boost {
7176 namespace unordered {
7177
7178#ifdef BOOST_MSVC
7179#pragma warning(push)
7180#pragma warning(disable : 4714)
7181#endif
7182
7183 namespace detail {
7184 template <class Key> struct node_set_types
7185 {
7186 using key_type = Key;
7187 using init_type = Key;
7188 using value_type = Key;
7189
7190 static Key const& extract(value_type const& key) { return key; }
7191
7192 using element_type=foa::element_type<value_type>;
7193
7194 static value_type& value_from(element_type const& x) { return *x.p; }
7195 static Key const& extract(element_type const& k) { return *k.p; }
7196 static element_type&& move(element_type& x) { return std::move(x); }
7197 static value_type&& move(value_type& x) { return std::move(x); }
7198
7199 template <class A>
7200 static void construct(A& al, element_type* p, element_type const& copy)
7201 {
7202 construct(al, p, *copy.p);
7203 }
7204
7205 template <typename Allocator>
7206 static void construct(
7207 Allocator&, element_type* p, element_type&& x) noexcept
7208 {
7209 p->p = x.p;
7210 x.p = nullptr;
7211 }
7212
7213 template <class A, class... Args>
7214 static void construct(A& al, value_type* p, Args&&... args)
7215 {
7216 std::allocator_traits<A>::construct(al, p, std::forward<Args>(args)...);
7217 }
7218
7219 template <class A, class... Args>
7220 static void construct(A& al, element_type* p, Args&&... args)
7221 {
7222 p->p = std::to_address(std::allocator_traits<A>::allocate(al, 1));
7223 BOOST_TRY
7224 {
7225 std::allocator_traits<A>::construct(al, p->p, std::forward<Args>(args)...);
7226 }
7227 BOOST_CATCH(...)
7228 {
7229 std::allocator_traits<A>::deallocate(al,
7230 std::pointer_traits<
7231 typename std::allocator_traits<A>::pointer>::pointer_to(*p->p),
7232 1);
7234 }
7236 }
7237
7238 template <class A> static void destroy(A& al, value_type* p) noexcept
7239 {
7240 std::allocator_traits<A>::destroy(al, p);
7241 }
7242
7243 template <class A> static void destroy(A& al, element_type* p) noexcept
7244 {
7245 if (p->p) {
7246 destroy(al, p->p);
7247 std::allocator_traits<A>::deallocate(al,
7248 std::pointer_traits<typename std::allocator_traits<A>::pointer>::pointer_to(*(p->p)),
7249 1);
7250 }
7251 }
7252 };
7253
7254 template <class TypePolicy, class Allocator>
7255 struct node_set_handle
7256 : public detail::foa::node_handle_base<TypePolicy, Allocator>
7257 {
7258 private:
7259 using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
7260
7261 using typename base_type::type_policy;
7262
7263 template <class Key, class Hash, class Pred, class Alloc>
7264 friend class boost::unordered::unordered_node_set;
7265
7266 public:
7267 using value_type = typename TypePolicy::value_type;
7268
7269 constexpr node_set_handle() noexcept = default;
7270 node_set_handle(node_set_handle&& nh) noexcept = default;
7271 node_set_handle& operator=(node_set_handle&&) noexcept = default;
7272
7273 value_type& value() const
7274 {
7275 BOOST_ASSERT(!this->empty());
7276 return const_cast<value_type&>(this->data());
7277 }
7278 };
7279 } // namespace detail
7280
7281 template <class Key, class Hash, class KeyEqual, class Allocator>
7282 class unordered_node_set
7283 {
7284 using set_types = detail::node_set_types<Key>;
7285
7286 using table_type = detail::foa::table<set_types, Hash, KeyEqual,
7287 typename std::allocator_traits<Allocator>::template rebind_alloc<
7288 typename set_types::value_type>>;
7289
7290 table_type table_;
7291
7292 template <class K, class H, class KE, class A, class Pred>
7293 typename unordered_node_set<K, H, KE, A>::size_type friend erase_if(
7294 unordered_node_set<K, H, KE, A>& set, Pred pred);
7295
7296 public:
7297 using key_type = Key;
7298 using value_type = typename set_types::value_type;
7299 using init_type = typename set_types::init_type;
7300 using size_type = std::size_t;
7301 using difference_type = std::ptrdiff_t;
7302 using hasher = Hash;
7303 using key_equal = KeyEqual;
7304 using allocator_type = Allocator;
7305 using reference = value_type&;
7306 using const_reference = value_type const&;
7307 using pointer = typename std::allocator_traits<allocator_type>::pointer;
7308 using const_pointer =
7309 typename std::allocator_traits<allocator_type>::const_pointer;
7310 using iterator = typename table_type::iterator;
7311 using const_iterator = typename table_type::const_iterator;
7312 using node_type = detail::node_set_handle<set_types,
7313 typename std::allocator_traits<Allocator>::template rebind_alloc<
7314 typename set_types::value_type>>;
7315 using insert_return_type =
7316 detail::foa::insert_return_type<iterator, node_type>;
7317
7318 unordered_node_set() : unordered_node_set(0) {}
7319
7320 explicit unordered_node_set(size_type n, hasher const& h = hasher(),
7321 key_equal const& pred = key_equal(),
7322 allocator_type const& a = allocator_type())
7323 : table_(n, h, pred, a)
7324 {
7325 }
7326
7327 unordered_node_set(size_type n, allocator_type const& a)
7328 : unordered_node_set(n, hasher(), key_equal(), a)
7329 {
7330 }
7331
7332 unordered_node_set(size_type n, hasher const& h, allocator_type const& a)
7333 : unordered_node_set(n, h, key_equal(), a)
7334 {
7335 }
7336
7337 template <class InputIterator>
7338 unordered_node_set(
7339 InputIterator f, InputIterator l, allocator_type const& a)
7340 : unordered_node_set(f, l, size_type(0), hasher(), key_equal(), a)
7341 {
7342 }
7343
7344 explicit unordered_node_set(allocator_type const& a)
7345 : unordered_node_set(0, a)
7346 {
7347 }
7348
7349 template <class Iterator>
7350 unordered_node_set(Iterator first, Iterator last, size_type n = 0,
7351 hasher const& h = hasher(), key_equal const& pred = key_equal(),
7352 allocator_type const& a = allocator_type())
7353 : unordered_node_set(n, h, pred, a)
7354 {
7355 this->insert(first, last);
7356 }
7357
7358 template <class InputIt>
7359 unordered_node_set(
7360 InputIt first, InputIt last, size_type n, allocator_type const& a)
7361 : unordered_node_set(first, last, n, hasher(), key_equal(), a)
7362 {
7363 }
7364
7365 template <class Iterator>
7366 unordered_node_set(Iterator first, Iterator last, size_type n,
7367 hasher const& h, allocator_type const& a)
7368 : unordered_node_set(first, last, n, h, key_equal(), a)
7369 {
7370 }
7371
7372 unordered_node_set(unordered_node_set const& other) : table_(other.table_)
7373 {
7374 }
7375
7376 unordered_node_set(
7377 unordered_node_set const& other, allocator_type const& a)
7378 : table_(other.table_, a)
7379 {
7380 }
7381
7382 unordered_node_set(unordered_node_set&& other)
7383 noexcept(std::is_nothrow_move_constructible<hasher>::value&&
7384 std::is_nothrow_move_constructible<key_equal>::value&&
7385 std::is_nothrow_move_constructible<allocator_type>::value)
7386 : table_(std::move(other.table_))
7387 {
7388 }
7389
7390 unordered_node_set(unordered_node_set&& other, allocator_type const& al)
7391 : table_(std::move(other.table_), al)
7392 {
7393 }
7394
7395 unordered_node_set(std::initializer_list<value_type> ilist,
7396 size_type n = 0, hasher const& h = hasher(),
7397 key_equal const& pred = key_equal(),
7398 allocator_type const& a = allocator_type())
7399 : unordered_node_set(ilist.begin(), ilist.end(), n, h, pred, a)
7400 {
7401 }
7402
7403 unordered_node_set(
7404 std::initializer_list<value_type> il, allocator_type const& a)
7405 : unordered_node_set(il, size_type(0), hasher(), key_equal(), a)
7406 {
7407 }
7408
7409 unordered_node_set(std::initializer_list<value_type> init, size_type n,
7410 allocator_type const& a)
7411 : unordered_node_set(init, n, hasher(), key_equal(), a)
7412 {
7413 }
7414
7415 unordered_node_set(std::initializer_list<value_type> init, size_type n,
7416 hasher const& h, allocator_type const& a)
7417 : unordered_node_set(init, n, h, key_equal(), a)
7418 {
7419 }
7420
7421 ~unordered_node_set() = default;
7422
7423 unordered_node_set& operator=(unordered_node_set const& other)
7424 {
7425 table_ = other.table_;
7426 return *this;
7427 }
7428
7429 unordered_node_set& operator=(unordered_node_set&& other) noexcept(
7430 noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
7431 {
7432 table_ = std::move(other.table_);
7433 return *this;
7434 }
7435
7436 allocator_type get_allocator() const noexcept
7437 {
7438 return table_.get_allocator();
7439 }
7440
7441 /// Iterators
7442 ///
7443
7444 iterator begin() noexcept { return table_.begin(); }
7445 const_iterator begin() const noexcept { return table_.begin(); }
7446 const_iterator cbegin() const noexcept { return table_.cbegin(); }
7447
7448 iterator end() noexcept { return table_.end(); }
7449 const_iterator end() const noexcept { return table_.end(); }
7450 const_iterator cend() const noexcept { return table_.cend(); }
7451
7452 /// Capacity
7453 ///
7454
7455 [[nodiscard]] bool empty() const noexcept
7456 {
7457 return table_.empty();
7458 }
7459
7460 size_type size() const noexcept { return table_.size(); }
7461
7462 size_type max_size() const noexcept { return table_.max_size(); }
7463
7464 /// Modifiers
7465 ///
7466
7467 void clear() noexcept { table_.clear(); }
7468
7469 BOOST_FORCEINLINE std::pair<iterator, bool> insert(
7470 value_type const& value)
7471 {
7472 return table_.insert(value);
7473 }
7474
7475 BOOST_FORCEINLINE std::pair<iterator, bool> insert(value_type&& value)
7476 {
7477 return table_.insert(std::move(value));
7478 }
7479
7480 template <class K>
7481 BOOST_FORCEINLINE typename std::enable_if<
7482 detail::transparent_non_iterable<K, unordered_node_set>::value,
7483 std::pair<iterator, bool> >::type
7484 insert(K&& k)
7485 {
7486 return table_.try_emplace(std::forward<K>(k));
7487 }
7488
7489 BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value)
7490 {
7491 return table_.insert(value).first;
7492 }
7493
7494 BOOST_FORCEINLINE iterator insert(const_iterator, value_type&& value)
7495 {
7496 return table_.insert(std::move(value)).first;
7497 }
7498
7499 template <class K>
7500 BOOST_FORCEINLINE typename std::enable_if<
7501 detail::transparent_non_iterable<K, unordered_node_set>::value,
7502 iterator>::type
7503 insert(const_iterator, K&& k)
7504 {
7505 return table_.try_emplace(std::forward<K>(k)).first;
7506 }
7507
7508 template <class InputIterator>
7509 void insert(InputIterator first, InputIterator last)
7510 {
7511 for (auto pos = first; pos != last; ++pos) {
7512 table_.emplace(*pos);
7513 }
7514 }
7515
7516 void insert(std::initializer_list<value_type> ilist)
7517 {
7518 this->insert(ilist.begin(), ilist.end());
7519 }
7520
7521 insert_return_type insert(node_type&& nh)
7522 {
7523 if (nh.empty()) {
7524 return {end(), false, node_type{}};
7525 }
7526
7527 BOOST_ASSERT(get_allocator() == nh.get_allocator());
7528
7529 auto itp = table_.insert(std::move(nh.element()));
7530 if (itp.second) {
7531 nh.reset();
7532 return {itp.first, true, node_type{}};
7533 } else {
7534 return {itp.first, false, std::move(nh)};
7535 }
7536 }
7537
7538 iterator insert(const_iterator, node_type&& nh)
7539 {
7540 if (nh.empty()) {
7541 return end();
7542 }
7543
7544 BOOST_ASSERT(get_allocator() == nh.get_allocator());
7545
7546 auto itp = table_.insert(std::move(nh.element()));
7547 if (itp.second) {
7548 nh.reset();
7549 return itp.first;
7550 } else {
7551 return itp.first;
7552 }
7553 }
7554
7555 template <class... Args>
7556 BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
7557 {
7558 return table_.emplace(std::forward<Args>(args)...);
7559 }
7560
7561 template <class... Args>
7562 BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
7563 {
7564 return table_.emplace(std::forward<Args>(args)...).first;
7565 }
7566
7567 BOOST_FORCEINLINE void erase(const_iterator pos)
7568 {
7569 return table_.erase(pos);
7570 }
7571 iterator erase(const_iterator first, const_iterator last)
7572 {
7573 while (first != last) {
7574 this->erase(first++);
7575 }
7576 return iterator{detail::foa::const_iterator_cast_tag{}, last};
7577 }
7578
7579 BOOST_FORCEINLINE size_type erase(key_type const& key)
7580 {
7581 return table_.erase(key);
7582 }
7583
7584 template <class K>
7585 BOOST_FORCEINLINE typename std::enable_if<
7586 detail::transparent_non_iterable<K, unordered_node_set>::value,
7587 size_type>::type
7588 erase(K const& key)
7589 {
7590 return table_.erase(key);
7591 }
7592
7593 void swap(unordered_node_set& rhs) noexcept(
7594 noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
7595 {
7596 table_.swap(rhs.table_);
7597 }
7598
7599 node_type extract(const_iterator pos)
7600 {
7601 BOOST_ASSERT(pos != end());
7602 node_type nh;
7603 auto elem = table_.extract(pos);
7604 nh.emplace(std::move(elem), get_allocator());
7605 return nh;
7606 }
7607
7608 node_type extract(key_type const& key)
7609 {
7610 auto pos = find(key);
7611 return pos != end() ? extract(pos) : node_type();
7612 }
7613
7614 template <class K>
7615 typename std::enable_if<
7616 boost::unordered::detail::transparent_non_iterable<K,
7617 unordered_node_set>::value,
7618 node_type>::type
7619 extract(K const& key)
7620 {
7621 auto pos = find(key);
7622 return pos != end() ? extract(pos) : node_type();
7623 }
7624
7625 template <class H2, class P2>
7626 void merge(unordered_node_set<key_type, H2, P2, allocator_type>& source)
7627 {
7628 table_.merge(source.table_);
7629 }
7630
7631 template <class H2, class P2>
7632 void merge(unordered_node_set<key_type, H2, P2, allocator_type>&& source)
7633 {
7634 table_.merge(std::move(source.table_));
7635 }
7636
7637 /// Lookup
7638 ///
7639
7640 BOOST_FORCEINLINE size_type count(key_type const& key) const
7641 {
7642 auto pos = table_.find(key);
7643 return pos != table_.end() ? 1 : 0;
7644 }
7645
7646 template <class K>
7647 BOOST_FORCEINLINE typename std::enable_if<
7648 detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
7649 count(K const& key) const
7650 {
7651 auto pos = table_.find(key);
7652 return pos != table_.end() ? 1 : 0;
7653 }
7654
7655 BOOST_FORCEINLINE iterator find(key_type const& key)
7656 {
7657 return table_.find(key);
7658 }
7659
7660 BOOST_FORCEINLINE const_iterator find(key_type const& key) const
7661 {
7662 return table_.find(key);
7663 }
7664
7665 template <class K>
7666 BOOST_FORCEINLINE typename std::enable_if<
7667 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
7668 iterator>::type
7669 find(K const& key)
7670 {
7671 return table_.find(key);
7672 }
7673
7674 template <class K>
7675 BOOST_FORCEINLINE typename std::enable_if<
7676 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
7677 const_iterator>::type
7678 find(K const& key) const
7679 {
7680 return table_.find(key);
7681 }
7682
7683 BOOST_FORCEINLINE bool contains(key_type const& key) const
7684 {
7685 return this->find(key) != this->end();
7686 }
7687
7688 template <class K>
7689 BOOST_FORCEINLINE typename std::enable_if<
7690 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
7691 bool>::type
7692 contains(K const& key) const
7693 {
7694 return this->find(key) != this->end();
7695 }
7696
7697 std::pair<iterator, iterator> equal_range(key_type const& key)
7698 {
7699 auto pos = table_.find(key);
7700 if (pos == table_.end()) {
7701 return {pos, pos};
7702 }
7703
7704 auto next = pos;
7705 ++next;
7706 return {pos, next};
7707 }
7708
7709 std::pair<const_iterator, const_iterator> equal_range(
7710 key_type const& key) const
7711 {
7712 auto pos = table_.find(key);
7713 if (pos == table_.end()) {
7714 return {pos, pos};
7715 }
7716
7717 auto next = pos;
7718 ++next;
7719 return {pos, next};
7720 }
7721
7722 template <class K>
7723 typename std::enable_if<
7724 detail::are_transparent<K, hasher, key_equal>::value,
7725 std::pair<iterator, iterator> >::type
7726 equal_range(K const& key)
7727 {
7728 auto pos = table_.find(key);
7729 if (pos == table_.end()) {
7730 return {pos, pos};
7731 }
7732
7733 auto next = pos;
7734 ++next;
7735 return {pos, next};
7736 }
7737
7738 template <class K>
7739 typename std::enable_if<
7740 detail::are_transparent<K, hasher, key_equal>::value,
7741 std::pair<const_iterator, const_iterator> >::type
7742 equal_range(K const& key) const
7743 {
7744 auto pos = table_.find(key);
7745 if (pos == table_.end()) {
7746 return {pos, pos};
7747 }
7748
7749 auto next = pos;
7750 ++next;
7751 return {pos, next};
7752 }
7753
7754 /// Hash Policy
7755 ///
7756
7757 size_type bucket_count() const noexcept { return table_.capacity(); }
7758
7759 float load_factor() const noexcept { return table_.load_factor(); }
7760
7761 float max_load_factor() const noexcept
7762 {
7763 return table_.max_load_factor();
7764 }
7765
7766 void max_load_factor(float) {}
7767
7768 size_type max_load() const noexcept { return table_.max_load(); }
7769
7770 void rehash(size_type n) { table_.rehash(n); }
7771
7772 void reserve(size_type n) { table_.reserve(n); }
7773
7774 /// Observers
7775 ///
7776
7777 hasher hash_function() const { return table_.hash_function(); }
7778
7779 key_equal key_eq() const { return table_.key_eq(); }
7780 };
7781
7782 template <class Key, class Hash, class KeyEqual, class Allocator>
7783 bool operator==(
7784 unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
7785 unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
7786 {
7787 if (&lhs == &rhs) {
7788 return true;
7789 }
7790
7791 return (lhs.size() == rhs.size()) && ([&] {
7792 for (auto const& key : lhs) {
7793 auto pos = rhs.find(key);
7794 if ((pos == rhs.end()) || (key != *pos)) {
7795 return false;
7796 }
7797 }
7798 return true;
7799 })();
7800 }
7801
7802 template <class Key, class Hash, class KeyEqual, class Allocator>
7803 bool operator!=(
7804 unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
7805 unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
7806 {
7807 return !(lhs == rhs);
7808 }
7809
7810 template <class Key, class Hash, class KeyEqual, class Allocator>
7811 void swap(unordered_node_set<Key, Hash, KeyEqual, Allocator>& lhs,
7812 unordered_node_set<Key, Hash, KeyEqual, Allocator>& rhs)
7813 noexcept(noexcept(lhs.swap(rhs)))
7814 {
7815 lhs.swap(rhs);
7816 }
7817
7818 template <class Key, class Hash, class KeyEqual, class Allocator,
7819 class Pred>
7820 typename unordered_node_set<Key, Hash, KeyEqual, Allocator>::size_type
7821 erase_if(unordered_node_set<Key, Hash, KeyEqual, Allocator>& set, Pred pred)
7822 {
7823 return erase_if(set.table_, pred);
7824 }
7825
7826#ifdef BOOST_MSVC
7827#pragma warning(pop)
7828#endif
7829
7831 template <class InputIterator,
7832 class Hash =
7833 boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
7834 class Pred =
7835 std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
7836 class Allocator = std::allocator<
7837 typename std::iterator_traits<InputIterator>::value_type>,
7838 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
7839 class = std::enable_if_t<detail::is_hash_v<Hash> >,
7840 class = std::enable_if_t<detail::is_pred_v<Pred> >,
7841 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
7842 unordered_node_set(InputIterator, InputIterator,
7843 std::size_t = boost::unordered::detail::foa::default_bucket_count,
7844 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
7845 -> unordered_node_set<
7846 typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
7847 Allocator>;
7848
7849 template <class T, class Hash = boost::hash<T>,
7850 class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
7851 class = std::enable_if_t<detail::is_hash_v<Hash> >,
7852 class = std::enable_if_t<detail::is_pred_v<Pred> >,
7853 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
7854 unordered_node_set(std::initializer_list<T>,
7855 std::size_t = boost::unordered::detail::foa::default_bucket_count,
7856 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
7857 -> unordered_node_set<T, Hash, Pred, Allocator>;
7858
7859 template <class InputIterator, class Allocator,
7860 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
7861 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
7862 unordered_node_set(InputIterator, InputIterator, std::size_t, Allocator)
7863 -> unordered_node_set<
7864 typename std::iterator_traits<InputIterator>::value_type,
7865 boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
7866 std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
7867 Allocator>;
7868
7869 template <class InputIterator, class Hash, class Allocator,
7870 class = std::enable_if_t<detail::is_hash_v<Hash> >,
7871 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
7872 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
7873 unordered_node_set(
7874 InputIterator, InputIterator, std::size_t, Hash, Allocator)
7875 -> unordered_node_set<
7876 typename std::iterator_traits<InputIterator>::value_type, Hash,
7877 std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
7878 Allocator>;
7879
7880 template <class T, class Allocator,
7881 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
7882 unordered_node_set(std::initializer_list<T>, std::size_t, Allocator)
7883 -> unordered_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
7884
7885 template <class T, class Hash, class Allocator,
7886 class = std::enable_if_t<detail::is_hash_v<Hash> >,
7887 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
7888 unordered_node_set(std::initializer_list<T>, std::size_t, Hash, Allocator)
7889 -> unordered_node_set<T, Hash, std::equal_to<T>, Allocator>;
7890
7891 template <class InputIterator, class Allocator,
7892 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
7893 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
7894 unordered_node_set(InputIterator, InputIterator, Allocator)
7895 -> unordered_node_set<
7896 typename std::iterator_traits<InputIterator>::value_type,
7897 boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
7898 std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
7899 Allocator>;
7900
7901 template <class T, class Allocator,
7902 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
7903 unordered_node_set(std::initializer_list<T>, Allocator)
7904 -> unordered_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
7905#endif
7906
7907 } // namespace unordered
7908} // namespace boost
7909
7910#endif
7911// Copyright (C) 2022 Christian Mazakas
7912// Distributed under the Boost Software License, Version 1.0. (See accompanying
7913// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7914
7915#ifndef BOOST_UNORDERED_NODE_MAP_FWD_HPP_INCLUDED
7916#define BOOST_UNORDERED_NODE_MAP_FWD_HPP_INCLUDED
7917
7918#pragma once
7919
7920namespace boost {
7921 namespace unordered {
7922 template <class Key, class T, class Hash = boost::hash<Key>,
7923 class KeyEqual = std::equal_to<Key>,
7924 class Allocator = std::allocator<std::pair<const Key, T> > >
7925 class unordered_node_map;
7926
7927 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
7928 bool operator==(
7929 unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
7930 unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
7931
7932 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
7933 bool operator!=(
7934 unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
7935 unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
7936
7937 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
7938 void swap(unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
7939 unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
7940 noexcept(noexcept(lhs.swap(rhs)));
7941 } // namespace unordered
7942
7943 using boost::unordered::unordered_node_map;
7944
7945 using boost::unordered::swap;
7946 using boost::unordered::operator==;
7947 using boost::unordered::operator!=;
7948} // namespace boost
7949
7950#endif
7951// Copyright (C) 2022-2023 Christian Mazakas
7952// Distributed under the Boost Software License, Version 1.0. (See accompanying
7953// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7954
7955#ifndef BOOST_UNORDERED_UNORDERED_NODE_MAP_HPP_INCLUDED
7956#define BOOST_UNORDERED_UNORDERED_NODE_MAP_HPP_INCLUDED
7957
7958#pragma once
7959
7960namespace boost {
7961 namespace unordered {
7962
7963#ifdef BOOST_MSVC
7964#pragma warning(push)
7965#pragma warning(disable : 4714)
7966#endif
7967
7968 namespace detail {
7969 template <class Key, class T> struct node_map_types
7970 {
7971 using key_type = Key;
7972 using mapped_type = T;
7973 using raw_key_type = typename std::remove_const<Key>::type;
7974 using raw_mapped_type = typename std::remove_const<T>::type;
7975
7976 using init_type = std::pair<raw_key_type, raw_mapped_type>;
7977 using value_type = std::pair<Key const, T>;
7978 using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
7979
7980 using element_type=foa::element_type<value_type>;
7981
7982 static value_type& value_from(element_type const& x) { return *(x.p); }
7983
7984 template <class K, class V>
7985 static raw_key_type const& extract(std::pair<K, V> const& kv)
7986 {
7987 return kv.first;
7988 }
7989
7990 static raw_key_type const& extract(element_type const& kv)
7991 {
7992 return kv.p->first;
7993 }
7994
7995 static element_type&& move(element_type& x) { return std::move(x); }
7996 static moved_type move(init_type& x)
7997 {
7998 return {std::move(x.first), std::move(x.second)};
7999 }
8000
8001 static moved_type move(value_type& x)
8002 {
8003 return {std::move(const_cast<raw_key_type&>(x.first)),
8004 std::move(const_cast<raw_mapped_type&>(x.second))};
8005 }
8006
8007 template <class A>
8008 static void construct(A&, element_type* p, element_type&& x) noexcept
8009 {
8010 p->p = x.p;
8011 x.p = nullptr;
8012 }
8013
8014 template <class A>
8015 static void construct(A& al, element_type* p, element_type const& copy)
8016 {
8017 construct(al, p, *copy.p);
8018 }
8019
8020 template <class A, class... Args>
8021 static void construct(A& al, init_type* p, Args&&... args)
8022 {
8023 std::allocator_traits<A>::construct(al, p, std::forward<Args>(args)...);
8024 }
8025
8026 template <class A, class... Args>
8027 static void construct(A& al, value_type* p, Args&&... args)
8028 {
8029 std::allocator_traits<A>::construct(al, p, std::forward<Args>(args)...);
8030 }
8031
8032 template <class A, class... Args>
8033 static void construct(A& al, element_type* p, Args&&... args)
8034 {
8035 p->p = std::to_address(std::allocator_traits<A>::allocate(al, 1));
8036 BOOST_TRY
8037 {
8038 std::allocator_traits<A>::construct(al, p->p, std::forward<Args>(args)...);
8039 }
8040 BOOST_CATCH(...)
8041 {
8042 using pointer_type = typename std::allocator_traits<A>::pointer;
8043 using pointer_traits = std::pointer_traits<pointer_type>;
8044
8045 std::allocator_traits<A>::deallocate(
8046 al, pointer_traits::pointer_to(*(p->p)), 1);
8048 }
8050 }
8051
8052 template <class A> static void destroy(A& al, value_type* p) noexcept
8053 {
8054 std::allocator_traits<A>::destroy(al, p);
8055 }
8056
8057 template <class A> static void destroy(A& al, init_type* p) noexcept
8058 {
8059 std::allocator_traits<A>::destroy(al, p);
8060 }
8061
8062 template <class A> static void destroy(A& al, element_type* p) noexcept
8063 {
8064 if (p->p) {
8065 using pointer_type = typename std::allocator_traits<A>::pointer;
8066 using pointer_traits = std::pointer_traits<pointer_type>;
8067
8068 destroy(al, p->p);
8069 std::allocator_traits<A>::deallocate(
8070 al, pointer_traits::pointer_to(*(p->p)), 1);
8071 }
8072 }
8073 };
8074
8075 template <class TypePolicy, class Allocator>
8076 struct node_map_handle
8077 : public detail::foa::node_handle_base<TypePolicy, Allocator>
8078 {
8079 private:
8080 using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
8081
8082 using typename base_type::type_policy;
8083
8084 template <class Key, class T, class Hash, class Pred, class Alloc>
8085 friend class boost::unordered::unordered_node_map;
8086
8087 public:
8088 using key_type = typename TypePolicy::key_type;
8089 using mapped_type = typename TypePolicy::mapped_type;
8090
8091 constexpr node_map_handle() noexcept = default;
8092 node_map_handle(node_map_handle&& nh) noexcept = default;
8093
8094 node_map_handle& operator=(node_map_handle&&) noexcept = default;
8095
8096 key_type& key() const
8097 {
8098 BOOST_ASSERT(!this->empty());
8099 return const_cast<key_type&>(this->data().first);
8100 }
8101
8102 mapped_type& mapped() const
8103 {
8104 BOOST_ASSERT(!this->empty());
8105 return const_cast<mapped_type&>(this->data().second);
8106 }
8107 };
8108 } // namespace detail
8109
8110 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
8111 class unordered_node_map
8112 {
8113 using map_types = detail::node_map_types<Key, T>;
8114
8115 using table_type = detail::foa::table<map_types, Hash, KeyEqual,
8116 typename std::allocator_traits<Allocator>::template rebind_alloc<
8117 std::pair<Key const, T> >>;
8118
8119 table_type table_;
8120
8121 template <class K, class V, class H, class KE, class A, class Pred>
8122 typename unordered_node_map<K, V, H, KE, A>::size_type friend erase_if(
8123 unordered_node_map<K, V, H, KE, A>& set, Pred pred);
8124
8125 public:
8126 using key_type = Key;
8127 using mapped_type = T;
8128 using value_type = typename map_types::value_type;
8129 using init_type = typename map_types::init_type;
8130 using size_type = std::size_t;
8131 using difference_type = std::ptrdiff_t;
8132 using hasher = typename std::type_identity<Hash>::type;
8133 using key_equal = typename std::type_identity<KeyEqual>::type;
8134 using allocator_type = typename std::type_identity<Allocator>::type;
8135 using reference = value_type&;
8136 using const_reference = value_type const&;
8137 using pointer = typename std::allocator_traits<allocator_type>::pointer;
8138 using const_pointer =
8139 typename std::allocator_traits<allocator_type>::const_pointer;
8140 using iterator = typename table_type::iterator;
8141 using const_iterator = typename table_type::const_iterator;
8142 using node_type = detail::node_map_handle<map_types,
8143 typename std::allocator_traits<Allocator>::template rebind_alloc<
8144 typename map_types::value_type>>;
8145 using insert_return_type =
8146 detail::foa::insert_return_type<iterator, node_type>;
8147
8148 unordered_node_map() : unordered_node_map(0) {}
8149
8150 explicit unordered_node_map(size_type n, hasher const& h = hasher(),
8151 key_equal const& pred = key_equal(),
8152 allocator_type const& a = allocator_type())
8153 : table_(n, h, pred, a)
8154 {
8155 }
8156
8157 unordered_node_map(size_type n, allocator_type const& a)
8158 : unordered_node_map(n, hasher(), key_equal(), a)
8159 {
8160 }
8161
8162 unordered_node_map(size_type n, hasher const& h, allocator_type const& a)
8163 : unordered_node_map(n, h, key_equal(), a)
8164 {
8165 }
8166
8167 template <class InputIterator>
8168 unordered_node_map(
8169 InputIterator f, InputIterator l, allocator_type const& a)
8170 : unordered_node_map(f, l, size_type(0), hasher(), key_equal(), a)
8171 {
8172 }
8173
8174 explicit unordered_node_map(allocator_type const& a)
8175 : unordered_node_map(0, a)
8176 {
8177 }
8178
8179 template <class Iterator>
8180 unordered_node_map(Iterator first, Iterator last, size_type n = 0,
8181 hasher const& h = hasher(), key_equal const& pred = key_equal(),
8182 allocator_type const& a = allocator_type())
8183 : unordered_node_map(n, h, pred, a)
8184 {
8185 this->insert(first, last);
8186 }
8187
8188 template <class Iterator>
8189 unordered_node_map(
8190 Iterator first, Iterator last, size_type n, allocator_type const& a)
8191 : unordered_node_map(first, last, n, hasher(), key_equal(), a)
8192 {
8193 }
8194
8195 template <class Iterator>
8196 unordered_node_map(Iterator first, Iterator last, size_type n,
8197 hasher const& h, allocator_type const& a)
8198 : unordered_node_map(first, last, n, h, key_equal(), a)
8199 {
8200 }
8201
8202 unordered_node_map(unordered_node_map const& other) : table_(other.table_)
8203 {
8204 }
8205
8206 unordered_node_map(
8207 unordered_node_map const& other, allocator_type const& a)
8208 : table_(other.table_, a)
8209 {
8210 }
8211
8212 unordered_node_map(unordered_node_map&& other)
8213 noexcept(std::is_nothrow_move_constructible<hasher>::value&&
8214 std::is_nothrow_move_constructible<key_equal>::value&&
8215 std::is_nothrow_move_constructible<allocator_type>::value)
8216 : table_(std::move(other.table_))
8217 {
8218 }
8219
8220 unordered_node_map(unordered_node_map&& other, allocator_type const& al)
8221 : table_(std::move(other.table_), al)
8222 {
8223 }
8224
8225 unordered_node_map(std::initializer_list<value_type> ilist,
8226 size_type n = 0, hasher const& h = hasher(),
8227 key_equal const& pred = key_equal(),
8228 allocator_type const& a = allocator_type())
8229 : unordered_node_map(ilist.begin(), ilist.end(), n, h, pred, a)
8230 {
8231 }
8232
8233 unordered_node_map(
8234 std::initializer_list<value_type> il, allocator_type const& a)
8235 : unordered_node_map(il, size_type(0), hasher(), key_equal(), a)
8236 {
8237 }
8238
8239 unordered_node_map(std::initializer_list<value_type> init, size_type n,
8240 allocator_type const& a)
8241 : unordered_node_map(init, n, hasher(), key_equal(), a)
8242 {
8243 }
8244
8245 unordered_node_map(std::initializer_list<value_type> init, size_type n,
8246 hasher const& h, allocator_type const& a)
8247 : unordered_node_map(init, n, h, key_equal(), a)
8248 {
8249 }
8250
8251 ~unordered_node_map() = default;
8252
8253 unordered_node_map& operator=(unordered_node_map const& other)
8254 {
8255 table_ = other.table_;
8256 return *this;
8257 }
8258
8259 unordered_node_map& operator=(unordered_node_map&& other) noexcept(
8260 noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
8261 {
8262 table_ = std::move(other.table_);
8263 return *this;
8264 }
8265
8266 allocator_type get_allocator() const noexcept
8267 {
8268 return table_.get_allocator();
8269 }
8270
8271 /// Iterators
8272 ///
8273
8274 iterator begin() noexcept { return table_.begin(); }
8275 const_iterator begin() const noexcept { return table_.begin(); }
8276 const_iterator cbegin() const noexcept { return table_.cbegin(); }
8277
8278 iterator end() noexcept { return table_.end(); }
8279 const_iterator end() const noexcept { return table_.end(); }
8280 const_iterator cend() const noexcept { return table_.cend(); }
8281
8282 /// Capacity
8283 ///
8284
8285 [[nodiscard]] bool empty() const noexcept
8286 {
8287 return table_.empty();
8288 }
8289
8290 size_type size() const noexcept { return table_.size(); }
8291
8292 size_type max_size() const noexcept { return table_.max_size(); }
8293
8294 /// Modifiers
8295 ///
8296
8297 void clear() noexcept { table_.clear(); }
8298
8299 template <class Ty>
8300 BOOST_FORCEINLINE auto insert(Ty&& value)
8301 -> decltype(table_.insert(std::forward<Ty>(value)))
8302 {
8303 return table_.insert(std::forward<Ty>(value));
8304 }
8305
8306 BOOST_FORCEINLINE std::pair<iterator, bool> insert(init_type&& value)
8307 {
8308 return table_.insert(std::move(value));
8309 }
8310
8311 template <class Ty>
8312 BOOST_FORCEINLINE auto insert(const_iterator, Ty&& value)
8313 -> decltype(table_.insert(std::forward<Ty>(value)).first)
8314 {
8315 return table_.insert(std::forward<Ty>(value)).first;
8316 }
8317
8318 BOOST_FORCEINLINE iterator insert(const_iterator, init_type&& value)
8319 {
8320 return table_.insert(std::move(value)).first;
8321 }
8322
8323 template <class InputIterator>
8324 BOOST_FORCEINLINE void insert(InputIterator first, InputIterator last)
8325 {
8326 for (auto pos = first; pos != last; ++pos) {
8327 table_.emplace(*pos);
8328 }
8329 }
8330
8331 void insert(std::initializer_list<value_type> ilist)
8332 {
8333 this->insert(ilist.begin(), ilist.end());
8334 }
8335
8336 insert_return_type insert(node_type&& nh)
8337 {
8338 if (nh.empty()) {
8339 return {end(), false, node_type{}};
8340 }
8341
8342 BOOST_ASSERT(get_allocator() == nh.get_allocator());
8343
8344 auto itp = table_.insert(std::move(nh.element()));
8345 if (itp.second) {
8346 nh.reset();
8347 return {itp.first, true, node_type{}};
8348 } else {
8349 return {itp.first, false, std::move(nh)};
8350 }
8351 }
8352
8353 iterator insert(const_iterator, node_type&& nh)
8354 {
8355 if (nh.empty()) {
8356 return end();
8357 }
8358
8359 BOOST_ASSERT(get_allocator() == nh.get_allocator());
8360
8361 auto itp = table_.insert(std::move(nh.element()));
8362 if (itp.second) {
8363 nh.reset();
8364 return itp.first;
8365 } else {
8366 return itp.first;
8367 }
8368 }
8369
8370 template <class M>
8371 std::pair<iterator, bool> insert_or_assign(key_type const& key, M&& obj)
8372 {
8373 auto ibp = table_.try_emplace(key, std::forward<M>(obj));
8374 if (ibp.second) {
8375 return ibp;
8376 }
8377 ibp.first->second = std::forward<M>(obj);
8378 return ibp;
8379 }
8380
8381 template <class M>
8382 std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj)
8383 {
8384 auto ibp = table_.try_emplace(std::move(key), std::forward<M>(obj));
8385 if (ibp.second) {
8386 return ibp;
8387 }
8388 ibp.first->second = std::forward<M>(obj);
8389 return ibp;
8390 }
8391
8392 template <class K, class M>
8393 typename std::enable_if<
8394 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
8395 std::pair<iterator, bool> >::type
8396 insert_or_assign(K&& k, M&& obj)
8397 {
8398 auto ibp = table_.try_emplace(std::forward<K>(k), std::forward<M>(obj));
8399 if (ibp.second) {
8400 return ibp;
8401 }
8402 ibp.first->second = std::forward<M>(obj);
8403 return ibp;
8404 }
8405
8406 template <class M>
8407 iterator insert_or_assign(const_iterator, key_type const& key, M&& obj)
8408 {
8409 return this->insert_or_assign(key, std::forward<M>(obj)).first;
8410 }
8411
8412 template <class M>
8413 iterator insert_or_assign(const_iterator, key_type&& key, M&& obj)
8414 {
8415 return this->insert_or_assign(std::move(key), std::forward<M>(obj))
8416 .first;
8417 }
8418
8419 template <class K, class M>
8420 typename std::enable_if<
8421 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
8422 iterator>::type
8423 insert_or_assign(const_iterator, K&& k, M&& obj)
8424 {
8425 return this->insert_or_assign(std::forward<K>(k), std::forward<M>(obj))
8426 .first;
8427 }
8428
8429 template <class... Args>
8430 BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
8431 {
8432 return table_.emplace(std::forward<Args>(args)...);
8433 }
8434
8435 template <class... Args>
8436 BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
8437 {
8438 return table_.emplace(std::forward<Args>(args)...).first;
8439 }
8440
8441 template <class... Args>
8442 BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
8443 key_type const& key, Args&&... args)
8444 {
8445 return table_.try_emplace(key, std::forward<Args>(args)...);
8446 }
8447
8448 template <class... Args>
8449 BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
8450 key_type&& key, Args&&... args)
8451 {
8452 return table_.try_emplace(std::move(key), std::forward<Args>(args)...);
8453 }
8454
8455 template <class K, class... Args>
8456 BOOST_FORCEINLINE typename std::enable_if<
8457 boost::unordered::detail::transparent_non_iterable<K,
8458 unordered_node_map>::value,
8459 std::pair<iterator, bool> >::type
8460 try_emplace(K&& key, Args&&... args)
8461 {
8462 return table_.try_emplace(
8463 std::forward<K>(key), std::forward<Args>(args)...);
8464 }
8465
8466 template <class... Args>
8467 BOOST_FORCEINLINE iterator try_emplace(
8468 const_iterator, key_type const& key, Args&&... args)
8469 {
8470 return table_.try_emplace(key, std::forward<Args>(args)...).first;
8471 }
8472
8473 template <class... Args>
8474 BOOST_FORCEINLINE iterator try_emplace(
8475 const_iterator, key_type&& key, Args&&... args)
8476 {
8477 return table_.try_emplace(std::move(key), std::forward<Args>(args)...)
8478 .first;
8479 }
8480
8481 template <class K, class... Args>
8482 BOOST_FORCEINLINE typename std::enable_if<
8483 boost::unordered::detail::transparent_non_iterable<K,
8484 unordered_node_map>::value,
8485 iterator>::type
8486 try_emplace(const_iterator, K&& key, Args&&... args)
8487 {
8488 return table_
8489 .try_emplace(std::forward<K>(key), std::forward<Args>(args)...)
8490 .first;
8491 }
8492
8493 BOOST_FORCEINLINE void erase(iterator pos) { table_.erase(pos); }
8494 BOOST_FORCEINLINE void erase(const_iterator pos)
8495 {
8496 return table_.erase(pos);
8497 }
8498 iterator erase(const_iterator first, const_iterator last)
8499 {
8500 while (first != last) {
8501 this->erase(first++);
8502 }
8503 return iterator{detail::foa::const_iterator_cast_tag{}, last};
8504 }
8505
8506 BOOST_FORCEINLINE size_type erase(key_type const& key)
8507 {
8508 return table_.erase(key);
8509 }
8510
8511 template <class K>
8512 BOOST_FORCEINLINE typename std::enable_if<
8513 detail::transparent_non_iterable<K, unordered_node_map>::value,
8514 size_type>::type
8515 erase(K const& key)
8516 {
8517 return table_.erase(key);
8518 }
8519
8520 void swap(unordered_node_map& rhs) noexcept(
8521 noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
8522 {
8523 table_.swap(rhs.table_);
8524 }
8525
8526 node_type extract(const_iterator pos)
8527 {
8528 BOOST_ASSERT(pos != end());
8529 node_type nh;
8530 auto elem = table_.extract(pos);
8531 nh.emplace(std::move(elem), get_allocator());
8532 return nh;
8533 }
8534
8535 node_type extract(key_type const& key)
8536 {
8537 auto pos = find(key);
8538 return pos != end() ? extract(pos) : node_type();
8539 }
8540
8541 template <class K>
8542 typename std::enable_if<
8543 boost::unordered::detail::transparent_non_iterable<K,
8544 unordered_node_map>::value,
8545 node_type>::type
8546 extract(K const& key)
8547 {
8548 auto pos = find(key);
8549 return pos != end() ? extract(pos) : node_type();
8550 }
8551
8552 template <class H2, class P2>
8553 void merge(
8554 unordered_node_map<key_type, mapped_type, H2, P2, allocator_type>&
8555 source)
8556 {
8557 table_.merge(source.table_);
8558 }
8559
8560 template <class H2, class P2>
8561 void merge(
8562 unordered_node_map<key_type, mapped_type, H2, P2, allocator_type>&&
8563 source)
8564 {
8565 table_.merge(std::move(source.table_));
8566 }
8567
8568 /// Lookup
8569 ///
8570
8571 mapped_type& at(key_type const& key)
8572 {
8573 auto pos = table_.find(key);
8574 if (pos != table_.end()) {
8575 return pos->second;
8576 }
8577 // TODO: someday refactor this to conditionally serialize the key and
8578 // include it in the error message
8579 //
8580 boost::throw_exception(
8581 std::out_of_range("key was not found in unordered_node_map"));
8582 }
8583
8584 mapped_type const& at(key_type const& key) const
8585 {
8586 auto pos = table_.find(key);
8587 if (pos != table_.end()) {
8588 return pos->second;
8589 }
8590 boost::throw_exception(
8591 std::out_of_range("key was not found in unordered_node_map"));
8592 }
8593
8594 template <class K>
8595 typename std::enable_if<
8596 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
8597 mapped_type&>::type
8598 at(K&& key)
8599 {
8600 auto pos = table_.find(std::forward<K>(key));
8601 if (pos != table_.end()) {
8602 return pos->second;
8603 }
8604 boost::throw_exception(
8605 std::out_of_range("key was not found in unordered_node_map"));
8606 }
8607
8608 template <class K>
8609 typename std::enable_if<
8610 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
8611 mapped_type const&>::type
8612 at(K&& key) const
8613 {
8614 auto pos = table_.find(std::forward<K>(key));
8615 if (pos != table_.end()) {
8616 return pos->second;
8617 }
8618 boost::throw_exception(
8619 std::out_of_range("key was not found in unordered_node_map"));
8620 }
8621
8622 BOOST_FORCEINLINE mapped_type& operator[](key_type const& key)
8623 {
8624 return table_.try_emplace(key).first->second;
8625 }
8626
8627 BOOST_FORCEINLINE mapped_type& operator[](key_type&& key)
8628 {
8629 return table_.try_emplace(std::move(key)).first->second;
8630 }
8631
8632 template <class K>
8633 typename std::enable_if<
8634 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
8635 mapped_type&>::type
8636 operator[](K&& key)
8637 {
8638 return table_.try_emplace(std::forward<K>(key)).first->second;
8639 }
8640
8641 BOOST_FORCEINLINE size_type count(key_type const& key) const
8642 {
8643 auto pos = table_.find(key);
8644 return pos != table_.end() ? 1 : 0;
8645 }
8646
8647 template <class K>
8648 BOOST_FORCEINLINE typename std::enable_if<
8649 detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
8650 count(K const& key) const
8651 {
8652 auto pos = table_.find(key);
8653 return pos != table_.end() ? 1 : 0;
8654 }
8655
8656 BOOST_FORCEINLINE iterator find(key_type const& key)
8657 {
8658 return table_.find(key);
8659 }
8660
8661 BOOST_FORCEINLINE const_iterator find(key_type const& key) const
8662 {
8663 return table_.find(key);
8664 }
8665
8666 template <class K>
8667 BOOST_FORCEINLINE typename std::enable_if<
8668 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
8669 iterator>::type
8670 find(K const& key)
8671 {
8672 return table_.find(key);
8673 }
8674
8675 template <class K>
8676 BOOST_FORCEINLINE typename std::enable_if<
8677 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
8678 const_iterator>::type
8679 find(K const& key) const
8680 {
8681 return table_.find(key);
8682 }
8683
8684 BOOST_FORCEINLINE bool contains(key_type const& key) const
8685 {
8686 return this->find(key) != this->end();
8687 }
8688
8689 template <class K>
8690 BOOST_FORCEINLINE typename std::enable_if<
8691 boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
8692 bool>::type
8693 contains(K const& key) const
8694 {
8695 return this->find(key) != this->end();
8696 }
8697
8698 std::pair<iterator, iterator> equal_range(key_type const& key)
8699 {
8700 auto pos = table_.find(key);
8701 if (pos == table_.end()) {
8702 return {pos, pos};
8703 }
8704
8705 auto next = pos;
8706 ++next;
8707 return {pos, next};
8708 }
8709
8710 std::pair<const_iterator, const_iterator> equal_range(
8711 key_type const& key) const
8712 {
8713 auto pos = table_.find(key);
8714 if (pos == table_.end()) {
8715 return {pos, pos};
8716 }
8717
8718 auto next = pos;
8719 ++next;
8720 return {pos, next};
8721 }
8722
8723 template <class K>
8724 typename std::enable_if<
8725 detail::are_transparent<K, hasher, key_equal>::value,
8726 std::pair<iterator, iterator> >::type
8727 equal_range(K const& key)
8728 {
8729 auto pos = table_.find(key);
8730 if (pos == table_.end()) {
8731 return {pos, pos};
8732 }
8733
8734 auto next = pos;
8735 ++next;
8736 return {pos, next};
8737 }
8738
8739 template <class K>
8740 typename std::enable_if<
8741 detail::are_transparent<K, hasher, key_equal>::value,
8742 std::pair<const_iterator, const_iterator> >::type
8743 equal_range(K const& key) const
8744 {
8745 auto pos = table_.find(key);
8746 if (pos == table_.end()) {
8747 return {pos, pos};
8748 }
8749
8750 auto next = pos;
8751 ++next;
8752 return {pos, next};
8753 }
8754
8755 /// Hash Policy
8756 ///
8757
8758 size_type bucket_count() const noexcept { return table_.capacity(); }
8759
8760 float load_factor() const noexcept { return table_.load_factor(); }
8761
8762 float max_load_factor() const noexcept
8763 {
8764 return table_.max_load_factor();
8765 }
8766
8767 void max_load_factor(float) {}
8768
8769 size_type max_load() const noexcept { return table_.max_load(); }
8770
8771 void rehash(size_type n) { table_.rehash(n); }
8772
8773 void reserve(size_type n) { table_.reserve(n); }
8774
8775 /// Observers
8776 ///
8777
8778 hasher hash_function() const { return table_.hash_function(); }
8779
8780 key_equal key_eq() const { return table_.key_eq(); }
8781 };
8782
8783 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
8784 bool operator==(
8785 unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
8786 unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
8787 {
8788 if (&lhs == &rhs) {
8789 return true;
8790 }
8791
8792 return (lhs.size() == rhs.size()) && ([&] {
8793 for (auto const& kvp : lhs) {
8794 auto pos = rhs.find(kvp.first);
8795 if ((pos == rhs.end()) || (*pos != kvp)) {
8796 return false;
8797 }
8798 }
8799 return true;
8800 })();
8801 }
8802
8803 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
8804 bool operator!=(
8805 unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
8806 unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
8807 {
8808 return !(lhs == rhs);
8809 }
8810
8811 template <class Key, class T, class Hash, class KeyEqual, class Allocator>
8812 void swap(unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
8813 unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
8814 noexcept(noexcept(lhs.swap(rhs)))
8815 {
8816 lhs.swap(rhs);
8817 }
8818
8819 template <class Key, class T, class Hash, class KeyEqual, class Allocator,
8820 class Pred>
8821 typename unordered_node_map<Key, T, Hash, KeyEqual, Allocator>::size_type
8822 erase_if(
8823 unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& map, Pred pred)
8824 {
8825 return erase_if(map.table_, pred);
8826 }
8827
8828#ifdef BOOST_MSVC
8829#pragma warning(pop)
8830#endif
8831
8833
8834 namespace detail {
8835 template <typename T>
8836 using iter_key_t =
8837 typename std::iterator_traits<T>::value_type::first_type;
8838 template <typename T>
8839 using iter_val_t =
8840 typename std::iterator_traits<T>::value_type::second_type;
8841 template <typename T>
8842 using iter_to_alloc_t =
8843 typename std::pair<iter_key_t<T> const, iter_val_t<T> >;
8844 } // namespace detail
8845
8846 template <class InputIterator,
8847 class Hash =
8848 boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
8849 class Pred =
8850 std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
8851 class Allocator = std::allocator<
8852 boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
8853 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
8854 class = std::enable_if_t<detail::is_hash_v<Hash> >,
8855 class = std::enable_if_t<detail::is_pred_v<Pred> >,
8856 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
8857 unordered_node_map(InputIterator, InputIterator,
8858 std::size_t = boost::unordered::detail::foa::default_bucket_count,
8859 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
8860 -> unordered_node_map<boost::unordered::detail::iter_key_t<InputIterator>,
8861 boost::unordered::detail::iter_val_t<InputIterator>, Hash, Pred,
8862 Allocator>;
8863
8864 template <class Key, class T,
8865 class Hash = boost::hash<std::remove_const_t<Key> >,
8866 class Pred = std::equal_to<std::remove_const_t<Key> >,
8867 class Allocator = std::allocator<std::pair<const Key, T> >,
8868 class = std::enable_if_t<detail::is_hash_v<Hash> >,
8869 class = std::enable_if_t<detail::is_pred_v<Pred> >,
8870 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
8871 unordered_node_map(std::initializer_list<std::pair<Key, T> >,
8872 std::size_t = boost::unordered::detail::foa::default_bucket_count,
8873 Hash = Hash(), Pred = Pred(), Allocator = Allocator())
8874 -> unordered_node_map<std::remove_const_t<Key>, T, Hash, Pred,
8875 Allocator>;
8876
8877 template <class InputIterator, class Allocator,
8878 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
8879 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
8880 unordered_node_map(InputIterator, InputIterator, std::size_t, Allocator)
8881 -> unordered_node_map<boost::unordered::detail::iter_key_t<InputIterator>,
8882 boost::unordered::detail::iter_val_t<InputIterator>,
8883 boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
8884 std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
8885 Allocator>;
8886
8887 template <class InputIterator, class Allocator,
8888 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
8889 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
8890 unordered_node_map(InputIterator, InputIterator, Allocator)
8891 -> unordered_node_map<boost::unordered::detail::iter_key_t<InputIterator>,
8892 boost::unordered::detail::iter_val_t<InputIterator>,
8893 boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
8894 std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
8895 Allocator>;
8896
8897 template <class InputIterator, class Hash, class Allocator,
8898 class = std::enable_if_t<detail::is_hash_v<Hash> >,
8899 class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
8900 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
8901 unordered_node_map(
8902 InputIterator, InputIterator, std::size_t, Hash, Allocator)
8903 -> unordered_node_map<boost::unordered::detail::iter_key_t<InputIterator>,
8904 boost::unordered::detail::iter_val_t<InputIterator>, Hash,
8905 std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
8906 Allocator>;
8907
8908 template <class Key, class T, class Allocator,
8909 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
8910 unordered_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
8911 Allocator) -> unordered_node_map<std::remove_const_t<Key>, T,
8912 boost::hash<std::remove_const_t<Key> >,
8913 std::equal_to<std::remove_const_t<Key> >, Allocator>;
8914
8915 template <class Key, class T, class Allocator,
8916 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
8917 unordered_node_map(std::initializer_list<std::pair<Key, T> >, Allocator)
8918 -> unordered_node_map<std::remove_const_t<Key>, T,
8919 boost::hash<std::remove_const_t<Key> >,
8920 std::equal_to<std::remove_const_t<Key> >, Allocator>;
8921
8922 template <class Key, class T, class Hash, class Allocator,
8923 class = std::enable_if_t<detail::is_hash_v<Hash> >,
8924 class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
8925 unordered_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
8926 Hash, Allocator) -> unordered_node_map<std::remove_const_t<Key>, T,
8927 Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
8928#endif
8929
8930 } // namespace unordered
8931} // namespace boost
8932
8933#endif
#define BOOST_ARCH_ARM
Definition boost_unordered.hpp:781
#define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
Definition boost_unordered.hpp:760
#define BOOST_CATCH(x)
Definition boost_unordered.hpp:438
#define BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
Definition boost_unordered.hpp:2897
#define BOOST_CURRENT_LOCATION
Definition boost_unordered.hpp:5214
#define BOOST_RETHROW
Definition boost_unordered.hpp:439
#define BOOST_CATCH_END
Definition boost_unordered.hpp:440
#define BOOST_TRY
Definition boost_unordered.hpp:437
#define BOOST_NORETURN
Definition boost_unordered.hpp:140
#define BOOST_FORCEINLINE
Definition boost_unordered.hpp:83
#define BOOST_ASSERT_SNPRINTF(buffer, format, arg)
Definition boost_unordered.hpp:5110
#define BOOST_NOINLINE
Definition boost_unordered.hpp:103
#define BOOST_SYMBOL_VISIBLE
Definition boost_unordered.hpp:112
#define BOOST_ASSERT_MSG(expr, msg)
Definition boost_unordered.hpp:297
#define BOOST_UNLIKELY(x)
Definition boost_unordered.hpp:119
#define BOOST_UNORDERED_ASSUME(cond)
Definition boost_unordered.hpp:743
#define BOOST_LIKELY(x)
Definition boost_unordered.hpp:116
#define BOOST_ASSERT(expr)
Definition boost_unordered.hpp:296