Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:53:19

0001 /* Common base for Boost.Unordered open-addressing tables.
0002  *
0003  * Copyright 2022-2023 Joaquin M Lopez Munoz.
0004  * Copyright 2023 Christian Mazakas.
0005  * Distributed under the Boost Software License, Version 1.0.
0006  * (See accompanying file LICENSE_1_0.txt or copy at
0007  * http://www.boost.org/LICENSE_1_0.txt)
0008  *
0009  * See https://www.boost.org/libs/unordered for library home page.
0010  */
0011 
0012 #ifndef BOOST_UNORDERED_DETAIL_FOA_CORE_HPP
0013 #define BOOST_UNORDERED_DETAIL_FOA_CORE_HPP
0014 
0015 #include <boost/assert.hpp>
0016 #include <boost/config.hpp>
0017 #include <boost/config/workaround.hpp>
0018 #include <boost/core/allocator_traits.hpp>
0019 #include <boost/core/bit.hpp>
0020 #include <boost/core/empty_value.hpp>
0021 #include <boost/core/no_exceptions_support.hpp>
0022 #include <boost/core/pointer_traits.hpp>
0023 #include <boost/cstdint.hpp>
0024 #include <boost/predef.h>
0025 #include <boost/unordered/detail/narrow_cast.hpp>
0026 #include <boost/unordered/detail/mulx.hpp>
0027 #include <boost/unordered/detail/static_assert.hpp>
0028 #include <boost/unordered/detail/type_traits.hpp>
0029 #include <boost/unordered/hash_traits.hpp>
0030 #include <climits>
0031 #include <cmath>
0032 #include <cstddef>
0033 #include <cstring>
0034 #include <limits>
0035 #include <memory>
0036 #include <new>
0037 #include <tuple>
0038 #include <type_traits>
0039 #include <utility>
0040 
0041 #if !defined(BOOST_UNORDERED_DISABLE_SSE2)
0042 #if defined(BOOST_UNORDERED_ENABLE_SSE2)|| \
0043     defined(__SSE2__)|| \
0044     defined(_M_X64)||(defined(_M_IX86_FP)&&_M_IX86_FP>=2)
0045 #define BOOST_UNORDERED_SSE2
0046 #endif
0047 #endif
0048 
0049 #if !defined(BOOST_UNORDERED_DISABLE_NEON)
0050 #if defined(BOOST_UNORDERED_ENABLE_NEON)||\
0051     (defined(__ARM_NEON)&&!defined(__ARM_BIG_ENDIAN))
0052 #define BOOST_UNORDERED_LITTLE_ENDIAN_NEON
0053 #endif
0054 #endif
0055 
0056 #if defined(BOOST_UNORDERED_SSE2)
0057 #include <emmintrin.h>
0058 #elif defined(BOOST_UNORDERED_LITTLE_ENDIAN_NEON)
0059 #include <arm_neon.h>
0060 #endif
0061 
0062 #ifdef __has_builtin
0063 #define BOOST_UNORDERED_HAS_BUILTIN(x) __has_builtin(x)
0064 #else
0065 #define BOOST_UNORDERED_HAS_BUILTIN(x) 0
0066 #endif
0067 
0068 #if !defined(NDEBUG)
0069 #define BOOST_UNORDERED_ASSUME(cond) BOOST_ASSERT(cond)
0070 #elif BOOST_UNORDERED_HAS_BUILTIN(__builtin_assume)
0071 #define BOOST_UNORDERED_ASSUME(cond) __builtin_assume(cond)
0072 #elif defined(__GNUC__) || BOOST_UNORDERED_HAS_BUILTIN(__builtin_unreachable)
0073 #define BOOST_UNORDERED_ASSUME(cond)    \
0074   do{                                   \
0075     if(!(cond))__builtin_unreachable(); \
0076   }while(0)
0077 #elif defined(_MSC_VER)
0078 #define BOOST_UNORDERED_ASSUME(cond) __assume(cond)
0079 #else
0080 #define BOOST_UNORDERED_ASSUME(cond)  \
0081   do{                                 \
0082     static_cast<void>(false&&(cond)); \
0083   }while(0)
0084 #endif
0085 
0086 /* We use BOOST_UNORDERED_PREFETCH[_ELEMENTS] macros rather than proper
0087  * functions because of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109985
0088  */
0089 
0090 #if defined(BOOST_GCC)||defined(BOOST_CLANG)
0091 #define BOOST_UNORDERED_PREFETCH(p) __builtin_prefetch((const char*)(p))
0092 #elif defined(BOOST_UNORDERED_SSE2)
0093 #define BOOST_UNORDERED_PREFETCH(p) _mm_prefetch((const char*)(p),_MM_HINT_T0)
0094 #else
0095 #define BOOST_UNORDERED_PREFETCH(p) ((void)(p))
0096 #endif
0097 
0098 /* We have experimentally confirmed that ARM architectures get a higher
0099  * speedup when around the first half of the element slots in a group are
0100  * prefetched, whereas for Intel just the first cache line is best.
0101  * Please report back if you find better tunings for some particular
0102  * architectures.
0103  */
0104 
0105 #if BOOST_ARCH_ARM
0106 /* Cache line size can't be known at compile time, so we settle on
0107  * the very frequent value of 64B.
0108  */
0109 
0110 #define BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N)                          \
0111   do{                                                                   \
0112     auto           BOOST_UNORDERED_P=(p);                               \
0113     constexpr int  cache_line=64;                                       \
0114     const char    *p0=reinterpret_cast<const char*>(BOOST_UNORDERED_P), \
0115                   *p1=p0+sizeof(*BOOST_UNORDERED_P)*(N)/2;              \
0116     for(;p0<p1;p0+=cache_line)BOOST_UNORDERED_PREFETCH(p0);             \
0117   }while(0)
0118 #else
0119 #define BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N) BOOST_UNORDERED_PREFETCH(p)
0120 #endif
0121 
0122 #ifdef __has_feature
0123 #define BOOST_UNORDERED_HAS_FEATURE(x) __has_feature(x)
0124 #else
0125 #define BOOST_UNORDERED_HAS_FEATURE(x) 0
0126 #endif
0127 
0128 #if BOOST_UNORDERED_HAS_FEATURE(thread_sanitizer)|| \
0129     defined(__SANITIZE_THREAD__)
0130 #define BOOST_UNORDERED_THREAD_SANITIZER
0131 #endif
0132 
0133 #define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)                    \
0134   static_assert(boost::unordered::detail::is_nothrow_swappable<Hash>::value,   \
0135     "Template parameter Hash is required to be nothrow Swappable.");           \
0136   static_assert(boost::unordered::detail::is_nothrow_swappable<Pred>::value,   \
0137     "Template parameter Pred is required to be nothrow Swappable");
0138 
0139 namespace boost{
0140 namespace unordered{
0141 namespace detail{
0142 namespace foa{
0143 
0144 static constexpr std::size_t default_bucket_count=0;
0145 
0146 /* foa::table_core is the common base of foa::table and foa::concurrent_table,
0147  * which in their turn serve as the foundational core of
0148  * boost::unordered_(flat|node)_(map|set) and boost::concurrent_flat_(map|set),
0149  * respectively. Its main internal design aspects are:
0150  * 
0151  *   - Element slots are logically split into groups of size N=15. The number
0152  *     of groups is always a power of two, so the number of allocated slots
0153        is of the form (N*2^n)-1 (final slot reserved for a sentinel mark).
0154  *   - Positioning is done at the group level rather than the slot level, that
0155  *     is, for any given element its hash value is used to locate a group and
0156  *     insertion is performed on the first available element of that group;
0157  *     if the group is full (overflow), further groups are tried using
0158  *     quadratic probing.
0159  *   - Each group has an associated 16B metadata word holding reduced hash
0160  *     values and overflow information. Reduced hash values are used to
0161  *     accelerate lookup within the group by using 128-bit SIMD or 64-bit word
0162  *     operations.
0163  */
0164 
0165 /* group15 controls metadata information of a group of N=15 element slots.
0166  * The 16B metadata word is organized as follows (LSB depicted rightmost):
0167  *
0168  *   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0169  *   |ofw|h14|h13|h13|h11|h10|h09|h08|h07|h06|h05|h04|h03|h02|h01|h00|
0170  *   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0171  *
0172  * hi is 0 if the i-th element slot is avalaible, 1 to mark a sentinel and,
0173  * when the slot is occupied, a value in the range [2,255] obtained from the
0174  * element's original hash value.
0175  * ofw is the so-called overflow byte. If insertion of an element with hash
0176  * value h is tried on a full group, then the (h%8)-th bit of the overflow
0177  * byte is set to 1 and a further group is probed. Having an overflow byte
0178  * brings two advantages:
0179  * 
0180  *   - There's no need to reserve a special value of hi to mark tombstone
0181  *     slots; each reduced hash value keeps then log2(254)=7.99 bits of the
0182  *     original hash (alternative approaches reserve one full bit to mark
0183  *     if the slot is available/deleted, so their reduced hash values are 7 bit
0184  *     strong only).
0185  *   - When doing an unsuccessful lookup (i.e. the element is not present in
0186  *     the table), probing stops at the first non-overflowed group. Having 8
0187  *     bits for signalling overflow makes it very likely that we stop at the
0188  *     current group (this happens when no element with the same (h%8) value
0189  *     has overflowed in the group), saving us an additional group check even
0190  *     under high-load/high-erase conditions. It is critical that hash
0191  *     reduction is invariant under modulo 8 (see maybe_caused_overflow).
0192  *
0193  * When looking for an element with hash value h, match(h) returns a bitmask
0194  * signalling which slots have the same reduced hash value. If available,
0195  * match uses SSE2 or (little endian) Neon 128-bit SIMD operations. On non-SIMD
0196  * scenarios, the logical layout described above is physically mapped to two
0197  * 64-bit words with *bit interleaving*, i.e. the least significant 16 bits of
0198  * the first 64-bit word contain the least significant bits of each byte in the
0199  * "logical" 128-bit word, and so forth. With this layout, match can be
0200  * implemented with 4 ANDs, 3 shifts, 2 XORs, 1 OR and 1 NOT.
0201  * 
0202  * IntegralWrapper<Integral> is used to implement group15's underlying
0203  * metadata: it behaves as a plain integral for foa::table or introduces
0204  * atomic ops for foa::concurrent_table. If IntegralWrapper<...> is trivially
0205  * constructible, so is group15, in which case it can be initialized via memset
0206  * etc. Where needed, group15::initialize resets the metadata to the all
0207  * zeros (default state).
0208  */
0209 
0210 #if defined(BOOST_UNORDERED_SSE2)
0211 
0212 template<template<typename> class IntegralWrapper>
0213 struct group15
0214 {
0215   static constexpr std::size_t N=15;
0216   static constexpr bool        regular_layout=true;
0217 
0218   struct dummy_group_type
0219   {
0220     alignas(16) unsigned char storage[N+1]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0};
0221   };
0222 
0223   inline void initialize()
0224   {
0225     _mm_store_si128(
0226       reinterpret_cast<__m128i*>(m),_mm_setzero_si128());
0227   }
0228 
0229   inline void set(std::size_t pos,std::size_t hash)
0230   {
0231     BOOST_ASSERT(pos<N);
0232     at(pos)=reduced_hash(hash);
0233   }
0234 
0235   inline void set_sentinel()
0236   {
0237     at(N-1)=sentinel_;
0238   }
0239 
0240   inline bool is_sentinel(std::size_t pos)const
0241   {
0242     BOOST_ASSERT(pos<N);
0243     return at(pos)==sentinel_;
0244   }
0245 
0246   static inline bool is_sentinel(unsigned char* pc)noexcept
0247   {
0248     return *pc==sentinel_;
0249   }
0250 
0251   inline void reset(std::size_t pos)
0252   {
0253     BOOST_ASSERT(pos<N);
0254     at(pos)=available_;
0255   }
0256 
0257   static inline void reset(unsigned char* pc)
0258   {
0259     *reinterpret_cast<slot_type*>(pc)=available_;
0260   }
0261 
0262   inline int match(std::size_t hash)const
0263   {
0264     return _mm_movemask_epi8(
0265       _mm_cmpeq_epi8(load_metadata(),_mm_set1_epi32(match_word(hash))))&0x7FFF;
0266   }
0267 
0268   inline bool is_not_overflowed(std::size_t hash)const
0269   {
0270     static constexpr unsigned char shift[]={1,2,4,8,16,32,64,128};
0271 
0272     return !(overflow()&shift[hash%8]);
0273   }
0274 
0275   inline void mark_overflow(std::size_t hash)
0276   {
0277     overflow()|=static_cast<unsigned char>(1<<(hash%8));
0278   }
0279 
0280   static inline bool maybe_caused_overflow(unsigned char* pc)
0281   {
0282     std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
0283     group15    *pg=reinterpret_cast<group15*>(pc-pos);
0284     return !pg->is_not_overflowed(*pc);
0285   }
0286 
0287   inline int match_available()const
0288   {
0289     return _mm_movemask_epi8(
0290       _mm_cmpeq_epi8(load_metadata(),_mm_setzero_si128()))&0x7FFF;
0291   }
0292 
0293   inline bool is_occupied(std::size_t pos)const
0294   {
0295     BOOST_ASSERT(pos<N);
0296     return at(pos)!=available_;
0297   }
0298 
0299   static inline bool is_occupied(unsigned char* pc)noexcept
0300   {
0301     return *reinterpret_cast<slot_type*>(pc)!=available_;
0302   }
0303 
0304   inline int match_occupied()const
0305   {
0306     return (~match_available())&0x7FFF;
0307   }
0308 
0309 private:
0310   using slot_type=IntegralWrapper<unsigned char>;
0311   BOOST_UNORDERED_STATIC_ASSERT(sizeof(slot_type)==1);
0312 
0313   static constexpr unsigned char available_=0,
0314                                  sentinel_=1;
0315 
0316   inline __m128i load_metadata()const
0317   {
0318 #if defined(BOOST_UNORDERED_THREAD_SANITIZER)
0319     /* ThreadSanitizer complains on 1-byte atomic writes combined with
0320      * 16-byte atomic reads.
0321      */
0322 
0323     return _mm_set_epi8(
0324       (char)m[15],(char)m[14],(char)m[13],(char)m[12],
0325       (char)m[11],(char)m[10],(char)m[ 9],(char)m[ 8],
0326       (char)m[ 7],(char)m[ 6],(char)m[ 5],(char)m[ 4],
0327       (char)m[ 3],(char)m[ 2],(char)m[ 1],(char)m[ 0]);
0328 #else
0329     return _mm_load_si128(reinterpret_cast<const __m128i*>(m));
0330 #endif
0331   }
0332 
0333   inline static int match_word(std::size_t hash)
0334   {
0335     static constexpr boost::uint32_t word[]=
0336     {
0337       0x08080808u,0x09090909u,0x02020202u,0x03030303u,0x04040404u,0x05050505u,
0338       0x06060606u,0x07070707u,0x08080808u,0x09090909u,0x0A0A0A0Au,0x0B0B0B0Bu,
0339       0x0C0C0C0Cu,0x0D0D0D0Du,0x0E0E0E0Eu,0x0F0F0F0Fu,0x10101010u,0x11111111u,
0340       0x12121212u,0x13131313u,0x14141414u,0x15151515u,0x16161616u,0x17171717u,
0341       0x18181818u,0x19191919u,0x1A1A1A1Au,0x1B1B1B1Bu,0x1C1C1C1Cu,0x1D1D1D1Du,
0342       0x1E1E1E1Eu,0x1F1F1F1Fu,0x20202020u,0x21212121u,0x22222222u,0x23232323u,
0343       0x24242424u,0x25252525u,0x26262626u,0x27272727u,0x28282828u,0x29292929u,
0344       0x2A2A2A2Au,0x2B2B2B2Bu,0x2C2C2C2Cu,0x2D2D2D2Du,0x2E2E2E2Eu,0x2F2F2F2Fu,
0345       0x30303030u,0x31313131u,0x32323232u,0x33333333u,0x34343434u,0x35353535u,
0346       0x36363636u,0x37373737u,0x38383838u,0x39393939u,0x3A3A3A3Au,0x3B3B3B3Bu,
0347       0x3C3C3C3Cu,0x3D3D3D3Du,0x3E3E3E3Eu,0x3F3F3F3Fu,0x40404040u,0x41414141u,
0348       0x42424242u,0x43434343u,0x44444444u,0x45454545u,0x46464646u,0x47474747u,
0349       0x48484848u,0x49494949u,0x4A4A4A4Au,0x4B4B4B4Bu,0x4C4C4C4Cu,0x4D4D4D4Du,
0350       0x4E4E4E4Eu,0x4F4F4F4Fu,0x50505050u,0x51515151u,0x52525252u,0x53535353u,
0351       0x54545454u,0x55555555u,0x56565656u,0x57575757u,0x58585858u,0x59595959u,
0352       0x5A5A5A5Au,0x5B5B5B5Bu,0x5C5C5C5Cu,0x5D5D5D5Du,0x5E5E5E5Eu,0x5F5F5F5Fu,
0353       0x60606060u,0x61616161u,0x62626262u,0x63636363u,0x64646464u,0x65656565u,
0354       0x66666666u,0x67676767u,0x68686868u,0x69696969u,0x6A6A6A6Au,0x6B6B6B6Bu,
0355       0x6C6C6C6Cu,0x6D6D6D6Du,0x6E6E6E6Eu,0x6F6F6F6Fu,0x70707070u,0x71717171u,
0356       0x72727272u,0x73737373u,0x74747474u,0x75757575u,0x76767676u,0x77777777u,
0357       0x78787878u,0x79797979u,0x7A7A7A7Au,0x7B7B7B7Bu,0x7C7C7C7Cu,0x7D7D7D7Du,
0358       0x7E7E7E7Eu,0x7F7F7F7Fu,0x80808080u,0x81818181u,0x82828282u,0x83838383u,
0359       0x84848484u,0x85858585u,0x86868686u,0x87878787u,0x88888888u,0x89898989u,
0360       0x8A8A8A8Au,0x8B8B8B8Bu,0x8C8C8C8Cu,0x8D8D8D8Du,0x8E8E8E8Eu,0x8F8F8F8Fu,
0361       0x90909090u,0x91919191u,0x92929292u,0x93939393u,0x94949494u,0x95959595u,
0362       0x96969696u,0x97979797u,0x98989898u,0x99999999u,0x9A9A9A9Au,0x9B9B9B9Bu,
0363       0x9C9C9C9Cu,0x9D9D9D9Du,0x9E9E9E9Eu,0x9F9F9F9Fu,0xA0A0A0A0u,0xA1A1A1A1u,
0364       0xA2A2A2A2u,0xA3A3A3A3u,0xA4A4A4A4u,0xA5A5A5A5u,0xA6A6A6A6u,0xA7A7A7A7u,
0365       0xA8A8A8A8u,0xA9A9A9A9u,0xAAAAAAAAu,0xABABABABu,0xACACACACu,0xADADADADu,
0366       0xAEAEAEAEu,0xAFAFAFAFu,0xB0B0B0B0u,0xB1B1B1B1u,0xB2B2B2B2u,0xB3B3B3B3u,
0367       0xB4B4B4B4u,0xB5B5B5B5u,0xB6B6B6B6u,0xB7B7B7B7u,0xB8B8B8B8u,0xB9B9B9B9u,
0368       0xBABABABAu,0xBBBBBBBBu,0xBCBCBCBCu,0xBDBDBDBDu,0xBEBEBEBEu,0xBFBFBFBFu,
0369       0xC0C0C0C0u,0xC1C1C1C1u,0xC2C2C2C2u,0xC3C3C3C3u,0xC4C4C4C4u,0xC5C5C5C5u,
0370       0xC6C6C6C6u,0xC7C7C7C7u,0xC8C8C8C8u,0xC9C9C9C9u,0xCACACACAu,0xCBCBCBCBu,
0371       0xCCCCCCCCu,0xCDCDCDCDu,0xCECECECEu,0xCFCFCFCFu,0xD0D0D0D0u,0xD1D1D1D1u,
0372       0xD2D2D2D2u,0xD3D3D3D3u,0xD4D4D4D4u,0xD5D5D5D5u,0xD6D6D6D6u,0xD7D7D7D7u,
0373       0xD8D8D8D8u,0xD9D9D9D9u,0xDADADADAu,0xDBDBDBDBu,0xDCDCDCDCu,0xDDDDDDDDu,
0374       0xDEDEDEDEu,0xDFDFDFDFu,0xE0E0E0E0u,0xE1E1E1E1u,0xE2E2E2E2u,0xE3E3E3E3u,
0375       0xE4E4E4E4u,0xE5E5E5E5u,0xE6E6E6E6u,0xE7E7E7E7u,0xE8E8E8E8u,0xE9E9E9E9u,
0376       0xEAEAEAEAu,0xEBEBEBEBu,0xECECECECu,0xEDEDEDEDu,0xEEEEEEEEu,0xEFEFEFEFu,
0377       0xF0F0F0F0u,0xF1F1F1F1u,0xF2F2F2F2u,0xF3F3F3F3u,0xF4F4F4F4u,0xF5F5F5F5u,
0378       0xF6F6F6F6u,0xF7F7F7F7u,0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,
0379       0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu,
0380     };
0381 
0382     return (int)word[narrow_cast<unsigned char>(hash)];
0383   }
0384 
0385   inline static unsigned char reduced_hash(std::size_t hash)
0386   {
0387     return narrow_cast<unsigned char>(match_word(hash));
0388   }
0389 
0390   inline slot_type& at(std::size_t pos)
0391   {
0392     return m[pos];
0393   }
0394 
0395   inline const slot_type& at(std::size_t pos)const
0396   {
0397     return m[pos];
0398   }
0399 
0400   inline slot_type& overflow()
0401   {
0402     return at(N);
0403   }
0404 
0405   inline const slot_type& overflow()const
0406   {
0407     return at(N);
0408   }
0409 
0410   alignas(16) slot_type m[16];
0411 };
0412 
0413 #elif defined(BOOST_UNORDERED_LITTLE_ENDIAN_NEON)
0414 
0415 template<template<typename> class IntegralWrapper>
0416 struct group15
0417 {
0418   static constexpr std::size_t N=15;
0419   static constexpr bool        regular_layout=true;
0420 
0421   struct dummy_group_type
0422   {
0423     alignas(16) unsigned char storage[N+1]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0};
0424   };
0425 
0426   inline void initialize()
0427   {
0428     vst1q_u8(reinterpret_cast<uint8_t*>(m),vdupq_n_u8(0));
0429   }
0430 
0431   inline void set(std::size_t pos,std::size_t hash)
0432   {
0433     BOOST_ASSERT(pos<N);
0434     at(pos)=reduced_hash(hash);
0435   }
0436 
0437   inline void set_sentinel()
0438   {
0439     at(N-1)=sentinel_;
0440   }
0441 
0442   inline bool is_sentinel(std::size_t pos)const
0443   {
0444     BOOST_ASSERT(pos<N);
0445     return pos==N-1&&at(N-1)==sentinel_;
0446   }
0447 
0448   static inline bool is_sentinel(unsigned char* pc)noexcept
0449   {
0450     return *reinterpret_cast<slot_type*>(pc)==sentinel_;
0451   }
0452 
0453   inline void reset(std::size_t pos)
0454   {
0455     BOOST_ASSERT(pos<N);
0456     at(pos)=available_;
0457   }
0458 
0459   static inline void reset(unsigned char* pc)
0460   {
0461     *reinterpret_cast<slot_type*>(pc)=available_;
0462   }
0463 
0464   inline int match(std::size_t hash)const
0465   {
0466     return simde_mm_movemask_epi8(vceqq_u8(
0467       load_metadata(),vdupq_n_u8(reduced_hash(hash))))&0x7FFF;
0468   }
0469 
0470   inline bool is_not_overflowed(std::size_t hash)const
0471   {
0472     static constexpr unsigned char shift[]={1,2,4,8,16,32,64,128};
0473 
0474     return !(overflow()&shift[hash%8]);
0475   }
0476 
0477   inline void mark_overflow(std::size_t hash)
0478   {
0479     overflow()|=static_cast<unsigned char>(1<<(hash%8));
0480   }
0481 
0482   static inline bool maybe_caused_overflow(unsigned char* pc)
0483   {
0484     std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
0485     group15    *pg=reinterpret_cast<group15*>(pc-pos);
0486     return !pg->is_not_overflowed(*pc);
0487   };
0488 
0489   inline int match_available()const
0490   {
0491     return simde_mm_movemask_epi8(vceqq_u8(
0492       load_metadata(),vdupq_n_u8(0)))&0x7FFF;
0493   }
0494 
0495   inline bool is_occupied(std::size_t pos)const
0496   {
0497     BOOST_ASSERT(pos<N);
0498     return at(pos)!=available_;
0499   }
0500 
0501   static inline bool is_occupied(unsigned char* pc)noexcept
0502   {
0503     return *reinterpret_cast<slot_type*>(pc)!=available_;
0504   }
0505 
0506   inline int match_occupied()const
0507   {
0508     return simde_mm_movemask_epi8(vcgtq_u8(
0509       load_metadata(),vdupq_n_u8(0)))&0x7FFF;
0510   }
0511 
0512 private:
0513   using slot_type=IntegralWrapper<unsigned char>;
0514   BOOST_UNORDERED_STATIC_ASSERT(sizeof(slot_type)==1);
0515 
0516   static constexpr unsigned char available_=0,
0517                                  sentinel_=1;
0518 
0519   inline uint8x16_t load_metadata()const
0520   {
0521 #if defined(BOOST_UNORDERED_THREAD_SANITIZER)
0522     /* ThreadSanitizer complains on 1-byte atomic writes combined with
0523      * 16-byte atomic reads.
0524      */
0525 
0526     alignas(16) uint8_t data[16]={
0527       m[ 0],m[ 1],m[ 2],m[ 3],m[ 4],m[ 5],m[ 6],m[ 7],
0528       m[ 8],m[ 9],m[10],m[11],m[12],m[13],m[14],m[15]};
0529     return vld1q_u8(data);
0530 #else
0531     return vld1q_u8(reinterpret_cast<const uint8_t*>(m));
0532 #endif
0533   }
0534 
0535   inline static unsigned char reduced_hash(std::size_t hash)
0536   {
0537     static constexpr unsigned char table[]={
0538       8,9,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
0539       16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
0540       32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
0541       48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
0542       64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
0543       80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
0544       96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
0545       112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
0546       128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
0547       144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
0548       160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
0549       176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
0550       192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
0551       208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
0552       224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
0553       240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
0554     };
0555     
0556     return table[(unsigned char)hash];
0557   }
0558 
0559   /* Copied from 
0560    * https://github.com/simd-everywhere/simde/blob/master/simde/x86/
0561    * sse2.h#L3763
0562    */
0563 
0564   static inline int simde_mm_movemask_epi8(uint8x16_t a)
0565   {
0566     static constexpr uint8_t md[16]={
0567       1 << 0, 1 << 1, 1 << 2, 1 << 3,
0568       1 << 4, 1 << 5, 1 << 6, 1 << 7,
0569       1 << 0, 1 << 1, 1 << 2, 1 << 3,
0570       1 << 4, 1 << 5, 1 << 6, 1 << 7,
0571     };
0572 
0573     uint8x16_t  masked=vandq_u8(vld1q_u8(md),a);
0574     uint8x8x2_t tmp=vzip_u8(vget_low_u8(masked),vget_high_u8(masked));
0575     uint16x8_t  x=vreinterpretq_u16_u8(vcombine_u8(tmp.val[0],tmp.val[1]));
0576 
0577 #if defined(__ARM_ARCH_ISA_A64)
0578     return vaddvq_u16(x);
0579 #else
0580     uint64x2_t t64=vpaddlq_u32(vpaddlq_u16(x));
0581     return int(vgetq_lane_u64(t64,0))+int(vgetq_lane_u64(t64,1));
0582 #endif
0583   }
0584 
0585   inline slot_type& at(std::size_t pos)
0586   {
0587     return m[pos];
0588   }
0589 
0590   inline const slot_type& at(std::size_t pos)const
0591   {
0592     return m[pos];
0593   }
0594 
0595   inline slot_type& overflow()
0596   {
0597     return at(N);
0598   }
0599 
0600   inline const slot_type& overflow()const
0601   {
0602     return at(N);
0603   }
0604 
0605   alignas(16) slot_type m[16];
0606 };
0607 
0608 #else /* non-SIMD */
0609 
0610 template<template<typename> class IntegralWrapper>
0611 struct group15
0612 {
0613   static constexpr std::size_t N=15;
0614   static constexpr bool        regular_layout=false;
0615 
0616   struct dummy_group_type
0617   {
0618     alignas(16) boost::uint64_t m[2]=
0619       {0x0000000000004000ull,0x0000000000000000ull};
0620   };
0621 
0622   inline void initialize(){m[0]=0;m[1]=0;}
0623 
0624   inline void set(std::size_t pos,std::size_t hash)
0625   {
0626     BOOST_ASSERT(pos<N);
0627     set_impl(pos,reduced_hash(hash));
0628   }
0629 
0630   inline void set_sentinel()
0631   {
0632     set_impl(N-1,sentinel_);
0633   }
0634 
0635   inline bool is_sentinel(std::size_t pos)const
0636   {
0637     BOOST_ASSERT(pos<N);
0638     return 
0639       pos==N-1&&
0640       (m[0] & boost::uint64_t(0x4000400040004000ull))==
0641         boost::uint64_t(0x4000ull)&&
0642       (m[1] & boost::uint64_t(0x4000400040004000ull))==0;
0643   }
0644 
0645   inline void reset(std::size_t pos)
0646   {
0647     BOOST_ASSERT(pos<N);
0648     set_impl(pos,available_);
0649   }
0650 
0651   static inline void reset(unsigned char* pc)
0652   {
0653     std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
0654     pc-=pos;
0655     reinterpret_cast<group15*>(pc)->reset(pos);
0656   }
0657 
0658   inline int match(std::size_t hash)const
0659   {
0660     return match_impl(reduced_hash(hash));
0661   }
0662 
0663   inline bool is_not_overflowed(std::size_t hash)const
0664   {
0665     return !(reinterpret_cast<const boost::uint16_t*>(m)[hash%8] & 0x8000u);
0666   }
0667 
0668   inline void mark_overflow(std::size_t hash)
0669   {
0670     reinterpret_cast<boost::uint16_t*>(m)[hash%8]|=0x8000u;
0671   }
0672 
0673   static inline bool maybe_caused_overflow(unsigned char* pc)
0674   {
0675     std::size_t     pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
0676     group15        *pg=reinterpret_cast<group15*>(pc-pos);
0677     boost::uint64_t x=((pg->m[0])>>pos)&0x000100010001ull;
0678     boost::uint32_t y=narrow_cast<boost::uint32_t>(x|(x>>15)|(x>>30));
0679     return !pg->is_not_overflowed(y);
0680   };
0681 
0682   inline int match_available()const
0683   {
0684     boost::uint64_t x=~(m[0]|m[1]);
0685     boost::uint32_t y=static_cast<boost::uint32_t>(x&(x>>32));
0686     y&=y>>16;
0687     return y&0x7FFF;
0688   }
0689 
0690   inline bool is_occupied(std::size_t pos)const
0691   {
0692     BOOST_ASSERT(pos<N);
0693     boost::uint64_t x=m[0]|m[1];
0694     return (x&(0x0001000100010001ull<<pos))!=0;
0695   }
0696 
0697   inline int match_occupied()const
0698   {
0699     boost::uint64_t x=m[0]|m[1];
0700     boost::uint32_t y=narrow_cast<boost::uint32_t>(x|(x>>32));
0701     y|=y>>16;
0702     return y&0x7FFF;
0703   }
0704 
0705 private:
0706   using word_type=IntegralWrapper<uint64_t>;
0707   BOOST_UNORDERED_STATIC_ASSERT(sizeof(word_type)==8);
0708 
0709   static constexpr unsigned char available_=0,
0710                                  sentinel_=1;
0711 
0712   inline static unsigned char reduced_hash(std::size_t hash)
0713   {
0714     static constexpr unsigned char table[]={
0715       8,9,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
0716       16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
0717       32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
0718       48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
0719       64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
0720       80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
0721       96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
0722       112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
0723       128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
0724       144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
0725       160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
0726       176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
0727       192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
0728       208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
0729       224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
0730       240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
0731     };
0732     
0733     return table[narrow_cast<unsigned char>(hash)];
0734   }
0735 
0736   inline void set_impl(std::size_t pos,std::size_t n)
0737   {
0738     BOOST_ASSERT(n<256);
0739     set_impl(m[0],pos,n&0xFu);
0740     set_impl(m[1],pos,n>>4);
0741   }
0742 
0743   static inline void set_impl(word_type& x,std::size_t pos,std::size_t n)
0744   {
0745     static constexpr boost::uint64_t mask[]=
0746     {
0747       0x0000000000000000ull,0x0000000000000001ull,0x0000000000010000ull,
0748       0x0000000000010001ull,0x0000000100000000ull,0x0000000100000001ull,
0749       0x0000000100010000ull,0x0000000100010001ull,0x0001000000000000ull,
0750       0x0001000000000001ull,0x0001000000010000ull,0x0001000000010001ull,
0751       0x0001000100000000ull,0x0001000100000001ull,0x0001000100010000ull,
0752       0x0001000100010001ull,
0753     };
0754     static constexpr boost::uint64_t imask[]=
0755     {
0756       0x0001000100010001ull,0x0001000100010000ull,0x0001000100000001ull,
0757       0x0001000100000000ull,0x0001000000010001ull,0x0001000000010000ull,
0758       0x0001000000000001ull,0x0001000000000000ull,0x0000000100010001ull,
0759       0x0000000100010000ull,0x0000000100000001ull,0x0000000100000000ull,
0760       0x0000000000010001ull,0x0000000000010000ull,0x0000000000000001ull,
0761       0x0000000000000000ull,
0762     };
0763 
0764     BOOST_ASSERT(pos<16&&n<16);
0765     x|=   mask[n]<<pos;
0766     x&=~(imask[n]<<pos);
0767   }
0768 
0769   inline int match_impl(std::size_t n)const
0770   {
0771     static constexpr boost::uint64_t mask[]=
0772     {
0773       0x0000000000000000ull,0x000000000000ffffull,0x00000000ffff0000ull,
0774       0x00000000ffffffffull,0x0000ffff00000000ull,0x0000ffff0000ffffull,
0775       0x0000ffffffff0000ull,0x0000ffffffffffffull,0xffff000000000000ull,
0776       0xffff00000000ffffull,0xffff0000ffff0000ull,0xffff0000ffffffffull,
0777       0xffffffff00000000ull,0xffffffff0000ffffull,0xffffffffffff0000ull,
0778       0xffffffffffffffffull,
0779     };
0780 
0781     BOOST_ASSERT(n<256);
0782     boost::uint64_t x=m[0]^mask[n&0xFu];
0783                     x=~((m[1]^mask[n>>4])|x);
0784     boost::uint32_t y=static_cast<boost::uint32_t>(x&(x>>32));
0785                     y&=y>>16;
0786     return          y&0x7FFF;
0787   }
0788 
0789   alignas(16) word_type m[2];
0790 };
0791 
0792 #endif
0793 
0794 /* foa::table_core uses a size policy to obtain the permissible sizes of the
0795  * group array (and, by implication, the element array) and to do the
0796  * hash->group mapping.
0797  * 
0798  *   - size_index(n) returns an unspecified "index" number used in other policy
0799  *     operations.
0800  *   - size(size_index_) returns the number of groups for the given index. It
0801  *     is guaranteed that size(size_index(n)) >= n.
0802  *   - min_size() is the minimum number of groups permissible, i.e.
0803  *     size(size_index(0)).
0804  *   - position(hash,size_index_) maps hash to a position in the range
0805  *     [0,size(size_index_)).
0806  * 
0807  * The reason we're introducing the intermediate index value for calculating
0808  * sizes and positions is that it allows us to optimize the implementation of
0809  * position, which is in the hot path of lookup and insertion operations:
0810  * pow2_size_policy, the actual size policy used by foa::table, returns 2^n
0811  * (n>0) as permissible sizes and returns the n most significant bits
0812  * of the hash value as the position in the group array; using a size index
0813  * defined as i = (bits in std::size_t) - n, we have an unbeatable
0814  * implementation of position(hash) as hash>>i.
0815  * There's a twofold reason for choosing the high bits of hash for positioning:
0816  *   - Multiplication-based mixing tends to yield better entropy in the high
0817  *     part of its result.
0818  *   - group15 reduced-hash values take the *low* bits of hash, and we want
0819  *     these values and positioning to be as uncorrelated as possible.
0820  */
0821 
0822 struct pow2_size_policy
0823 {
0824   static inline std::size_t size_index(std::size_t n)
0825   {
0826     // TODO: min size is 2, see if we can bring it down to 1 without loss
0827     // of performance
0828 
0829     return sizeof(std::size_t)*CHAR_BIT-
0830       (n<=2?1:((std::size_t)(boost::core::bit_width(n-1))));
0831   }
0832 
0833   static inline std::size_t size(std::size_t size_index_)
0834   {
0835      return std::size_t(1)<<(sizeof(std::size_t)*CHAR_BIT-size_index_);  
0836   }
0837     
0838   static constexpr std::size_t min_size(){return 2;}
0839 
0840   static inline std::size_t position(std::size_t hash,std::size_t size_index_)
0841   {
0842     return hash>>size_index_;
0843   }
0844 };
0845 
0846 /* size index of a group array for a given *element* capacity */
0847 
0848 template<typename Group,typename SizePolicy>
0849 static inline std::size_t size_index_for(std::size_t n)
0850 {
0851   /* n/N+1 == ceil((n+1)/N) (extra +1 for the sentinel) */
0852   return SizePolicy::size_index(n/Group::N+1);
0853 }
0854 
0855 /* Quadratic prober over a power-of-two range using triangular numbers.
0856  * mask in next(mask) must be the range size minus one (and since size is 2^n,
0857  * mask has exactly its n first bits set to 1).
0858  */
0859 
0860 struct pow2_quadratic_prober
0861 {
0862   pow2_quadratic_prober(std::size_t pos_):pos{pos_}{}
0863 
0864   inline std::size_t get()const{return pos;}
0865 
0866   /* next returns false when the whole array has been traversed, which ends
0867    * probing (in practice, full-table probing will only happen with very small
0868    * arrays).
0869    */
0870 
0871   inline bool next(std::size_t mask)
0872   {
0873     step+=1;
0874     pos=(pos+step)&mask;
0875     return step<=mask;
0876   }
0877 
0878 private:
0879   std::size_t pos,step=0;
0880 };
0881 
0882 /* Mixing policies: no_mix is the identity function, and mulx_mix
0883  * uses the mulx function from <boost/unordered/detail/mulx.hpp>.
0884  *
0885  * foa::table_core mixes hash results with mulx_mix unless the hash is marked
0886  * as avalanching, i.e. of good quality
0887  * (see <boost/unordered/hash_traits.hpp>).
0888  */
0889 
0890 struct no_mix
0891 {
0892   template<typename Hash,typename T>
0893   static inline std::size_t mix(const Hash& h,const T& x)
0894   {
0895     return h(x);
0896   }
0897 };
0898 
0899 struct mulx_mix
0900 {
0901   template<typename Hash,typename T>
0902   static inline std::size_t mix(const Hash& h,const T& x)
0903   {
0904     return mulx(h(x));
0905   }
0906 };
0907 
0908 /* boost::core::countr_zero has a potentially costly check for
0909  * the case x==0.
0910  */
0911 
0912 inline unsigned int unchecked_countr_zero(int x)
0913 {
0914 #if defined(BOOST_MSVC)
0915   unsigned long r;
0916   _BitScanForward(&r,(unsigned long)x);
0917   return (unsigned int)r;
0918 #else
0919   BOOST_UNORDERED_ASSUME(x!=0);
0920   return (unsigned int)boost::core::countr_zero((unsigned int)x);
0921 #endif
0922 }
0923 
0924 /* table_arrays controls allocation, initialization and deallocation of
0925  * paired arrays of groups and element slots. Only one chunk of memory is
0926  * allocated to place both arrays: this is not done for efficiency reasons,
0927  * but in order to be able to properly align the group array without storing
0928  * additional offset information --the alignment required (16B) is usually
0929  * greater than alignof(std::max_align_t) and thus not guaranteed by
0930  * allocators.
0931  */
0932 
0933 template<typename Group,std::size_t Size>
0934 Group* dummy_groups()
0935 {
0936   /* Dummy storage initialized as if in an empty container (actually, each
0937    * of its groups is initialized like a separate empty container).
0938    * We make table_arrays::groups point to this when capacity()==0, so that
0939    * we are not allocating any dynamic memory and yet lookup can be implemented
0940    * without checking for groups==nullptr. This space won't ever be used for
0941    * insertion as the container's capacity is precisely zero.
0942    */
0943 
0944   static constexpr typename Group::dummy_group_type
0945   storage[Size]={typename Group::dummy_group_type(),};
0946 
0947   return reinterpret_cast<Group*>(
0948     const_cast<typename Group::dummy_group_type*>(storage));
0949 }
0950 
0951 template<
0952   typename Ptr,typename Ptr2,
0953   typename std::enable_if<!std::is_same<Ptr,Ptr2>::value>::type* = nullptr
0954 >
0955 Ptr to_pointer(Ptr2 p)
0956 {
0957   if(!p){return nullptr;}
0958   return boost::pointer_traits<Ptr>::pointer_to(*p);
0959 }
0960 
0961 template<typename Ptr>
0962 Ptr to_pointer(Ptr p)
0963 {
0964   return p;
0965 }
0966 
0967 template<typename Arrays,typename Allocator>
0968 struct arrays_holder
0969 {
0970   arrays_holder(const Arrays& arrays,const Allocator& al):
0971     arrays_{arrays},al_{al}
0972   {}
0973   
0974   /* not defined but VS in pre-C++17 mode needs to see it for RVO */
0975   arrays_holder(arrays_holder const&);
0976   arrays_holder& operator=(arrays_holder const&)=delete;
0977 
0978   ~arrays_holder(){if(!released_)arrays_.delete_(al_,arrays_);}
0979 
0980   const Arrays& release()
0981   {
0982     released_=true;
0983     return arrays_;
0984   }
0985 
0986 private:
0987   Arrays    arrays_;
0988   Allocator al_;
0989   bool      released_=false;
0990 };
0991 
0992 template<typename Value,typename Group,typename SizePolicy,typename Allocator>
0993 struct table_arrays
0994 {
0995   using allocator_type=typename boost::allocator_rebind<Allocator,Value>::type;
0996 
0997   using value_type=Value;
0998   using group_type=Group;
0999   static constexpr auto N=group_type::N;
1000   using size_policy=SizePolicy;
1001   using value_type_pointer=
1002     typename boost::allocator_pointer<allocator_type>::type;
1003   using group_type_pointer=
1004     typename boost::pointer_traits<value_type_pointer>::template
1005       rebind<group_type>;
1006   using group_type_pointer_traits=boost::pointer_traits<group_type_pointer>;
1007 
1008   table_arrays(
1009     std::size_t gsi,std::size_t gsm,
1010     group_type_pointer pg,value_type_pointer pe):
1011     groups_size_index{gsi},groups_size_mask{gsm},groups_{pg},elements_{pe}{}
1012 
1013   value_type* elements()const noexcept{return boost::to_address(elements_);}
1014   group_type* groups()const noexcept{return boost::to_address(groups_);}
1015 
1016   static void set_arrays(table_arrays& arrays,allocator_type al,std::size_t n)
1017   {
1018     return set_arrays(
1019       arrays,al,n,std::is_same<group_type*,group_type_pointer>{});
1020   }
1021 
1022   static void set_arrays(
1023     table_arrays& arrays,allocator_type al,std::size_t,
1024     std::false_type /* always allocate */)
1025   {
1026     using storage_traits=boost::allocator_traits<allocator_type>;
1027     auto groups_size_index=arrays.groups_size_index;
1028     auto groups_size=size_policy::size(groups_size_index);
1029 
1030     auto sal=allocator_type(al);
1031     arrays.elements_=storage_traits::allocate(sal,buffer_size(groups_size));
1032     
1033     /* Align arrays.groups to sizeof(group_type). table_iterator critically
1034       * depends on such alignment for its increment operation.
1035       */
1036 
1037     auto p=reinterpret_cast<unsigned char*>(arrays.elements()+groups_size*N-1);
1038     p+=(uintptr_t(sizeof(group_type))-
1039         reinterpret_cast<uintptr_t>(p))%sizeof(group_type);
1040     arrays.groups_=
1041       group_type_pointer_traits::pointer_to(*reinterpret_cast<group_type*>(p));
1042 
1043     initialize_groups(
1044       arrays.groups(),groups_size,
1045       is_trivially_default_constructible<group_type>{});
1046     arrays.groups()[groups_size-1].set_sentinel();
1047   }
1048 
1049   static void set_arrays(
1050     table_arrays& arrays,allocator_type al,std::size_t n,
1051     std::true_type /* optimize for n==0*/)
1052   {
1053     if(!n){
1054       arrays.groups_=dummy_groups<group_type,size_policy::min_size()>();
1055     }
1056     else{
1057       set_arrays(arrays,al,n,std::false_type{});
1058     }
1059   }
1060 
1061   static table_arrays new_(allocator_type al,std::size_t n)
1062   {
1063     auto         groups_size_index=size_index_for<group_type,size_policy>(n);
1064     auto         groups_size=size_policy::size(groups_size_index);
1065     table_arrays arrays{groups_size_index,groups_size-1,nullptr,nullptr};
1066 
1067     set_arrays(arrays,al,n);
1068     return arrays;
1069   }
1070 
1071   static void delete_(allocator_type al,table_arrays& arrays)noexcept
1072   {
1073     using storage_traits=boost::allocator_traits<allocator_type>;
1074 
1075     auto sal=allocator_type(al);
1076     if(arrays.elements()){
1077       storage_traits::deallocate(
1078         sal,arrays.elements_,buffer_size(arrays.groups_size_mask+1));
1079     }
1080   }
1081 
1082   /* combined space for elements and groups measured in sizeof(value_type)s */
1083 
1084   static std::size_t buffer_size(std::size_t groups_size)
1085   {
1086     auto buffer_bytes=
1087       /* space for elements (we subtract 1 because of the sentinel) */
1088       sizeof(value_type)*(groups_size*N-1)+
1089       /* space for groups + padding for group alignment */
1090       sizeof(group_type)*(groups_size+1)-1;
1091 
1092     /* ceil(buffer_bytes/sizeof(value_type)) */
1093     return (buffer_bytes+sizeof(value_type)-1)/sizeof(value_type);
1094   }
1095 
1096   static void initialize_groups(
1097     group_type* pg,std::size_t size,std::true_type /* memset */)
1098   {
1099     /* memset faster/not slower than manual, assumes all zeros is group_type's
1100      * default layout.
1101      * reinterpret_cast: GCC may complain about group_type not being trivially
1102      * copy-assignable when we're relying on trivial copy constructibility.
1103      */
1104 
1105     std::memset(
1106       reinterpret_cast<unsigned char*>(pg),0,sizeof(group_type)*size);
1107   }
1108 
1109   static void initialize_groups(
1110     group_type* pg,std::size_t size,std::false_type /* manual */)
1111   {
1112     while(size--!=0)::new (pg++) group_type();
1113   }
1114 
1115   std::size_t        groups_size_index;
1116   std::size_t        groups_size_mask;
1117   group_type_pointer groups_;
1118   value_type_pointer elements_;
1119 };
1120 
1121 struct if_constexpr_void_else{void operator()()const{}};
1122 
1123 template<bool B,typename F,typename G=if_constexpr_void_else>
1124 void if_constexpr(F f,G g={})
1125 {
1126   std::get<B?0:1>(std::forward_as_tuple(f,g))();
1127 }
1128 
1129 template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
1130 void copy_assign_if(T& x,const T& y){x=y;}
1131 
1132 template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
1133 void copy_assign_if(T&,const T&){}
1134 
1135 template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
1136 void move_assign_if(T& x,T& y){x=std::move(y);}
1137 
1138 template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
1139 void move_assign_if(T&,T&){}
1140 
1141 template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
1142 void swap_if(T& x,T& y){using std::swap; swap(x,y);}
1143 
1144 template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
1145 void swap_if(T&,T&){}
1146 
1147 template<typename Allocator>
1148 struct is_std_allocator:std::false_type{};
1149 
1150 template<typename T>
1151 struct is_std_allocator<std::allocator<T>>:std::true_type{};
1152 
1153 /* std::allocator::construct marked as deprecated */
1154 #if defined(_LIBCPP_SUPPRESS_DEPRECATED_PUSH)
1155 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
1156 #elif defined(_STL_DISABLE_DEPRECATED_WARNING)
1157 _STL_DISABLE_DEPRECATED_WARNING
1158 #elif defined(_MSC_VER)
1159 #pragma warning(push)
1160 #pragma warning(disable:4996)
1161 #endif
1162 
1163 template<typename Allocator,typename Ptr,typename... Args>
1164 struct alloc_has_construct
1165 {
1166 private:
1167   template<typename Allocator2>
1168   static decltype(
1169     std::declval<Allocator2&>().construct(
1170       std::declval<Ptr>(),std::declval<Args&&>()...),
1171     std::true_type{}
1172   ) check(int);
1173 
1174   template<typename> static std::false_type check(...);
1175 
1176 public:
1177   static constexpr bool value=decltype(check<Allocator>(0))::value;
1178 };
1179 
1180 #if defined(_LIBCPP_SUPPRESS_DEPRECATED_POP)
1181 _LIBCPP_SUPPRESS_DEPRECATED_POP
1182 #elif defined(_STL_RESTORE_DEPRECATED_WARNING)
1183 _STL_RESTORE_DEPRECATED_WARNING
1184 #elif defined(_MSC_VER)
1185 #pragma warning(pop)
1186 #endif
1187 
1188 /* We expose the hard-coded max load factor so that tests can use it without
1189  * needing to pull it from an instantiated class template such as the table
1190  * class.
1191  */
1192 static constexpr float mlf=0.875f;
1193 
1194 template<typename Group,typename Element>
1195 struct table_locator
1196 {
1197   table_locator()=default;
1198   table_locator(Group* pg_,unsigned int n_,Element* p_):pg{pg_},n{n_},p{p_}{}
1199 
1200   explicit operator bool()const noexcept{return p!=nullptr;}
1201 
1202   Group        *pg=nullptr;
1203   unsigned int  n=0;
1204   Element      *p=nullptr;
1205 };
1206 
1207 struct try_emplace_args_t{};
1208 
1209 template<typename TypePolicy,typename Allocator,typename... Args>
1210 class alloc_cted_insert_type
1211 {
1212   using emplace_type=typename std::conditional<
1213     std::is_constructible<typename TypePolicy::init_type,Args...>::value,
1214     typename TypePolicy::init_type,
1215     typename TypePolicy::value_type
1216   >::type;
1217 
1218   using insert_type=typename std::conditional<
1219     std::is_constructible<typename TypePolicy::value_type,emplace_type>::value,
1220     emplace_type,typename TypePolicy::element_type
1221   >::type;
1222 
1223   alignas(insert_type) unsigned char storage[sizeof(insert_type)];
1224   Allocator                          al;
1225 
1226 public:
1227   alloc_cted_insert_type(const Allocator& al_,Args&&... args):al{al_}
1228   {
1229     TypePolicy::construct(al,data(),std::forward<Args>(args)...);
1230   }
1231 
1232   ~alloc_cted_insert_type()
1233   {
1234     TypePolicy::destroy(al,data());
1235   }
1236 
1237   insert_type* data(){return reinterpret_cast<insert_type*>(&storage);}
1238   insert_type& value(){return *data();}
1239 };
1240 
1241 template<typename TypePolicy,typename Allocator,typename... Args>
1242 alloc_cted_insert_type<TypePolicy,Allocator,Args...>
1243 alloc_make_insert_type(const Allocator& al,Args&&... args)
1244 {
1245   return {al,std::forward<Args>(args)...};
1246 }
1247 
1248 /* table_core. The TypePolicy template parameter is used to generate
1249  * instantiations suitable for either maps or sets, and introduces non-standard
1250  * init_type and element_type:
1251  *
1252  *   - TypePolicy::key_type and TypePolicy::value_type have the obvious
1253  *     meaning. TypePolicy::mapped_type is expected to be provided as well
1254  *     when key_type and value_type are not the same.
1255  *
1256  *   - TypePolicy::init_type is the type implicitly converted to when
1257  *     writing x.insert({...}). For maps, this is std::pair<Key,T> rather
1258  *     than std::pair<const Key,T> so that, for instance, x.insert({"hello",0})
1259  *     produces a cheaply moveable std::string&& ("hello") rather than
1260  *     a copyable const std::string&&. foa::table::insert is extended to accept
1261  *     both init_type and value_type references.
1262  *
1263  *   - TypePolicy::construct and TypePolicy::destroy are used for the
1264  *     construction and destruction of the internal types: value_type,
1265  *     init_type and element_type.
1266  * 
1267  *   - TypePolicy::move is used to provide move semantics for the internal
1268  *     types used by the container during rehashing and emplace. These types
1269  *     are init_type, value_type and emplace_type. During insertion, a
1270  *     stack-local type will be created based on the constructibility of the
1271  *     value_type and the supplied arguments. TypePolicy::move is used here
1272  *     for transfer of ownership. Similarly, TypePolicy::move is also used
1273  *     during rehashing when elements are moved to the new table.
1274  *
1275  *   - TypePolicy::extract returns a const reference to the key part of
1276  *     a value of type value_type, init_type, element_type or
1277  *     decltype(TypePolicy::move(...)).
1278  *
1279  *   - TypePolicy::element_type is the type that table_arrays uses when
1280  *     allocating buckets, which allows us to have flat and node container.
1281  *     For flat containers, element_type is value_type. For node
1282  *     containers, it is a strong typedef to value_type*.
1283  *
1284  *   - TypePolicy::value_from returns a mutable reference to value_type from
1285  *     a given element_type. This is used when elements of the table themselves
1286  *     need to be moved, such as during move construction/assignment when
1287  *     allocators are unequal and there is no propagation. For all other cases,
1288  *     the element_type itself is moved.
1289  */
1290 
1291 #include <boost/unordered/detail/foa/ignore_wshadow.hpp>
1292 
1293 #if defined(BOOST_MSVC)
1294 #pragma warning(push)
1295 #pragma warning(disable:4714) /* marked as __forceinline not inlined */
1296 #endif
1297 
1298 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
1299 /* VS2015 marks as unreachable generic catch clauses around non-throwing
1300  * code.
1301  */
1302 #pragma warning(push)
1303 #pragma warning(disable:4702)
1304 #endif
1305 
1306 template<
1307   typename TypePolicy,typename Group,template<typename...> class Arrays,
1308   typename SizeControl,typename Hash,typename Pred,typename Allocator
1309 >
1310 class 
1311 
1312 #if defined(_MSC_VER)&&_MSC_FULL_VER>=190023918
1313 __declspec(empty_bases) /* activate EBO with multiple inheritance */
1314 #endif
1315 
1316 table_core:empty_value<Hash,0>,empty_value<Pred,1>,empty_value<Allocator,2>
1317 {
1318 public:
1319   using type_policy=TypePolicy;
1320   using group_type=Group;
1321   static constexpr auto N=group_type::N;
1322   using size_policy=pow2_size_policy;
1323   using prober=pow2_quadratic_prober;
1324   using mix_policy=typename std::conditional<
1325     hash_is_avalanching<Hash>::value,
1326     no_mix,
1327     mulx_mix
1328   >::type;
1329   using alloc_traits=boost::allocator_traits<Allocator>;
1330   using element_type=typename type_policy::element_type;
1331   using arrays_type=Arrays<element_type,group_type,size_policy,Allocator>;
1332   using size_ctrl_type=SizeControl;
1333   static constexpr auto uses_fancy_pointers=!std::is_same<
1334     typename alloc_traits::pointer,
1335     typename alloc_traits::value_type*
1336   >::value;
1337 
1338   using key_type=typename type_policy::key_type;
1339   using init_type=typename type_policy::init_type;
1340   using value_type=typename type_policy::value_type;
1341   using hasher=Hash;
1342   using key_equal=Pred;
1343   using allocator_type=Allocator;
1344   using pointer=value_type*;
1345   using const_pointer=const value_type*;
1346   using reference=value_type&;
1347   using const_reference=const value_type&;
1348   using size_type=std::size_t;
1349   using difference_type=std::ptrdiff_t;
1350   using locator=table_locator<group_type,element_type>;
1351   using arrays_holder_type=arrays_holder<arrays_type,Allocator>;
1352 
1353   table_core(
1354     std::size_t n=default_bucket_count,const Hash& h_=Hash(),
1355     const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
1356     hash_base{empty_init,h_},pred_base{empty_init,pred_},
1357     allocator_base{empty_init,al_},arrays(new_arrays(n)),
1358     size_ctrl{initial_max_load(),0}
1359     {}
1360 
1361   /* genericize on an ArraysFn so that we can do things like delay an
1362    * allocation for the group_access data required by cfoa after the move
1363    * constructors of Hash, Pred have been invoked
1364    */
1365   template<typename ArraysFn>
1366   table_core(
1367     Hash&& h_,Pred&& pred_,Allocator&& al_,
1368     ArraysFn arrays_fn,const size_ctrl_type& size_ctrl_):
1369     hash_base{empty_init,std::move(h_)},
1370     pred_base{empty_init,std::move(pred_)},
1371     allocator_base{empty_init,std::move(al_)},
1372     arrays(arrays_fn()),size_ctrl(size_ctrl_)
1373   {}
1374 
1375   table_core(const table_core& x):
1376     table_core{x,alloc_traits::select_on_container_copy_construction(x.al())}{}
1377 
1378   template<typename ArraysFn>
1379   table_core(table_core&& x,arrays_holder_type&& ah,ArraysFn arrays_fn):
1380     table_core(
1381       std::move(x.h()),std::move(x.pred()),std::move(x.al()),
1382       arrays_fn,x.size_ctrl)
1383   {
1384     x.arrays=ah.release();
1385     x.size_ctrl.ml=x.initial_max_load();
1386     x.size_ctrl.size=0;
1387   }
1388 
1389   table_core(table_core&& x)
1390     noexcept(
1391       std::is_nothrow_move_constructible<Hash>::value&&
1392       std::is_nothrow_move_constructible<Pred>::value&&
1393       std::is_nothrow_move_constructible<Allocator>::value&&
1394       !uses_fancy_pointers):
1395     table_core{
1396       std::move(x),x.make_empty_arrays(),[&x]{return x.arrays;}}
1397   {}
1398 
1399   table_core(const table_core& x,const Allocator& al_):
1400     table_core{std::size_t(std::ceil(float(x.size())/mlf)),x.h(),x.pred(),al_}
1401   {
1402     copy_elements_from(x);
1403   }
1404 
1405   table_core(table_core&& x,const Allocator& al_):
1406     table_core{std::move(x.h()),std::move(x.pred()),al_}
1407   {
1408     if(al()==x.al()){
1409       using std::swap;
1410       swap(arrays,x.arrays);
1411       swap(size_ctrl,x.size_ctrl);
1412     }
1413     else{
1414       reserve(x.size());
1415       clear_on_exit c{x};
1416       (void)c; /* unused var warning */
1417 
1418       /* This works because subsequent x.clear() does not depend on the
1419        * elements' values.
1420        */
1421       x.for_all_elements([this](element_type* p){
1422         unchecked_insert(type_policy::move(type_policy::value_from(*p)));
1423       });
1424     }
1425   }
1426 
1427   ~table_core()noexcept
1428   {
1429     for_all_elements([this](element_type* p){
1430       destroy_element(p);
1431     });
1432     delete_arrays(arrays);
1433   }
1434 
1435   std::size_t initial_max_load()const
1436   {
1437     static constexpr std::size_t small_capacity=2*N-1;
1438 
1439     auto capacity_=capacity();
1440     if(capacity_<=small_capacity){
1441       return capacity_; /* we allow 100% usage */
1442     }
1443     else{
1444       return (std::size_t)(mlf*(float)(capacity_));
1445     }
1446   }
1447 
1448   arrays_holder_type make_empty_arrays()const
1449   {
1450     return make_arrays(0);
1451   }
1452 
1453   table_core& operator=(const table_core& x)
1454   {
1455     BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
1456 
1457     static constexpr auto pocca=
1458       alloc_traits::propagate_on_container_copy_assignment::value;
1459 
1460     if(this!=std::addressof(x)){
1461       /* If copy construction here winds up throwing, the container is still
1462        * left intact so we perform these operations first.
1463        */
1464       hasher    tmp_h=x.h();
1465       key_equal tmp_p=x.pred();
1466 
1467       clear();
1468 
1469       /* Because we've asserted at compile-time that Hash and Pred are nothrow
1470        * swappable, we can safely mutate our source container and maintain
1471        * consistency between the Hash, Pred compatibility.
1472        */
1473       using std::swap;
1474       swap(h(),tmp_h);
1475       swap(pred(),tmp_p);
1476 
1477       if_constexpr<pocca>([&,this]{
1478         if(al()!=x.al()){
1479           auto ah=x.make_arrays(std::size_t(std::ceil(float(x.size())/mlf)));
1480           delete_arrays(arrays);
1481           arrays=ah.release();
1482           size_ctrl.ml=initial_max_load();
1483         }
1484         copy_assign_if<pocca>(al(),x.al());
1485       });
1486       /* noshrink: favor memory reuse over tightness */
1487       noshrink_reserve(x.size());
1488       copy_elements_from(x);
1489     }
1490     return *this;
1491   }
1492 
1493 #if defined(BOOST_MSVC)
1494 #pragma warning(push)
1495 #pragma warning(disable:4127) /* conditional expression is constant */
1496 #endif
1497 
1498   table_core& operator=(table_core&& x)
1499     noexcept(
1500       (alloc_traits::propagate_on_container_move_assignment::value||
1501       alloc_traits::is_always_equal::value)&&!uses_fancy_pointers)
1502   {
1503     BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
1504 
1505     static constexpr auto pocma=
1506       alloc_traits::propagate_on_container_move_assignment::value;
1507 
1508     if(this!=std::addressof(x)){
1509       /* Given ambiguity in implementation strategies briefly discussed here:
1510        * https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2227
1511        *
1512        * we opt into requiring nothrow swappability and eschew the move
1513        * operations associated with Hash, Pred.
1514        *
1515        * To this end, we ensure that the user never has to consider the
1516        * moved-from state of their Hash, Pred objects
1517        */
1518 
1519       using std::swap;
1520 
1521       clear();
1522 
1523       if(pocma||al()==x.al()){
1524         auto ah=x.make_empty_arrays();
1525         swap(h(),x.h());
1526         swap(pred(),x.pred());
1527         delete_arrays(arrays);
1528         move_assign_if<pocma>(al(),x.al());
1529         arrays=x.arrays;
1530         size_ctrl.ml=std::size_t(x.size_ctrl.ml);
1531         size_ctrl.size=std::size_t(x.size_ctrl.size);
1532         x.arrays=ah.release();
1533         x.size_ctrl.ml=x.initial_max_load();
1534         x.size_ctrl.size=0;
1535       }
1536       else{
1537         swap(h(),x.h());
1538         swap(pred(),x.pred());
1539 
1540         /* noshrink: favor memory reuse over tightness */
1541         noshrink_reserve(x.size());
1542         clear_on_exit c{x};
1543         (void)c; /* unused var warning */
1544 
1545         /* This works because subsequent x.clear() does not depend on the
1546          * elements' values.
1547          */
1548         x.for_all_elements([this](element_type* p){
1549           unchecked_insert(type_policy::move(type_policy::value_from(*p)));
1550         });
1551       }
1552     }
1553     return *this;
1554   }
1555 
1556 #if defined(BOOST_MSVC)
1557 #pragma warning(pop) /* C4127 */
1558 #endif
1559 
1560   allocator_type get_allocator()const noexcept{return al();}
1561 
1562   bool        empty()const noexcept{return size()==0;}
1563   std::size_t size()const noexcept{return size_ctrl.size;}
1564   std::size_t max_size()const noexcept{return SIZE_MAX;}
1565 
1566   BOOST_FORCEINLINE
1567   void erase(group_type* pg,unsigned int pos,element_type* p)noexcept
1568   {
1569     destroy_element(p);
1570     recover_slot(pg,pos);
1571   }
1572 
1573   BOOST_FORCEINLINE
1574   void erase(unsigned char* pc,element_type* p)noexcept
1575   {
1576     destroy_element(p);
1577     recover_slot(pc);
1578   }
1579 
1580   template<typename Key>
1581   BOOST_FORCEINLINE locator find(const Key& x)const
1582   {
1583     auto hash=hash_for(x);
1584     return find(x,position_for(hash),hash);
1585   }
1586 
1587 #if defined(BOOST_MSVC)
1588 /* warning: forcing value to bool 'true' or 'false' in bool(pred()...) */
1589 #pragma warning(push)
1590 #pragma warning(disable:4800)
1591 #endif
1592 
1593   template<typename Key>
1594   BOOST_FORCEINLINE locator find(
1595     const Key& x,std::size_t pos0,std::size_t hash)const
1596   {    
1597     prober pb(pos0);
1598     do{
1599       auto pos=pb.get();
1600       auto pg=arrays.groups()+pos;
1601       auto mask=pg->match(hash);
1602       if(mask){
1603         auto elements=arrays.elements();
1604         BOOST_UNORDERED_ASSUME(elements!=nullptr);
1605         auto p=elements+pos*N;
1606         BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
1607         do{
1608           auto n=unchecked_countr_zero(mask);
1609           if(BOOST_LIKELY(bool(pred()(x,key_from(p[n]))))){
1610             return {pg,n,p+n};
1611           }
1612           mask&=mask-1;
1613         }while(mask);
1614       }
1615       if(BOOST_LIKELY(pg->is_not_overflowed(hash))){
1616         return {};
1617       }
1618     }
1619     while(BOOST_LIKELY(pb.next(arrays.groups_size_mask)));
1620     return {};
1621   }
1622 
1623 #if defined(BOOST_MSVC)
1624 #pragma warning(pop) /* C4800 */
1625 #endif
1626 
1627   void swap(table_core& x)
1628     noexcept(
1629       alloc_traits::propagate_on_container_swap::value||
1630       alloc_traits::is_always_equal::value)
1631   {
1632     BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
1633 
1634     static constexpr auto pocs=
1635       alloc_traits::propagate_on_container_swap::value;
1636 
1637     using std::swap;
1638     if_constexpr<pocs>([&,this]{
1639       swap_if<pocs>(al(),x.al());
1640     },
1641     [&,this]{ /* else */
1642       BOOST_ASSERT(al()==x.al());
1643       (void)this; /* makes sure captured this is used */
1644     });
1645 
1646     swap(h(),x.h());
1647     swap(pred(),x.pred());
1648     swap(arrays,x.arrays);
1649     swap(size_ctrl,x.size_ctrl);
1650   }
1651 
1652   void clear()noexcept
1653   {
1654     auto p=arrays.elements();
1655     if(p){
1656       for(auto pg=arrays.groups(),last=pg+arrays.groups_size_mask+1;
1657           pg!=last;++pg,p+=N){
1658         auto mask=match_really_occupied(pg,last);
1659         while(mask){
1660           destroy_element(p+unchecked_countr_zero(mask));
1661           mask&=mask-1;
1662         }
1663         /* we wipe the entire metadata to reset the overflow byte as well */
1664         pg->initialize();
1665       }
1666       arrays.groups()[arrays.groups_size_mask].set_sentinel();
1667       size_ctrl.ml=initial_max_load();
1668       size_ctrl.size=0;
1669     }
1670   }
1671 
1672   hasher hash_function()const{return h();}
1673   key_equal key_eq()const{return pred();}
1674 
1675   std::size_t capacity()const noexcept
1676   {
1677     return arrays.elements()?(arrays.groups_size_mask+1)*N-1:0;
1678   }
1679   
1680   float load_factor()const noexcept
1681   {
1682     if(capacity()==0)return 0;
1683     else             return float(size())/float(capacity());
1684   }
1685 
1686   float max_load_factor()const noexcept{return mlf;}
1687 
1688   std::size_t max_load()const noexcept{return size_ctrl.ml;}
1689 
1690   void rehash(std::size_t n)
1691   {
1692     auto m=size_t(std::ceil(float(size())/mlf));
1693     if(m>n)n=m;
1694     if(n)n=capacity_for(n); /* exact resulting capacity */
1695 
1696     if(n!=capacity())unchecked_rehash(n);
1697   }
1698 
1699   void reserve(std::size_t n)
1700   {
1701     rehash(std::size_t(std::ceil(float(n)/mlf)));
1702   }
1703 
1704   friend bool operator==(const table_core& x,const table_core& y)
1705   {
1706     return
1707       x.size()==y.size()&&
1708       x.for_all_elements_while([&](element_type* p){
1709         auto loc=y.find(key_from(*p));
1710         return loc&&
1711           const_cast<const value_type&>(type_policy::value_from(*p))==
1712           const_cast<const value_type&>(type_policy::value_from(*loc.p));
1713       });
1714   }
1715 
1716   friend bool operator!=(const table_core& x,const table_core& y)
1717   {
1718     return !(x==y);
1719   }
1720 
1721   struct clear_on_exit
1722   {
1723     ~clear_on_exit(){x.clear();}
1724     table_core& x;
1725   };
1726 
1727   Hash&            h(){return hash_base::get();}
1728   const Hash&      h()const{return hash_base::get();}
1729   Pred&            pred(){return pred_base::get();}
1730   const Pred&      pred()const{return pred_base::get();}
1731   Allocator&       al(){return allocator_base::get();}
1732   const Allocator& al()const{return allocator_base::get();}
1733 
1734   template<typename... Args>
1735   void construct_element(element_type* p,Args&&... args)
1736   {
1737     type_policy::construct(al(),p,std::forward<Args>(args)...);
1738   }
1739 
1740   template<typename... Args>
1741   void construct_element(element_type* p,try_emplace_args_t,Args&&... args)
1742   {
1743     construct_element_from_try_emplace_args(
1744       p,
1745       std::integral_constant<bool,std::is_same<key_type,value_type>::value>{},
1746       std::forward<Args>(args)...);
1747   }
1748 
1749   void destroy_element(element_type* p)noexcept
1750   {
1751     type_policy::destroy(al(),p);
1752   }
1753 
1754   struct destroy_element_on_exit
1755   {
1756     ~destroy_element_on_exit(){this_->destroy_element(p);}
1757     table_core   *this_;
1758     element_type *p;
1759   };
1760 
1761   template<typename T>
1762   static inline auto key_from(const T& x)
1763     ->decltype(type_policy::extract(x))
1764   {
1765     return type_policy::extract(x);
1766   }
1767 
1768   template<typename Key,typename... Args>
1769   static inline const Key& key_from(
1770     try_emplace_args_t,const Key& x,const Args&...)
1771   {
1772     return x;
1773   }
1774 
1775   template<typename Key>
1776   inline std::size_t hash_for(const Key& x)const
1777   {
1778     return mix_policy::mix(h(),x);
1779   }
1780 
1781   inline std::size_t position_for(std::size_t hash)const
1782   {
1783     return position_for(hash,arrays);
1784   }
1785 
1786   static inline std::size_t position_for(
1787     std::size_t hash,const arrays_type& arrays_)
1788   {
1789     return size_policy::position(hash,arrays_.groups_size_index);
1790   }
1791 
1792   static inline int match_really_occupied(group_type* pg,group_type* last)
1793   {
1794     /* excluding the sentinel */
1795     return pg->match_occupied()&~(int(pg==last-1)<<(N-1));
1796   }
1797 
1798   template<typename... Args>
1799   locator unchecked_emplace_at(
1800     std::size_t pos0,std::size_t hash,Args&&... args)
1801   {
1802     auto res=nosize_unchecked_emplace_at(
1803       arrays,pos0,hash,std::forward<Args>(args)...);
1804     ++size_ctrl.size;
1805     return res;
1806   }
1807 
1808   BOOST_NOINLINE void unchecked_rehash_for_growth()
1809   {
1810     auto new_arrays_=new_arrays_for_growth();
1811     unchecked_rehash(new_arrays_);
1812   }
1813 
1814   template<typename... Args>
1815   BOOST_NOINLINE locator
1816   unchecked_emplace_with_rehash(std::size_t hash,Args&&... args)
1817   {
1818     auto    new_arrays_=new_arrays_for_growth();
1819     locator it;
1820     BOOST_TRY{
1821       /* strong exception guarantee -> try insertion before rehash */
1822       it=nosize_unchecked_emplace_at(
1823         new_arrays_,position_for(hash,new_arrays_),
1824         hash,std::forward<Args>(args)...);
1825     }
1826     BOOST_CATCH(...){
1827       delete_arrays(new_arrays_);
1828       BOOST_RETHROW
1829     }
1830     BOOST_CATCH_END
1831 
1832     /* new_arrays_ lifetime taken care of by unchecked_rehash */
1833     unchecked_rehash(new_arrays_);
1834     ++size_ctrl.size;
1835     return it;
1836   }
1837 
1838   void noshrink_reserve(std::size_t n)
1839   {
1840     /* used only on assignment after element clearance */
1841     BOOST_ASSERT(empty());
1842 
1843     if(n){
1844       n=std::size_t(std::ceil(float(n)/mlf)); /* elements -> slots */
1845       n=capacity_for(n); /* exact resulting capacity */
1846 
1847       if(n>capacity()){
1848         auto new_arrays_=new_arrays(n);
1849         delete_arrays(arrays);
1850         arrays=new_arrays_;
1851         size_ctrl.ml=initial_max_load();
1852       }
1853     }
1854   }
1855 
1856   template<typename F>
1857   void for_all_elements(F f)const
1858   {
1859     for_all_elements(arrays,f);
1860   }
1861 
1862   template<typename F>
1863   static auto for_all_elements(const arrays_type& arrays_,F f)
1864     ->decltype(f(nullptr),void())
1865   {
1866     for_all_elements_while(arrays_,[&](element_type* p){f(p);return true;});
1867   }
1868 
1869   template<typename F>
1870   static auto for_all_elements(const arrays_type& arrays_,F f)
1871     ->decltype(f(nullptr,0,nullptr),void())
1872   {
1873     for_all_elements_while(
1874       arrays_,[&](group_type* pg,unsigned int n,element_type* p)
1875         {f(pg,n,p);return true;});
1876   }
1877 
1878   template<typename F>
1879   bool for_all_elements_while(F f)const
1880   {
1881     return for_all_elements_while(arrays,f);
1882   }
1883 
1884   template<typename F>
1885   static auto for_all_elements_while(const arrays_type& arrays_,F f)
1886     ->decltype(f(nullptr),bool())
1887   {
1888     return for_all_elements_while(
1889       arrays_,[&](group_type*,unsigned int,element_type* p){return f(p);});
1890   }
1891 
1892   template<typename F>
1893   static auto for_all_elements_while(const arrays_type& arrays_,F f)
1894     ->decltype(f(nullptr,0,nullptr),bool())
1895   {
1896     auto p=arrays_.elements();
1897     if(p){
1898       for(auto pg=arrays_.groups(),last=pg+arrays_.groups_size_mask+1;
1899           pg!=last;++pg,p+=N){
1900         auto mask=match_really_occupied(pg,last);
1901         while(mask){
1902           auto n=unchecked_countr_zero(mask);
1903           if(!f(pg,n,p+n))return false;
1904           mask&=mask-1;
1905         }
1906       }
1907     }
1908     return true;
1909   }
1910 
1911   arrays_type    arrays;
1912   size_ctrl_type size_ctrl;
1913 
1914 private:
1915   template<
1916     typename,typename,template<typename...> class,
1917     typename,typename,typename,typename
1918   >
1919   friend class table_core;
1920 
1921   using hash_base=empty_value<Hash,0>;
1922   using pred_base=empty_value<Pred,1>;
1923   using allocator_base=empty_value<Allocator,2>;
1924 
1925   /* used by allocator-extended move ctor */
1926 
1927   table_core(Hash&& h_,Pred&& pred_,const Allocator& al_):
1928     hash_base{empty_init,std::move(h_)},
1929     pred_base{empty_init,std::move(pred_)},
1930     allocator_base{empty_init,al_},arrays(new_arrays(0)),
1931     size_ctrl{initial_max_load(),0}
1932   {
1933   }
1934 
1935   arrays_type new_arrays(std::size_t n)const
1936   {
1937     return arrays_type::new_(al(),n);
1938   }
1939 
1940   arrays_type new_arrays_for_growth()const
1941   {
1942     /* Due to the anti-drift mechanism (see recover_slot), the new arrays may
1943      * be of the same size as the old arrays; in the limit, erasing one
1944      * element at full load and then inserting could bring us back to the same
1945      * capacity after a costly rehash. To avoid this, we jump to the next
1946      * capacity level when the number of erased elements is <= 10% of total
1947      * elements at full load, which is implemented by requesting additional
1948      * F*size elements, with F = P * 10% / (1 - P * 10%), where P is the
1949      * probability of an element having caused overflow; P has been measured as
1950      * ~0.162 under ideal conditions, yielding F ~ 0.0165 ~ 1/61.
1951      */
1952     return new_arrays(std::size_t(
1953       std::ceil(static_cast<float>(size()+size()/61+1)/mlf)));
1954   }
1955 
1956   void delete_arrays(arrays_type& arrays_)noexcept
1957   {
1958     arrays_type::delete_(al(),arrays_);
1959   }
1960 
1961   arrays_holder_type make_arrays(std::size_t n)const
1962   {
1963     return {new_arrays(n),al()};
1964   }
1965 
1966   template<typename Key,typename... Args>
1967   void construct_element_from_try_emplace_args(
1968     element_type* p,std::false_type,Key&& x,Args&&... args)
1969   {
1970     type_policy::construct(
1971       this->al(),p,
1972       std::piecewise_construct,
1973       std::forward_as_tuple(std::forward<Key>(x)),
1974       std::forward_as_tuple(std::forward<Args>(args)...));
1975   }
1976 
1977   /* This overload allows boost::unordered_flat_set to internally use
1978    * try_emplace to implement heterogeneous insert (P2363).
1979    */
1980 
1981   template<typename Key>
1982   void construct_element_from_try_emplace_args(
1983     element_type* p,std::true_type,Key&& x)
1984   {
1985     type_policy::construct(this->al(),p,std::forward<Key>(x));
1986   }
1987 
1988   void copy_elements_from(const table_core& x)
1989   {
1990     BOOST_ASSERT(empty());
1991     BOOST_ASSERT(this!=std::addressof(x));
1992     if(arrays.groups_size_mask==x.arrays.groups_size_mask){
1993       fast_copy_elements_from(x);
1994     }
1995     else{
1996       x.for_all_elements([this](const element_type* p){
1997         unchecked_insert(*p);
1998       });
1999     }
2000   }
2001 
2002   void fast_copy_elements_from(const table_core& x)
2003   {
2004     if(arrays.elements()&&x.arrays.elements()){
2005       copy_elements_array_from(x);
2006       copy_groups_array_from(x);
2007       size_ctrl.ml=std::size_t(x.size_ctrl.ml);
2008       size_ctrl.size=std::size_t(x.size_ctrl.size);
2009     }
2010   }
2011 
2012   void copy_elements_array_from(const table_core& x)
2013   {
2014     copy_elements_array_from(
2015       x,
2016       std::integral_constant<
2017         bool,
2018         is_trivially_copy_constructible<element_type>::value&&(
2019           is_std_allocator<Allocator>::value||
2020           !alloc_has_construct<Allocator,value_type*,const value_type&>::value)
2021       >{}
2022     );
2023   }
2024 
2025   void copy_elements_array_from(
2026     const table_core& x,std::true_type /* -> memcpy */)
2027   {
2028     /* reinterpret_cast: GCC may complain about value_type not being trivially
2029      * copy-assignable when we're relying on trivial copy constructibility.
2030      */
2031     std::memcpy(
2032       reinterpret_cast<unsigned char*>(arrays.elements()),
2033       reinterpret_cast<unsigned char*>(x.arrays.elements()),
2034       x.capacity()*sizeof(value_type));
2035   }
2036 
2037   void copy_elements_array_from(
2038     const table_core& x,std::false_type /* -> manual */)
2039   {
2040     std::size_t num_constructed=0;
2041     BOOST_TRY{
2042       x.for_all_elements([&,this](const element_type* p){
2043         construct_element(arrays.elements()+(p-x.arrays.elements()),*p);
2044         ++num_constructed;
2045       });
2046     }
2047     BOOST_CATCH(...){
2048       if(num_constructed){
2049         x.for_all_elements_while([&,this](const element_type* p){
2050           destroy_element(arrays.elements()+(p-x.arrays.elements()));
2051           return --num_constructed!=0;
2052         });
2053       }
2054       BOOST_RETHROW
2055     }
2056     BOOST_CATCH_END
2057   }
2058 
2059   void copy_groups_array_from(const table_core& x) {
2060     copy_groups_array_from(x,is_trivially_copy_assignable<group_type>{});
2061   }
2062 
2063   void copy_groups_array_from(
2064     const table_core& x, std::true_type /* -> memcpy */)
2065   {
2066     std::memcpy(
2067       arrays.groups(),x.arrays.groups(),
2068       (arrays.groups_size_mask+1)*sizeof(group_type));
2069   }
2070 
2071   void copy_groups_array_from(
2072     const table_core& x, std::false_type /* -> manual */) 
2073   {
2074     auto pg=arrays.groups();
2075     auto xpg=x.arrays.groups();
2076     for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
2077       pg[i]=xpg[i];
2078     }
2079   }
2080 
2081   void recover_slot(unsigned char* pc)
2082   {
2083     /* If this slot potentially caused overflow, we decrease the maximum load
2084      * so that average probe length won't increase unboundedly in repeated
2085      * insert/erase cycles (drift).
2086      */
2087     size_ctrl.ml-=group_type::maybe_caused_overflow(pc);
2088     group_type::reset(pc);
2089     --size_ctrl.size;
2090   }
2091 
2092   void recover_slot(group_type* pg,std::size_t pos)
2093   {
2094     recover_slot(reinterpret_cast<unsigned char*>(pg)+pos);
2095   }
2096 
2097   static std::size_t capacity_for(std::size_t n)
2098   {
2099     return size_policy::size(size_index_for<group_type,size_policy>(n))*N-1;
2100   }
2101 
2102   BOOST_NOINLINE void unchecked_rehash(std::size_t n)
2103   {
2104     auto new_arrays_=new_arrays(n);
2105     unchecked_rehash(new_arrays_);
2106   }
2107 
2108   BOOST_NOINLINE void unchecked_rehash(arrays_type& new_arrays_)
2109   {
2110     std::size_t num_destroyed=0;
2111     BOOST_TRY{
2112       for_all_elements([&,this](element_type* p){
2113         nosize_transfer_element(p,new_arrays_,num_destroyed);
2114       });
2115     }
2116     BOOST_CATCH(...){
2117       if(num_destroyed){
2118         for_all_elements_while(
2119           [&,this](group_type* pg,unsigned int n,element_type*){
2120             recover_slot(pg,n);
2121             return --num_destroyed!=0;
2122           }
2123         );
2124       }
2125       for_all_elements(new_arrays_,[this](element_type* p){
2126         destroy_element(p);
2127       });
2128       delete_arrays(new_arrays_);
2129       BOOST_RETHROW
2130     }
2131     BOOST_CATCH_END
2132 
2133     /* either all moved and destroyed or all copied */
2134     BOOST_ASSERT(num_destroyed==size()||num_destroyed==0);
2135     if(num_destroyed!=size()){
2136       for_all_elements([this](element_type* p){
2137         destroy_element(p);
2138       });
2139     }
2140     delete_arrays(arrays);
2141     arrays=new_arrays_;
2142     size_ctrl.ml=initial_max_load();
2143   }
2144 
2145   template<typename Value>
2146   void unchecked_insert(Value&& x)
2147   {
2148     auto hash=hash_for(key_from(x));
2149     unchecked_emplace_at(position_for(hash),hash,std::forward<Value>(x));
2150   }
2151 
2152   void nosize_transfer_element(
2153     element_type* p,const arrays_type& arrays_,std::size_t& num_destroyed)
2154   {
2155     nosize_transfer_element(
2156       p,hash_for(key_from(*p)),arrays_,num_destroyed,
2157       std::integral_constant< /* std::move_if_noexcept semantics */
2158         bool,
2159         std::is_nothrow_move_constructible<init_type>::value||
2160         !std::is_same<element_type,value_type>::value||
2161         !std::is_copy_constructible<element_type>::value>{});
2162   }
2163 
2164   void nosize_transfer_element(
2165     element_type* p,std::size_t hash,const arrays_type& arrays_,
2166     std::size_t& num_destroyed,std::true_type /* ->move */)
2167   {
2168     /* Destroy p even if an an exception is thrown in the middle of move
2169      * construction, which could leave the source half-moved.
2170      */
2171     ++num_destroyed;
2172     destroy_element_on_exit d{this,p};
2173     (void)d; /* unused var warning */
2174     nosize_unchecked_emplace_at(
2175       arrays_,position_for(hash,arrays_),hash,type_policy::move(*p));
2176   }
2177 
2178   void nosize_transfer_element(
2179     element_type* p,std::size_t hash,const arrays_type& arrays_,
2180     std::size_t& /*num_destroyed*/,std::false_type /* ->copy */)
2181   {
2182     nosize_unchecked_emplace_at(
2183       arrays_,position_for(hash,arrays_),hash,
2184       const_cast<const element_type&>(*p));
2185   }
2186 
2187   template<typename... Args>
2188   locator nosize_unchecked_emplace_at(
2189     const arrays_type& arrays_,std::size_t pos0,std::size_t hash,
2190     Args&&... args)
2191   {
2192     for(prober pb(pos0);;pb.next(arrays_.groups_size_mask)){
2193       auto pos=pb.get();
2194       auto pg=arrays_.groups()+pos;
2195       auto mask=pg->match_available();
2196       if(BOOST_LIKELY(mask!=0)){
2197         auto n=unchecked_countr_zero(mask);
2198         auto p=arrays_.elements()+pos*N+n;
2199         construct_element(p,std::forward<Args>(args)...);
2200         pg->set(n,hash);
2201         return {pg,n,p};
2202       }
2203       else pg->mark_overflow(hash);
2204     }
2205   }
2206 };
2207 
2208 #if BOOST_WORKAROUND(BOOST_MSVC,<=1900)
2209 #pragma warning(pop) /* C4702 */
2210 #endif
2211 
2212 #if defined(BOOST_MSVC)
2213 #pragma warning(pop) /* C4714 */
2214 #endif
2215 
2216 #include <boost/unordered/detail/foa/restore_wshadow.hpp>
2217 
2218 } /* namespace foa */
2219 } /* namespace detail */
2220 } /* namespace unordered */
2221 } /* namespace boost */
2222 
2223 #undef BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED
2224 #undef BOOST_UNORDERED_HAS_FEATURE
2225 #undef BOOST_UNORDERED_HAS_BUILTIN
2226 #endif