diff --git a/.appveyor.yml b/.appveyor.yml index 7c4042a2..5dfa6c5d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -42,7 +42,12 @@ environment: - FLAVOR: Visual Studio 2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - B2_CXXSTD: 14,17,latest + B2_CXXSTD: 14,17 + B2_TOOLSET: msvc-14.1 + + - FLAVOR: Visual Studio 2017 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + B2_CXXSTD: latest B2_TOOLSET: msvc-14.1 - FLAVOR: cygwin (32-bit) @@ -63,7 +68,7 @@ environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 ADDPATH: C:\cygwin64\bin; B2_ADDRESS_MODEL: 64 - B2_CXXSTD: 03,11 + B2_CXXSTD: 03 B2_TOOLSET: gcc B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception" @@ -71,14 +76,37 @@ environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 ADDPATH: C:\cygwin64\bin; B2_ADDRESS_MODEL: 64 - B2_CXXSTD: 14,1z + B2_CXXSTD: 11 + B2_TOOLSET: gcc + B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception" + + - FLAVOR: cygwin (64-bit, latest) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 + ADDPATH: C:\cygwin64\bin; + B2_ADDRESS_MODEL: 64 + B2_CXXSTD: 14 + B2_TOOLSET: gcc + B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception" + + - FLAVOR: cygwin (64-bit, latest) + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 + ADDPATH: C:\cygwin64\bin; + B2_ADDRESS_MODEL: 64 + B2_CXXSTD: 1z B2_TOOLSET: gcc B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception" - FLAVOR: mingw-w64, 32 bit APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin; - B2_CXXSTD: 03,11,14,17,2a + B2_CXXSTD: 03,11,14 + B2_TOOLSET: gcc + B2_ADDRESS_MODEL: 32 + + - FLAVOR: mingw-w64, 32 bit + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin; + B2_CXXSTD: 17,2a B2_TOOLSET: gcc B2_ADDRESS_MODEL: 32 diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp new file mode 100644 index 00000000..ec74aa5b --- /dev/null +++ b/include/boost/unordered/detail/foa.hpp @@ -0,0 +1,1843 @@ +/* Fast open-addressing hash table. + * + * Copyright 2022 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_FOA_HPP +#define BOOST_UNORDERED_DETAIL_FOA_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__SSE2__)||\ + defined(_M_X64)||(defined(_M_IX86_FP)&&_M_IX86_FP>=2) +#define BOOST_UNORDERED_SSE2 +#include +#elif defined(__ARM_NEON)&&!defined(__ARM_BIG_ENDIAN) +#define BOOST_UNORDERED_LITTLE_ENDIAN_NEON +#include +#endif + +#ifdef __has_builtin +#define BOOST_UNORDERED_HAS_BUILTIN(x) __has_builtin(x) +#else +#define BOOST_UNORDERED_HAS_BUILTIN(x) 0 +#endif + +#if !defined(NDEBUG) +#define BOOST_UNORDERED_ASSUME(cond) BOOST_ASSERT(cond) +#elif BOOST_UNORDERED_HAS_BUILTIN(__builtin_assume) +#define BOOST_UNORDERED_ASSUME(cond) __builtin_assume(cond) +#elif defined(__GNUC__) || BOOST_UNORDERED_HAS_BUILTIN(__builtin_unreachable) +#define BOOST_UNORDERED_ASSUME(cond) \ + do{ \ + if(!(cond))__builtin_unreachable(); \ + }while(0) +#elif defined(_MSC_VER) +#define BOOST_UNORDERED_ASSUME(cond) __assume(cond) +#else +#define BOOST_UNORDERED_ASSUME(cond) \ + do{ \ + static_cast(false&&(cond)); \ + }while(0) +#endif + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +/* foa::table is an open-addressing hash table serving as the foundational core + * of boost::unordered_flat_[map|set]. Its main internal design aspects are: + * + * - Element slots are logically split into groups of size N=15. The number + * of groups is always a power of two, so the number of allocated slots + is of the form (N*2^n)-1 (final slot reserved for a sentinel mark). + * - Positioning is done at the group level rather than the slot level, that + * is, for any given element its hash value is used to locate a group and + * insertion is performed on the first available element of that group; + * if the group is full (overflow), further groups are tried using + * quadratic probing. + * - Each group has an associated 16B metadata word holding reduced hash + * values and overflow information. Reduced hash values are used to + * accelerate lookup within the group by using 128-bit SIMD or 64-bit word + * operations. + */ + +/* group15 controls metadata information of a group of N=15 element slots. + * The 16B metadata word is organized as follows (LSB depicted rightmost): + * + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * |ofw|h14|h13|h13|h11|h10|h09|h08|h07|h06|h05|h04|h03|h02|h01|h00| + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * hi is 0 if the i-th element slot is avalaible, 1 to mark a sentinel and, + * when the slot is occupied, a value in the range [2,255] obtained from the + * element's original hash value. + * ofw is the so-called overflow byte. If insertion of an element with hash + * value h is tried on a full group, then the (h%8)-th bit of the overflow + * byte is set to 1 and a further group is probed. Having an overflow byte + * brings two advantages: + * + * - There's no need to reserve a special value of hi to mark tombstone + * slots; each reduced hash value keeps then log2(254)=7.99 bits of the + * original hash (alternative approaches reserve one full bit to mark + * if the slot is available/deleted, so their reduced hash values are 7 bit + * strong only). + * - When doing an unsuccessful lookup (i.e. the element is not present in + * the table), probing stops at the first non-overflowed group. Having 8 + * bits for signalling overflow makes it very likely that we stop at the + * current group (this happens when no element with the same (h%8) value + * has overflowed in the group), saving us an additional group check even + * under high-load/high-erase conditions. + * + * When looking for an element with hash value h, match(h) returns a bitmask + * signalling which slots have the same reduced hash value. If available, + * match uses SSE2 or (little endian) Neon 128-bit SIMD operations. On non-SIMD + * scenarios, the logical layout described above is physically mapped to two + * 64-bit words with *bit interleaving*, i.e. the least significant 16 bits of + * the first 64-bit word contain the least significant bits of each byte in the + * "logical" 128-bit word, and so forth. With this layout, match can be + * implemented with 4 ANDs, 3 shifts, 2 XORs, 1 OR and 1 NOT. + * + * group15 has no user-defined ctor so that it's a trivial type and can be + * initialized via memset etc. Where needed, group15::initialize sets the + * metadata to all zeros. + */ + +#if defined(BOOST_UNORDERED_SSE2) + +struct group15 +{ + static constexpr int N=15; + + struct dummy_group_type + { + alignas(16) unsigned char storage[N+1]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}; + }; + + inline void initialize(){m=_mm_setzero_si128();} + + inline void set(std::size_t pos,std::size_t hash) + { + BOOST_ASSERT(pos(1<<(hash%8)); + } + + inline int match_available()const + { + return _mm_movemask_epi8( + _mm_cmpeq_epi8(m,_mm_setzero_si128()))&0x7FFF; + } + + inline int match_occupied()const + { + return (~match_available())&0x7FFF; + } + + inline int match_really_occupied()const /* excluding sentinel */ + { + return at(N-1)==sentinel_?match_occupied()&0x3FFF:match_occupied(); + } + +private: + static constexpr unsigned char available_=0, + sentinel_=1; + + inline static int match_word(std::size_t hash) + { + static constexpr boost::uint32_t word[]= + { + 0x02020202u,0x03030303u,0x02020202u,0x03030303u,0x04040404u,0x05050505u,0x06060606u,0x07070707u, + 0x08080808u,0x09090909u,0x0A0A0A0Au,0x0B0B0B0Bu,0x0C0C0C0Cu,0x0D0D0D0Du,0x0E0E0E0Eu,0x0F0F0F0Fu, + 0x10101010u,0x11111111u,0x12121212u,0x13131313u,0x14141414u,0x15151515u,0x16161616u,0x17171717u, + 0x18181818u,0x19191919u,0x1A1A1A1Au,0x1B1B1B1Bu,0x1C1C1C1Cu,0x1D1D1D1Du,0x1E1E1E1Eu,0x1F1F1F1Fu, + 0x20202020u,0x21212121u,0x22222222u,0x23232323u,0x24242424u,0x25252525u,0x26262626u,0x27272727u, + 0x28282828u,0x29292929u,0x2A2A2A2Au,0x2B2B2B2Bu,0x2C2C2C2Cu,0x2D2D2D2Du,0x2E2E2E2Eu,0x2F2F2F2Fu, + 0x30303030u,0x31313131u,0x32323232u,0x33333333u,0x34343434u,0x35353535u,0x36363636u,0x37373737u, + 0x38383838u,0x39393939u,0x3A3A3A3Au,0x3B3B3B3Bu,0x3C3C3C3Cu,0x3D3D3D3Du,0x3E3E3E3Eu,0x3F3F3F3Fu, + 0x40404040u,0x41414141u,0x42424242u,0x43434343u,0x44444444u,0x45454545u,0x46464646u,0x47474747u, + 0x48484848u,0x49494949u,0x4A4A4A4Au,0x4B4B4B4Bu,0x4C4C4C4Cu,0x4D4D4D4Du,0x4E4E4E4Eu,0x4F4F4F4Fu, + 0x50505050u,0x51515151u,0x52525252u,0x53535353u,0x54545454u,0x55555555u,0x56565656u,0x57575757u, + 0x58585858u,0x59595959u,0x5A5A5A5Au,0x5B5B5B5Bu,0x5C5C5C5Cu,0x5D5D5D5Du,0x5E5E5E5Eu,0x5F5F5F5Fu, + 0x60606060u,0x61616161u,0x62626262u,0x63636363u,0x64646464u,0x65656565u,0x66666666u,0x67676767u, + 0x68686868u,0x69696969u,0x6A6A6A6Au,0x6B6B6B6Bu,0x6C6C6C6Cu,0x6D6D6D6Du,0x6E6E6E6Eu,0x6F6F6F6Fu, + 0x70707070u,0x71717171u,0x72727272u,0x73737373u,0x74747474u,0x75757575u,0x76767676u,0x77777777u, + 0x78787878u,0x79797979u,0x7A7A7A7Au,0x7B7B7B7Bu,0x7C7C7C7Cu,0x7D7D7D7Du,0x7E7E7E7Eu,0x7F7F7F7Fu, + 0x80808080u,0x81818181u,0x82828282u,0x83838383u,0x84848484u,0x85858585u,0x86868686u,0x87878787u, + 0x88888888u,0x89898989u,0x8A8A8A8Au,0x8B8B8B8Bu,0x8C8C8C8Cu,0x8D8D8D8Du,0x8E8E8E8Eu,0x8F8F8F8Fu, + 0x90909090u,0x91919191u,0x92929292u,0x93939393u,0x94949494u,0x95959595u,0x96969696u,0x97979797u, + 0x98989898u,0x99999999u,0x9A9A9A9Au,0x9B9B9B9Bu,0x9C9C9C9Cu,0x9D9D9D9Du,0x9E9E9E9Eu,0x9F9F9F9Fu, + 0xA0A0A0A0u,0xA1A1A1A1u,0xA2A2A2A2u,0xA3A3A3A3u,0xA4A4A4A4u,0xA5A5A5A5u,0xA6A6A6A6u,0xA7A7A7A7u, + 0xA8A8A8A8u,0xA9A9A9A9u,0xAAAAAAAAu,0xABABABABu,0xACACACACu,0xADADADADu,0xAEAEAEAEu,0xAFAFAFAFu, + 0xB0B0B0B0u,0xB1B1B1B1u,0xB2B2B2B2u,0xB3B3B3B3u,0xB4B4B4B4u,0xB5B5B5B5u,0xB6B6B6B6u,0xB7B7B7B7u, + 0xB8B8B8B8u,0xB9B9B9B9u,0xBABABABAu,0xBBBBBBBBu,0xBCBCBCBCu,0xBDBDBDBDu,0xBEBEBEBEu,0xBFBFBFBFu, + 0xC0C0C0C0u,0xC1C1C1C1u,0xC2C2C2C2u,0xC3C3C3C3u,0xC4C4C4C4u,0xC5C5C5C5u,0xC6C6C6C6u,0xC7C7C7C7u, + 0xC8C8C8C8u,0xC9C9C9C9u,0xCACACACAu,0xCBCBCBCBu,0xCCCCCCCCu,0xCDCDCDCDu,0xCECECECEu,0xCFCFCFCFu, + 0xD0D0D0D0u,0xD1D1D1D1u,0xD2D2D2D2u,0xD3D3D3D3u,0xD4D4D4D4u,0xD5D5D5D5u,0xD6D6D6D6u,0xD7D7D7D7u, + 0xD8D8D8D8u,0xD9D9D9D9u,0xDADADADAu,0xDBDBDBDBu,0xDCDCDCDCu,0xDDDDDDDDu,0xDEDEDEDEu,0xDFDFDFDFu, + 0xE0E0E0E0u,0xE1E1E1E1u,0xE2E2E2E2u,0xE3E3E3E3u,0xE4E4E4E4u,0xE5E5E5E5u,0xE6E6E6E6u,0xE7E7E7E7u, + 0xE8E8E8E8u,0xE9E9E9E9u,0xEAEAEAEAu,0xEBEBEBEBu,0xECECECECu,0xEDEDEDEDu,0xEEEEEEEEu,0xEFEFEFEFu, + 0xF0F0F0F0u,0xF1F1F1F1u,0xF2F2F2F2u,0xF3F3F3F3u,0xF4F4F4F4u,0xF5F5F5F5u,0xF6F6F6F6u,0xF7F7F7F7u, + 0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu, + }; + + return (int)word[(unsigned char)hash]; + } + + inline static unsigned char reduced_hash(std::size_t hash) + { + return (unsigned char)match_word(hash); + } + + inline unsigned char& at(std::size_t pos) + { + return reinterpret_cast(&m)[pos]; + } + + inline unsigned char at(std::size_t pos)const + { + return reinterpret_cast(&m)[pos]; + } + + inline unsigned char& overflow() + { + return at(N); + } + + inline unsigned char overflow()const + { + return at(N); + } + + alignas(16) __m128i m; +}; + +#elif defined(BOOST_UNORDERED_LITTLE_ENDIAN_NEON) + +struct group15 +{ + static constexpr int N=15; + + struct dummy_group_type + { + alignas(16) unsigned char storage[N+1]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}; + }; + + inline void initialize(){m=vdupq_n_s8(0);} + + inline void set(std::size_t pos,std::size_t hash) + { + BOOST_ASSERT(pos(1<<(hash%8)); + } + + inline int match_available()const + { + return simde_mm_movemask_epi8(vceqq_s8(m,vdupq_n_s8(0)))&0x7FFF; + } + + inline int match_occupied()const + { + return simde_mm_movemask_epi8( + vcgtq_u8(vreinterpretq_u8_s8(m),vdupq_n_u8(0)))&0x7FFF; + } + + inline int match_really_occupied()const /* excluding sentinel */ + { + return at(N-1)==sentinel_?match_occupied()&0x3FFF:match_occupied(); + } + +private: + static constexpr unsigned char available_=0, + sentinel_=1; + + inline static unsigned char reduced_hash(std::size_t hash) + { + static constexpr unsigned char table[]={ + 2,3,2,3,4,5,6,7,8,9,10,11,12,13,14,15, + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, + }; + + return table[(unsigned char)hash]; + } + + /* Copied from + * https://github.com/simd-everywhere/simde/blob/master/simde/x86/sse2.h#L3763 + */ + + static inline int simde_mm_movemask_epi8(uint8x16_t a) + { + static constexpr uint8_t md[16]={ + 1 << 0, 1 << 1, 1 << 2, 1 << 3, + 1 << 4, 1 << 5, 1 << 6, 1 << 7, + 1 << 0, 1 << 1, 1 << 2, 1 << 3, + 1 << 4, 1 << 5, 1 << 6, 1 << 7, + }; + + uint8x16_t masked=vandq_u8(vld1q_u8(md),a); + uint8x8x2_t tmp=vzip_u8(vget_low_u8(masked),vget_high_u8(masked)); + uint16x8_t x=vreinterpretq_u16_u8(vcombine_u8(tmp.val[0],tmp.val[1])); + +#if defined(__ARM_ARCH_ISA_A64) + return vaddvq_u16(x); +#else + uint64x2_t t64=vpaddlq_u32(vpaddlq_u16(x)); + return int(vgetq_lane_u64(t64,0))+int(vgetq_lane_u64(t64,1)); +#endif + } + + inline unsigned char& at(std::size_t pos) + { + return reinterpret_cast(&m)[pos]; + } + + inline unsigned char at(std::size_t pos)const + { + return reinterpret_cast(&m)[pos]; + } + + inline unsigned char& overflow() + { + return at(N); + } + + inline unsigned char overflow()const + { + return at(N); + } + + alignas(16) int8x16_t m; +}; + +#else /* non-SIMD */ + +struct group15 +{ + static constexpr int N=15; + + struct dummy_group_type + { + alignas(16) boost::uint64_t m[2]= + {0x0000000000004000ull,0x0000000000000000ull}; + }; + + inline void initialize(){m[0]=0;m[1]=0;} + + inline void set(std::size_t pos,std::size_t hash) + { + BOOST_ASSERT(pos(pc)%sizeof(group15); + pc-=pos; + reinterpret_cast(pc)->reset(pos); + } + + inline int match(std::size_t hash)const + { + return match_impl(reduced_hash(hash)); + } + + inline bool is_not_overflowed(std::size_t hash)const + { + return !(reinterpret_cast(m)[hash%8] & 0x8000u); + } + + inline void mark_overflow(std::size_t hash) + { + reinterpret_cast(m)[hash%8]|=0x8000u; + } + + inline int match_available()const + { + boost::uint64_t x=~(m[0]|m[1]); + boost::uint32_t y=static_cast(x&(x>>32)); + y&=y>>16; + return y&0x7FFF; + } + + inline int match_occupied()const + { + boost::uint64_t x=m[0]|m[1]; + boost::uint32_t y=static_cast(x|(x>>32)); + y|=y>>16; + return y&0x7FFF; + } + + inline int match_really_occupied()const /* excluding sentinel */ + { + return ~(match_impl(0)|match_impl(1))&0x7FFF; + } + +private: + static constexpr unsigned char available_=0, + sentinel_=1; + + inline static unsigned char reduced_hash(std::size_t hash) + { + static constexpr unsigned char table[]={ + 2,3,2,3,4,5,6,7,8,9,10,11,12,13,14,15, + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, + }; + + return table[(unsigned char)hash]; + } + + inline void set_impl(std::size_t pos,std::size_t n) + { + BOOST_ASSERT(n<256); + set_impl(m[0],pos,n&0xFu); + set_impl(m[1],pos,n>>4); + } + + static inline void set_impl(boost::uint64_t& x,std::size_t pos,std::size_t n) + { + static constexpr boost::uint64_t mask[]= + { + 0x0000000000000000ull,0x0000000000000001ull,0x0000000000010000ull, + 0x0000000000010001ull,0x0000000100000000ull,0x0000000100000001ull, + 0x0000000100010000ull,0x0000000100010001ull,0x0001000000000000ull, + 0x0001000000000001ull,0x0001000000010000ull,0x0001000000010001ull, + 0x0001000100000000ull,0x0001000100000001ull,0x0001000100010000ull, + 0x0001000100010001ull, + }; + static constexpr boost::uint64_t imask[]= + { + 0x0001000100010001ull,0x0001000100010000ull,0x0001000100000001ull, + 0x0001000100000000ull,0x0001000000010001ull,0x0001000000010000ull, + 0x0001000000000001ull,0x0001000000000000ull,0x0000000100010001ull, + 0x0000000100010000ull,0x0000000100000001ull,0x0000000100000000ull, + 0x0000000000010001ull,0x0000000000010000ull,0x0000000000000001ull, + 0x0000000000000000ull, + }; + + BOOST_ASSERT(pos<16&&n<16); + x|= mask[n]<>4])|x); + boost::uint32_t y=static_cast(x&(x>>32)); + y&=y>>16; + return y&0x7FFF; + } + + alignas(16) boost::uint64_t m[2]; +}; + +#endif + +/* foa::table uses a size policy to obtain the permissible sizes of the group + * array (and, by implication, the element array) and to do the hash->group + * mapping. + * + * - size_index(n) returns an unspecified "index" number used in other policy + * operations. + * - size(size_index_) returns the number of groups for the given index. It is + * guaranteed that size(size_index(n)) >= n. + * - min_size() is the minimum number of groups permissible, i.e. + * size(size_index(0)). + * - position(hash,size_index_) maps hash to a position in the range + * [0,size(size_index_)). + * + * The reason we're introducing the intermediate index value for calculating + * sizes and positions is that it allows us to optimize the implementation of + * position, which is in the hot path of lookup and insertion operations: + * pow2_size_policy, the actual size policy used by foa::table, returns 2^n + * (n>0) as permissible sizes and returns the n most significant bits + * of the hash value as the position in the group array; using a size index + * defined as i = (bits in std::size_t) - n, we have an unbeatable + * implementation of position(hash) as hash>>i. + * There's a twofold reason for choosing the high bits of hash for positioning: + * - Multiplication-based mixing tends to yield better entropy in the high + * part of its result. + * - group15 reduced-hash values take the *low* bits of hash, and we want + * these values and positioning to be as uncorrelated as possible. + */ + +struct pow2_size_policy +{ + static inline std::size_t size_index(std::size_t n) + { + // TODO: min size is 2, see if we can bring it down to 1 without loss + // of performance + + return sizeof(std::size_t)*CHAR_BIT- + (n<=2?1:((std::size_t)(boost::core::bit_width(n-1)))); + } + + static inline std::size_t size(std::size_t size_index_) + { + return std::size_t(1)<<(sizeof(std::size_t)*CHAR_BIT-size_index_); + } + + static constexpr std::size_t min_size(){return 2;} + + static inline std::size_t position(std::size_t hash,std::size_t size_index_) + { + return hash>>size_index_; + } +}; + +/* size index of a group array for a given *element* capacity */ + +template +static inline std::size_t size_index_for(std::size_t n) +{ + /* n/N+1 == ceil((n+1)/N) (extra +1 for the sentinel) */ + return SizePolicy::size_index(n/Group::N+1); +} + +/* Quadratic prober over a power-of-two range using triangular numbers. + * mask in next(mask) must be the range size minus one (and since size is 2^n, + * mask has exactly its n first bits set to 1). + */ + +struct pow2_quadratic_prober +{ + pow2_quadratic_prober(std::size_t pos_):pos{pos_}{} + + inline std::size_t get()const{return pos;} + + /* next returns false when the whole array has been traversed, which ends + * probing (in practice, full-table probing will only happen with very small + * arrays). + */ + + inline bool next(std::size_t mask) + { + step+=1; + pos=(pos+step)&mask; + return step<=mask; + } + +private: + std::size_t pos,step=0; +}; + +/* Mixing policies: no_mix is the identity function and xmx_mix uses the + * xmx function defined in . + * foa::table mixes hash results with xmx_mix unless the hash is marked as + * avalanching, i.e. of good quality (see ). + */ + +struct no_mix +{ + template + static inline std::size_t mix(const Hash& h,const T& x) + { + return h(x); + } +}; + +struct xmx_mix +{ + template + static inline std::size_t mix(const Hash& h,const T& x) + { + return xmx(h(x)); + } +}; + +/* boost::core::countr_zero has a potentially costly check for + * the case x==0. + */ + +inline unsigned int unchecked_countr_zero(int x) +{ +#if defined(BOOST_MSVC) + unsigned long r; + _BitScanForward(&r,(unsigned long)x); + return (unsigned int)r; +#else + BOOST_UNORDERED_ASSUME(x!=0); + return (unsigned int)boost::core::countr_zero((unsigned int)x); +#endif +} + +template +class table; + +/* table_iterator keeps two pointers: + * + * - A pointer p to the element slot. + * - A pointer pc to the n-th byte of the associated group metadata, where n + * is the position of the element in the group. + * + * A simpler solution would have been to keep a pointer p to the element, a + * pointer pg to the group, and the position n, but that would increase + * sizeof(table_iterator) by 4/8 bytes. In order to make this compact + * representation feasible, it is required that group objects are aligned + * to their size, so that we can recover pg and n as + * + * - n = pc%sizeof(group) + * - pg = pc-n + * + * (for explanatory purposes pg and pc are treated above as if they were memory + * addresses rather than pointers).The main drawback of this two-pointer + * representation is that iterator increment is relatively slow. + * + * p = nullptr is conventionally used to mark end() iterators. + */ + +/* internal conversion from const_iterator to iterator */ +class const_iterator_cast_tag {}; + +template +class table_iterator +{ +public: + using difference_type=std::ptrdiff_t; + using value_type=Value; + using pointer= + typename std::conditional::type; + using reference= + typename std::conditional::type; + using iterator_category=std::forward_iterator_tag; + using element_type= + typename std::conditional::type; + + table_iterator()=default; + template::type* =nullptr> + table_iterator(const table_iterator& x): + pc{x.pc},p{x.p}{} + table_iterator( + const_iterator_cast_tag, const table_iterator& x): + pc{x.pc},p{x.p}{} + + inline reference operator*()const noexcept{return *p;} + inline pointer operator->()const noexcept{return p;} + inline table_iterator& operator++()noexcept{increment();return *this;} + inline table_iterator operator++(int)noexcept + {auto x=*this;increment();return x;} + friend inline bool operator==( + const table_iterator& x,const table_iterator& y) + {return x.p==y.p;} + friend inline bool operator!=( + const table_iterator& x,const table_iterator& y) + {return !(x==y);} + +private: + template friend class table_iterator; + template friend class table; + + table_iterator(Group* pg,std::size_t n,const Value* p_): + pc{reinterpret_cast(const_cast(pg))+n}, + p{const_cast(p_)} + {} + + inline std::size_t rebase() noexcept + { + std::size_t off=reinterpret_cast(pc)%sizeof(Group); + pc-=off; + return off; + } + + inline void increment()noexcept + { + std::size_t n0=rebase(); + + int mask=(reinterpret_cast(pc)->match_occupied()>>(n0+1))<<(n0+1); + if(!mask){ + do{ + pc+=sizeof(Group); + p+=Group::N; + } + while((mask=reinterpret_cast(pc)->match_occupied())==0); + } + + auto n=unchecked_countr_zero(mask); + if(BOOST_UNLIKELY(reinterpret_cast(pc)->is_sentinel(n))){ + p=nullptr; + } + else{ + pc+=n; + p-=n0; + p+=n; + } + } + + unsigned char *pc=nullptr; + Value *p=nullptr; +}; + +/* table_arrays controls allocation, initialization and deallocation of + * paired arrays of groups and element slots. Only one chunk of memory is + * allocated to place both arrays: this is not done for efficiency reasons, + * but in order to be able to properly align the group array without storing + * additional offset information --the alignment required (16B) is usually + * greater than alignof(std::max_align_t) and thus not guaranteed by + * allocators. + */ + +template +Group* dummy_groups() +{ + /* Dummy storage initialized as if in an empty container (actually, each + * of its groups is initialized like a separate empty container). + * We make table_arrays::groups point to this when capacity()==0, so that + * we are not allocating any dynamic memory and yet lookup can be implemented + * without checking for groups==nullptr. This space won't ever be used for + * insertion as the container's capacity is precisely zero. + */ + + static constexpr typename Group::dummy_group_type + storage[Size]={typename Group::dummy_group_type(),}; + + return reinterpret_cast( + const_cast(storage)); +} + +template +struct table_arrays +{ + using value_type=Value; + using group_type=Group; + static constexpr auto N=group_type::N; + using size_policy=SizePolicy; + + template + static table_arrays new_(Allocator& al,std::size_t n) + { + using alloc_traits=boost::allocator_traits; + + auto groups_size_index=size_index_for(n); + auto groups_size=size_policy::size(groups_size_index); + table_arrays arrays{groups_size_index,groups_size-1,nullptr,nullptr}; + + if(!n){ + arrays.groups=dummy_groups(); + } + else{ + arrays.elements= + boost::to_address(alloc_traits::allocate(al,buffer_size(groups_size))); + + /* Align arrays.groups to sizeof(group_type). table_iterator critically + * depends on such alignment for its increment operation. + */ + + auto p=reinterpret_cast(arrays.elements+groups_size*N-1); + p+=(uintptr_t(sizeof(group_type))- + reinterpret_cast(p))%sizeof(group_type); + arrays.groups=reinterpret_cast(p); + + /* memset is faster/not slower than initializing groups individually. + * This assumes all zeros is group_type's default layout. + */ + + std::memset(arrays.groups,0,sizeof(group_type)*groups_size); + arrays.groups[groups_size-1].set_sentinel(); + } + return arrays; + } + + template + static void delete_(Allocator& al,table_arrays& arrays)noexcept + { + using alloc_traits=boost::allocator_traits; + using pointer=typename alloc_traits::pointer; + using pointer_traits=boost::pointer_traits; + + if(arrays.elements){ + alloc_traits::deallocate( + al,pointer_traits::pointer_to(*arrays.elements), + buffer_size(arrays.groups_size_mask+1)); + } + } + + /* combined space for elements and groups measured in sizeof(value_type)s */ + + static std::size_t buffer_size(std::size_t groups_size) + { + auto buffer_bytes= + /* space for elements (we subtract 1 because of the sentinel) */ + sizeof(value_type)*(groups_size*N-1)+ + /* space for groups + padding for group alignment */ + sizeof(group_type)*(groups_size+1)-1; + + /* ceil(buffer_bytes/sizeof(value_type)) */ + return (buffer_bytes+sizeof(value_type)-1)/sizeof(value_type); + } + + std::size_t groups_size_index; + std::size_t groups_size_mask; + group_type *groups; + value_type *elements; +}; + +struct if_constexpr_void_else{void operator()()const{}}; + +template +void if_constexpr(F f,G g={}) +{ + std::get(std::forward_as_tuple(f,g))(); +} + +template::type* =nullptr> +void copy_assign_if(T& x,const T& y){x=y;} + +template::type* =nullptr> +void copy_assign_if(T&,const T&){} + +template::type* =nullptr> +void move_assign_if(T& x,T& y){x=std::move(y);} + +template::type* =nullptr> +void move_assign_if(T&,T&){} + +template::type* =nullptr> +void swap_if(T& x,T& y){using std::swap; swap(x,y);} + +template::type* =nullptr> +void swap_if(T&,T&){} + +inline void prefetch(const void* p) +{ +#if defined(BOOST_GCC)||defined(BOOST_CLANG) + __builtin_prefetch((const char*)p); +#elif defined(BOOST_UNORDERED_SSE2) + _mm_prefetch((const char*)p,_MM_HINT_T0); +#endif +} + +#if defined(BOOST_GCC) +/* GCC's -Wshadow triggers at scenarios like this: + * + * struct foo{}; + * template + * struct derived:Base + * { + * void f(){int foo;} + * }; + * + * derivedx; + * x.f(); // declaration of "foo" in derived::f shadows base type "foo" + * + * This makes shadowing warnings unavoidable in general when a class template + * derives from user-provided classes, as is the case with table and + * empty_value's below. + */ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#endif + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4714) /* marked as __forceinline not inlined */ +#endif + +#if BOOST_WORKAROUND(BOOST_MSVC,<=1900) +/* VS2015 marks as unreachable generic catch clauses around non-throwing + * code. + */ +#pragma warning(push) +#pragma warning(disable:4702) +#endif + +/* foa::table interface departs in a number of ways from that of C++ unordered + * associative containers because it's not for end-user consumption + * (boost::unordered_flat_[map|set] wrappers complete it as appropriate) and, + * more importantly, because of fundamental restrictions imposed by open + * addressing: + * + * - value_type must be moveable. + * - Pointer stability is not kept under rehashing. + * - begin() is not O(1). + * - No bucket API. + * - Load factor is fixed and can't be set by the user. + * - No extract API. + * + * The TypePolicy template parameter is used to generate instantiations + * suitable for either maps or sets, and introduces non-standard init_type: + * + * - TypePolicy::key_type and TypePolicy::value_type have the obvious + * meaning. + * - TypePolicy::init_type is the type implicitly converted to when + * writing x.insert({...}). For maps, this is std::pair rather + * than std::pair so that, for instance, x.insert({"hello",0}) + * produces a cheaply moveable std::string&& ("hello") rather than + * a copyable const std::string&&. foa::table::insert is extended to accept + * both init_type and value_type references. + * - TypePolicy::move(value_type&) returns a temporary object for value + * transfer on rehashing, move copy/assignment, and merge. For maps, this + * object is a std::pair, which is generally cheaper to move + * than std::pair&& because of the constness in Key. + * - TypePolicy::extract returns a const reference to the key part of + * a value of type value_type, init_type or + * decltype(TypePolicy::move(...)). + * + * try_emplace, erase and find support heterogenous lookup by default, that is, + * without checking for any ::is_transparent typedefs --the checking is done by + * boost::unordered_flat_[map|set]. + * + * At the moment, we're not supporting allocators with fancy pointers. + * Allocator::pointer must be convertible to/from regular pointers. + */ + +/* We pull this out so the tests don't have to rely on a magic constant or + * instantiate the table class template as it can be quite gory. + */ +constexpr static float const mlf = 0.875f; + +template +class + +#if defined(_MSC_VER)&&_MSC_FULL_VER>=190023918 +__declspec(empty_bases) /* activate EBO with multiple inheritance */ +#endif + +table:empty_value,empty_value,empty_value +{ + using hash_base=empty_value; + using pred_base=empty_value; + using allocator_base=empty_value; + using type_policy=TypePolicy; + using group_type=group15; + static constexpr auto N=group_type::N; + using size_policy=pow2_size_policy; + using prober=pow2_quadratic_prober; + using mix_policy=typename std::conditional< + hash_is_avalanching::value, + no_mix, + xmx_mix + >::type; + using alloc_traits=boost::allocator_traits; + +public: + using key_type=typename type_policy::key_type; + using init_type=typename type_policy::init_type; + using value_type=typename type_policy::value_type; + +private: + static constexpr bool has_mutable_iterator= + !std::is_same::value; + +public: + using hasher=Hash; + using key_equal=Pred; + using allocator_type=Allocator; + using pointer=value_type*; + using const_pointer=const value_type*; + using reference=value_type&; + using const_reference=const value_type&; + using size_type=std::size_t; + using difference_type=std::ptrdiff_t; + using const_iterator=table_iterator; + using iterator=typename std::conditional< + has_mutable_iterator, + table_iterator, + const_iterator>::type; + + table( + std::size_t n=0,const Hash& h_=Hash(),const Pred& pred_=Pred(), + const Allocator& al_=Allocator()): + hash_base{empty_init,h_},pred_base{empty_init,pred_}, + allocator_base{empty_init,al_},size_{0},arrays(new_arrays(n)), + ml{max_load()} + {} + + table(const table& x): + table{x,alloc_traits::select_on_container_copy_construction(x.al())}{} + + table(table&& x) + noexcept( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value): + hash_base{empty_init,std::move(x.h())}, + pred_base{empty_init,std::move(x.pred())}, + allocator_base{empty_init,std::move(x.al())}, + size_{x.size_},arrays(x.arrays),ml{x.ml} + { + x.size_=0; + x.arrays=x.new_arrays(0); + x.ml=x.max_load(); + } + + table(const table& x,const Allocator& al_): + table{std::size_t(std::ceil(float(x.size())/mlf)),x.h(),x.pred(),al_} + { + x.for_all_elements([this](value_type* p){ + unchecked_insert(*p); + }); + } + + table(table&& x,const Allocator& al_): + table{0,std::move(x.h()),std::move(x.pred()),al_} + { + if(al()==x.al()){ + std::swap(size_,x.size_); + std::swap(arrays,x.arrays); + std::swap(ml,x.ml); + } + else{ + reserve(x.size()); + clear_on_exit c{x}; + (void)c; /* unused var warning */ + + /* This works because subsequent x.clear() does not depend on the + * elements' values. + */ + x.for_all_elements([this](value_type* p){ + unchecked_insert(type_policy::move(*p)); + }); + } + } + + ~table()noexcept + { + for_all_elements([this](value_type* p){ + destroy_element(p); + }); + delete_arrays(arrays); + } + + table& operator=(const table& x) + { + static constexpr auto pocca= + alloc_traits::propagate_on_container_copy_assignment::value; + + if(this!=std::addressof(x)){ + clear(); + h()=x.h(); + pred()=x.pred(); + if_constexpr([&,this]{ + if(al()!=x.al())reserve(0); + copy_assign_if(al(),x.al()); + }); + /* noshrink: favor memory reuse over tightness */ + noshrink_reserve(x.size()); + x.for_all_elements([this](value_type* p){ + unchecked_insert(*p); + }); + } + return *this; + } + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4127) /* conditional expression is constant */ +#endif + + table& operator=(table&& x) + noexcept( + alloc_traits::is_always_equal::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_assignable::value) + { + static constexpr auto pocma= + alloc_traits::propagate_on_container_move_assignment::value; + + if(this!=std::addressof(x)){ + clear(); + h()=std::move(x.h()); + pred()=std::move(x.pred()); + if(pocma||al()==x.al()){ + using std::swap; + reserve(0); + move_assign_if(al(),x.al()); + swap(size_,x.size_); + swap(arrays,x.arrays); + swap(ml,x.ml); + } + else{ + /* noshrink: favor memory reuse over tightness */ + noshrink_reserve(x.size()); + clear_on_exit c{x}; + (void)c; /* unused var warning */ + + /* This works because subsequent x.clear() does not depend on the + * elements' values. + */ + x.for_all_elements([this](value_type* p){ + unchecked_insert(type_policy::move(*p)); + }); + } + } + return *this; + } + +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4127 */ +#endif + + allocator_type get_allocator()const noexcept{return al();} + + iterator begin()noexcept + { + iterator it{arrays.groups,0,arrays.elements}; + if(!(arrays.groups[0].match_occupied()&0x1))++it; + return it; + } + + const_iterator begin()const noexcept + {return const_cast(this)->begin();} + iterator end()noexcept{return {};} + const_iterator end()const noexcept{return const_cast(this)->end();} + const_iterator cbegin()const noexcept{return begin();} + const_iterator cend()const noexcept{return end();} + + bool empty()const noexcept{return size()==0;} + std::size_t size()const noexcept{return size_;} + std::size_t max_size()const noexcept{return SIZE_MAX;} + + template + BOOST_FORCEINLINE std::pair emplace(Args&&... args) + { + using emplace_type = typename std::conditional< + std::is_constructible< + init_type, Args... + >::value, + init_type, + value_type + >::type; + return emplace_impl(emplace_type(std::forward(args)...)); + } + + template + BOOST_FORCEINLINE std::pair try_emplace( + Key&& k,Args&&... args) + { + return emplace_impl( + std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + } + + BOOST_FORCEINLINE std::pair + insert(const init_type& x){return emplace_impl(x);} + + BOOST_FORCEINLINE std::pair + insert(init_type&& x){return emplace_impl(std::move(x));} + + /* template tilts call ambiguities in favor of init_type */ + + template + BOOST_FORCEINLINE std::pair + insert(const value_type& x){return emplace_impl(x);} + + template + BOOST_FORCEINLINE std::pair + insert(value_type&& x){return emplace_impl(std::move(x));} + + template< + bool dependent_value=false, + typename std::enable_if< + has_mutable_iterator||dependent_value>::type* =nullptr + > + void erase(iterator pos)noexcept{return erase(const_iterator(pos));} + + BOOST_FORCEINLINE + void erase(const_iterator pos)noexcept + { + destroy_element(pos.p); + group_type::reset(pos.pc); + --size_; + } + + template + BOOST_FORCEINLINE + auto erase(Key&& x) -> typename std::enable_if< + !std::is_convertible::value&& + !std::is_convertible::value, std::size_t>::type + { + auto it=find(x); + if(it!=end()){ + erase(it); + return 1; + } + else return 0; + } + + void swap(table& x) + noexcept( + alloc_traits::is_always_equal::value&& + boost::is_nothrow_swappable::value&& + boost::is_nothrow_swappable::value) + { + static constexpr auto pocs= + alloc_traits::propagate_on_container_swap::value; + + using std::swap; + swap(h(),x.h()); + swap(pred(),x.pred()); + if_constexpr([&,this]{ + swap_if(al(),x.al()); + }, + [&,this]{ /* else */ + BOOST_ASSERT(al()==x.al()); + (void)this; /* makes sure captured this is used */ + }); + swap(size_,x.size_); + swap(arrays,x.arrays); + swap(ml,x.ml); + } + + void clear()noexcept + { + auto p=arrays.elements; + if(p){ + for(auto pg=arrays.groups,last=pg+arrays.groups_size_mask+1; + pg!=last;++pg,p+=N){ + auto mask=pg->match_really_occupied(); + while(mask){ + destroy_element(p+unchecked_countr_zero(mask)); + mask&=mask-1; + } + /* we wipe the entire metadata to reset the overflow byte as well */ + pg->initialize(); + } + arrays.groups[arrays.groups_size_mask].set_sentinel(); + size_=0; + } + } + + // TODO: should we accept different allocator too? + template + void merge(table& x) + { + x.for_all_elements([&,this](group_type* pg,unsigned int n,value_type* p){ + if(emplace_impl(type_policy::move(*p)).second){ + x.erase(iterator{pg,n,p}); + } + }); + } + + template + void merge(table&& x){merge(x);} + + hasher hash_function()const{return h();} + key_equal key_eq()const{return pred();} + + template + BOOST_FORCEINLINE iterator find(const Key& x) + { + auto hash=hash_for(x); + return find_impl(x,position_for(hash),hash); + } + + template + BOOST_FORCEINLINE const_iterator find(const Key& x)const + { + return const_cast(this)->find(x); + } + + std::size_t capacity()const noexcept + { + return arrays.elements?(arrays.groups_size_mask+1)*N-1:0; + } + + float load_factor()const noexcept + { + if (capacity() == 0) { return 0; } + return float(size())/float(capacity()); + } + + float max_load_factor()const noexcept{return mlf;} + + void rehash(std::size_t n) + { + auto m=size_t(std::ceil(float(size())/mlf)); + if(m>n)n=m; + if(n)n=capacity_for(n); /* exact resulting capacity */ + + if(n!=capacity())unchecked_rehash(n); + } + + void reserve(std::size_t n) + { + rehash(std::size_t(std::ceil(float(n)/mlf))); + } + + template + friend std::size_t erase_if(table& x,Predicate pr) + { + return x.erase_if_impl(pr); + } + +private: + template friend class table; + using arrays_type=table_arrays; + + struct clear_on_exit + { + ~clear_on_exit(){x.clear();} + table& x; + }; + + Hash& h(){return hash_base::get();} + const Hash& h()const{return hash_base::get();} + Pred& pred(){return pred_base::get();} + const Pred& pred()const{return pred_base::get();} + Allocator& al(){return allocator_base::get();} + const Allocator& al()const{return allocator_base::get();} + + arrays_type new_arrays(std::size_t n) + { + return arrays_type::new_(al(),n); + } + + void delete_arrays(arrays_type& arrays_)noexcept + { + arrays_type::delete_(al(),arrays_); + } + + template + void construct_element(value_type* p,Args&&... args) + { + alloc_traits::construct(al(),p,std::forward(args)...); + } + + void destroy_element(value_type* p)noexcept + { + alloc_traits::destroy(al(),p); + } + + struct destroy_element_on_exit + { + ~destroy_element_on_exit(){this_->destroy_element(p);} + table *this_; + value_type *p; + }; + + std::size_t max_load()const + { + static constexpr std::size_t small_capacity=2*N-1; + + auto capacity_=capacity(); + if(capacity_<=small_capacity){ + return capacity_; /* we allow 100% usage */ + } + else{ + return (std::size_t)(mlf*(float)(capacity_)); + } + } + + template + static inline auto key_from(const T& x) + ->decltype(type_policy::extract(x)) + { + return type_policy::extract(x); + } + + template + static inline auto key_from( + std::piecewise_construct_t,const Arg1& k,const Arg2&) + ->decltype(std::get<0>(k)) + { + return std::get<0>(k); + } + + template + inline std::size_t hash_for(const Key& x)const + { + return mix_policy::mix(h(),x); + } + + inline std::size_t position_for(std::size_t hash)const + { + return position_for(hash,arrays); + } + + static inline std::size_t position_for( + std::size_t hash,const arrays_type& arrays_) + { + return size_policy::position(hash,arrays_.groups_size_index); + } + + static inline void prefetch_elements(const value_type* p) + { + /* We have experimentally confirmed that ARM architectures get a higher + * speedup when around the first half of the element slots in a group are + * prefetched, whereas for Intel just the first cache line is best. + * Please report back if you find better tunings for some particular + * architectures. + */ + +#if BOOST_ARCH_ARM + /* Cache line size can't be known at compile time, so we settle on + * the very frequent value of 64B. + */ + constexpr int cache_line=64; + const char *p0=reinterpret_cast(p), + *p1=p0+sizeof(value_type)*N/2; + for(;p0 + BOOST_FORCEINLINE iterator find_impl( + const Key& x,std::size_t pos0,std::size_t hash)const + { + prober pb(pos0); + do{ + auto pos=pb.get(); + auto pg=arrays.groups+pos; + auto mask=pg->match(hash); + if(mask){ + auto p=arrays.elements+pos*N; + prefetch_elements(p); + do{ + auto n=unchecked_countr_zero(mask); + if(BOOST_LIKELY(bool(pred()(x,key_from(p[n]))))){ + return {pg,n,p+n}; + } + mask&=mask-1; + }while(mask); + } + if(BOOST_LIKELY(pg->is_not_overflowed(hash))){ + return {}; /* end() */ + } + } + while(BOOST_LIKELY(pb.next(arrays.groups_size_mask))); + return {}; /* end() */ + } + +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4800 */ +#endif + + template + BOOST_FORCEINLINE std::pair emplace_impl(Args&&... args) + { + const auto &k=key_from(std::forward(args)...); + auto hash=hash_for(k); + auto pos0=position_for(hash); + auto it=find_impl(k,pos0,hash); + + if(it!=end()){ + return {it,false}; + } + if(BOOST_LIKELY(size_(args)...), + true + }; + } + else{ + return { + unchecked_emplace_with_rehash(hash,std::forward(args)...), + true + }; + } + } + + static std::size_t capacity_for(std::size_t n) + { + return size_policy::size(size_index_for(n))*N-1; + } + + template + BOOST_NOINLINE iterator + unchecked_emplace_with_rehash(std::size_t hash,Args&&... args) + { + /* strong exception guarantee -> try insertion before rehash */ + auto new_arrays_=new_arrays( + std::size_t(std::ceil(static_cast(size_+1)/mlf))); + iterator it; + BOOST_TRY{ + it=nosize_unchecked_emplace_at( + new_arrays_,position_for(hash,new_arrays_), + hash,std::forward(args)...); + } + BOOST_CATCH(...){ + delete_arrays(new_arrays_); + BOOST_RETHROW + } + BOOST_CATCH_END + + /* new_arrays_ lifetime taken care of by unchecked_rehash */ + unchecked_rehash(new_arrays_); + ++size_; + return it; + } + + BOOST_NOINLINE void unchecked_rehash(std::size_t n) + { + auto new_arrays_=new_arrays(n); + unchecked_rehash(new_arrays_); + } + + BOOST_NOINLINE void unchecked_rehash(arrays_type& new_arrays_) + { + std::size_t num_destroyed=0; + BOOST_TRY{ + for_all_elements([&,this](value_type* p){ + nosize_transfer_element(p,new_arrays_,num_destroyed); + }); + } + BOOST_CATCH(...){ + if(num_destroyed){ + size_-=num_destroyed; + for(auto pg=arrays.groups;;++pg){ + auto mask=pg->match_occupied(); + while(mask){ + auto nz=unchecked_countr_zero(mask); + pg->reset(nz); + if(!(--num_destroyed))goto continue_; + mask&=mask-1; + } + } + } + continue_: + for_all_elements(new_arrays_,[this](value_type* p){ + destroy_element(p); + }); + delete_arrays(new_arrays_); + BOOST_RETHROW + } + BOOST_CATCH_END + + /* either all moved and destroyed or all copied */ + BOOST_ASSERT(num_destroyed==size()||num_destroyed==0); + if(num_destroyed!=size()){ + for_all_elements([this](value_type* p){ + destroy_element(p); + }); + } + delete_arrays(arrays); + arrays=new_arrays_; + ml=max_load(); + } + + void noshrink_reserve(std::size_t n) + { + /* used only on assignment after element clearance */ + BOOST_ASSERT(empty()); + + if(n){ + n=std::size_t(std::ceil(float(n)/mlf)); /* elements -> slots */ + n=capacity_for(n); /* exact resulting capacity */ + + if(n>capacity()){ + auto new_arrays_=new_arrays(n); + delete_arrays(arrays); + arrays=new_arrays_; + ml=max_load(); + } + } + } + + template + void unchecked_insert(Value&& x) + { + auto hash=hash_for(key_from(x)); + unchecked_emplace_at(position_for(hash),hash,std::forward(x)); + } + + void nosize_transfer_element( + value_type* p,const arrays_type& arrays_,std::size_t& num_destroyed) + { + nosize_transfer_element( + p,hash_for(key_from(*p)),arrays_,num_destroyed, + std::integral_constant< /* std::move_if_noexcept semantics */ + bool, + std::is_nothrow_move_constructible::value|| + !std::is_copy_constructible::value>{}); + } + + void nosize_transfer_element( + value_type* p,std::size_t hash,const arrays_type& arrays_, + std::size_t& num_destroyed,std::true_type /* ->move */) + { + /* Destroy p even if an an exception is thrown in the middle of move + * construction, which could leave the source half-moved. + */ + ++num_destroyed; + destroy_element_on_exit d{this,p}; + (void)d; /* unused var warning */ + nosize_unchecked_emplace_at( + arrays_,position_for(hash,arrays_),hash,type_policy::move(*p)); + } + + void nosize_transfer_element( + value_type* p,std::size_t hash,const arrays_type& arrays_, + std::size_t& /*num_destroyed*/,std::false_type /* ->copy */) + { + nosize_unchecked_emplace_at( + arrays_,position_for(hash,arrays_),hash, + const_cast(*p)); + } + + template + iterator unchecked_emplace_at( + std::size_t pos0,std::size_t hash,Args&&... args) + { + auto res=nosize_unchecked_emplace_at( + arrays,pos0,hash,std::forward(args)...); + ++size_; + return res; + } + + template + iterator nosize_unchecked_emplace_at( + const arrays_type& arrays_,std::size_t pos0,std::size_t hash, + Args&&... args) + { + for(prober pb(pos0);;pb.next(arrays_.groups_size_mask)){ + auto pos=pb.get(); + auto pg=arrays_.groups+pos; + auto mask=pg->match_available(); + if(BOOST_LIKELY(mask!=0)){ + auto n=unchecked_countr_zero(mask); + auto p=arrays_.elements+pos*N+n; + construct_element(p,std::forward(args)...); + pg->set(n,hash); + return {pg,n,p}; + } + else pg->mark_overflow(hash); + } + } + + template + std::size_t erase_if_impl(Predicate pr) + { + std::size_t s=size(); + for_all_elements([&,this](group_type* pg,unsigned int n,value_type* p){ + if(pr(*p)) erase(iterator{pg,n,p}); + }); + return std::size_t(s-size()); + } + + template + void for_all_elements(F f)const + { + for_all_elements(arrays,f); + } + + template + static auto for_all_elements(const arrays_type& arrays_,F f) + ->decltype(f(nullptr),void()) + { + for_all_elements( + arrays_,[&](group_type*,unsigned int,value_type* p){return f(p);}); + } + + template + static auto for_all_elements(const arrays_type& arrays_,F f) + ->decltype(f(nullptr,0,nullptr),void()) + { + auto p=arrays_.elements; + if(!p){return;} + for(auto pg=arrays_.groups,last=pg+arrays_.groups_size_mask+1; + pg!=last;++pg,p+=N){ + auto mask=pg->match_really_occupied(); + while(mask){ + auto n=unchecked_countr_zero(mask); + f(pg,n,p+n); + mask&=mask-1; + } + } + } + + std::size_t size_; + arrays_type arrays; + std::size_t ml; +}; + +#if BOOST_WORKAROUND(BOOST_MSVC,<=1900) +#pragma warning(pop) /* C4702 */ +#endif + +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4714 */ +#endif + +#if defined(BOOST_GCC) +#pragma GCC diagnostic pop /* ignored "-Wshadow" */ +#endif + +} /* namespace foa */ +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#undef BOOST_UNORDERED_ASSUME +#undef BOOST_UNORDERED_HAS_BUILTIN +#ifdef BOOST_UNORDERED_LITTLE_ENDIAN_NEON +#undef BOOST_UNORDERED_LITTLE_ENDIAN_NEON +#endif +#ifdef BOOST_UNORDERED_SSE2 +#undef BOOST_UNORDERED_SSE2 +#endif +#endif diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 1dd11ecb..4ee11748 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -618,39 +619,6 @@ namespace boost { #endif -//////////////////////////////////////////////////////////////////////////// -// Type checkers used for the transparent member functions added by C++20 and up - - template struct is_transparent : public false_type - { - }; - - template - struct is_transparent::type> - : public true_type - { - }; - - template struct are_transparent - { - static bool const value = - is_transparent::value && is_transparent::value; - }; - - template struct transparent_non_iterable - { - typedef typename UnorderedMap::hasher hash; - typedef typename UnorderedMap::key_equal key_equal; - typedef typename UnorderedMap::iterator iterator; - typedef typename UnorderedMap::const_iterator const_iterator; - - static bool const value = - are_transparent::value && - !boost::is_convertible::value && - !boost::is_convertible::value; - }; - //////////////////////////////////////////////////////////////////////////// // Explicitly call a destructor diff --git a/include/boost/unordered/detail/type_traits.hpp b/include/boost/unordered/detail/type_traits.hpp new file mode 100644 index 00000000..3d7dcf67 --- /dev/null +++ b/include/boost/unordered/detail/type_traits.hpp @@ -0,0 +1,59 @@ +// Copyright (C) 2022 Christian Mazakas +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP +#define BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP + +#include +#if defined(BOOST_HAS_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + +namespace boost { + namespace unordered { + namespace detail { + //////////////////////////////////////////////////////////////////////////// + // Type checkers used for the transparent member functions added by C++20 + // and up + + template + struct is_transparent : public boost::false_type + { + }; + + template + struct is_transparent::type> + : public boost::true_type + { + }; + + template struct are_transparent + { + static bool const value = + is_transparent::value && is_transparent::value; + }; + + template struct transparent_non_iterable + { + typedef typename UnorderedMap::hasher hash; + typedef typename UnorderedMap::key_equal key_equal; + typedef typename UnorderedMap::iterator iterator; + typedef typename UnorderedMap::const_iterator const_iterator; + + static bool const value = + are_transparent::value && + !boost::is_convertible::value && + !boost::is_convertible::value; + }; + } // namespace detail + } // namespace unordered +} // namespace boost + +#endif // BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP diff --git a/include/boost/unordered/detail/xmx.hpp b/include/boost/unordered/detail/xmx.hpp new file mode 100644 index 00000000..4b24fd80 --- /dev/null +++ b/include/boost/unordered/detail/xmx.hpp @@ -0,0 +1,75 @@ +/* 32b/64b xmx mix function. + * + * Copyright 2022 Peter Dimov. + * Copyright 2022 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_XMX_HPP +#define BOOST_UNORDERED_DETAIL_XMX_HPP + +#include +#include +#include + +namespace boost{ +namespace unordered{ +namespace detail{ + +/* Bit mixer for improvement of statistical properties of hash functions. + * The implementation is different on 64bit and 32bit architectures: + * + * - 64bit: same as xmx function in + * http://jonkagstrom.com/bit-mixer-construction/index.html + * - 32bit: generated by Hash Function Prospector + * (https://github.com/skeeto/hash-prospector) and selected as the + * best overall performer in benchmarks of Boost.Unordered flat containers. + * Score assigned by Hash Prospector: 333.7934929677524 + */ + +#if defined(SIZE_MAX) +#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0 +#define BOOST_UNORDERED_64B_ARCHITECTURE /* >64 bits assumed as 64 bits */ +#endif +#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */ +#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0 +#define BOOST_UNORDERED_64B_ARCHITECTURE +#endif +#endif + +static inline std::size_t xmx(std::size_t x)noexcept +{ +#if defined(BOOST_UNORDERED_64B_ARCHITECTURE) + + boost::uint64_t z=(boost::uint64_t)x; + + z^=z>>23; + z*=0xff51afd7ed558ccdull; + z^=z>>23; + + return (std::size_t)z; + +#else /* 32 bits assumed */ + + x^=x>>18; + x*=0x56b5aaadu; + x^=x>>16; + + return x; + +#endif +} + +#ifdef BOOST_UNORDERED_64B_ARCHITECTURE +#undef BOOST_UNORDERED_64B_ARCHITECTURE +#endif + +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#endif diff --git a/include/boost/unordered/hash_traits.hpp b/include/boost/unordered/hash_traits.hpp new file mode 100644 index 00000000..5ab4a556 --- /dev/null +++ b/include/boost/unordered/hash_traits.hpp @@ -0,0 +1,44 @@ +/* Hash function characterization. + * + * Copyright 2022 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP +#define BOOST_UNORDERED_HASH_TRAITS_HPP + +#include +#include + +namespace boost{ +namespace unordered{ + +namespace detail{ + +template +struct hash_is_avalanching_impl:std::false_type{}; + +template +struct hash_is_avalanching_impl>: + std::true_type{}; + +} /* namespace detail */ + +/* Each trait can be partially specialized by users for concrete hash functions + * when actual characterization differs from default. + */ + +/* Derived from std::true_type if the type Hash::is_avalanching is present, + * derived from std::false_type otherwise. + */ +template +struct hash_is_avalanching:detail::hash_is_avalanching_impl::type{}; + +} /* namespace unordered */ +} /* namespace boost */ + +#endif diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp new file mode 100644 index 00000000..dd464eb2 --- /dev/null +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -0,0 +1,619 @@ +// Copyright (C) 2022 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_UNORDERED_FLAT_MAP_HPP_INCLUDED +#define BOOST_UNORDERED_UNORDERED_FLAT_MAP_HPP_INCLUDED + +#include +#if defined(BOOST_HAS_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace boost { + namespace unordered { + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable : 4714) /* marked as __forceinline not inlined */ +#endif + + template + class unordered_flat_map + { + struct map_types + { + using key_type = Key; + using raw_key_type = typename std::remove_const::type; + using raw_mapped_type = typename std::remove_const::type; + + using init_type = std::pair; + using moved_type = std::pair; + using value_type = std::pair; + + template + static raw_key_type const& extract(std::pair const& kv) + { + return kv.first; + } + + static moved_type move(value_type& x) + { + // TODO: we probably need to launder here + return {std::move(const_cast(x.first)), + std::move(const_cast(x.second))}; + } + }; + + using table_type = detail::foa::table::type>; + + table_type table_; + + template + typename unordered_flat_map::size_type friend erase_if( + unordered_flat_map& set, Pred pred); + + public: + using key_type = Key; + using mapped_type = T; + using value_type = typename map_types::value_type; + using init_type = typename map_types::init_type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = KeyEqual; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = value_type const&; + using pointer = typename boost::allocator_pointer::type; + using const_pointer = + typename boost::allocator_const_pointer::type; + using iterator = typename table_type::iterator; + using const_iterator = typename table_type::const_iterator; + + unordered_flat_map() : unordered_flat_map(0) {} + + explicit unordered_flat_map(size_type n, hasher const& h = hasher(), + key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : table_(n, h, pred, a) + { + } + + unordered_flat_map(size_type n, allocator_type const& a) + : unordered_flat_map(n, hasher(), key_equal(), a) + { + } + + unordered_flat_map(size_type n, hasher const& h, allocator_type const& a) + : unordered_flat_map(n, h, key_equal(), a) + { + } + + explicit unordered_flat_map(allocator_type const& a) + : unordered_flat_map(0, a) + { + } + + template + unordered_flat_map(Iterator first, Iterator last, size_type n = 0, + hasher const& h = hasher(), key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : unordered_flat_map(n, h, pred, a) + { + this->insert(first, last); + } + + template + unordered_flat_map( + Iterator first, Iterator last, size_type n, allocator_type const& a) + : unordered_flat_map(first, last, n, hasher(), key_equal(), a) + { + } + + template + unordered_flat_map(Iterator first, Iterator last, size_type n, + hasher const& h, allocator_type const& a) + : unordered_flat_map(first, last, n, h, key_equal(), a) + { + } + + unordered_flat_map(unordered_flat_map const& other) : table_(other.table_) + { + } + + unordered_flat_map( + unordered_flat_map const& other, allocator_type const& a) + : table_(other.table_, a) + { + } + + unordered_flat_map(unordered_flat_map&& other) + noexcept(std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value) + : table_(std::move(other.table_)) + { + } + + unordered_flat_map(unordered_flat_map&& other, allocator_type const& al) + : table_(std::move(other.table_), al) + { + } + + unordered_flat_map(std::initializer_list ilist, + size_type n = 0, hasher const& h = hasher(), + key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : unordered_flat_map(ilist.begin(), ilist.end(), n, h, pred, a) + { + } + + unordered_flat_map(std::initializer_list init, size_type n, + allocator_type const& a) + : unordered_flat_map(init, n, hasher(), key_equal(), a) + { + } + + unordered_flat_map(std::initializer_list init, size_type n, + hasher const& h, allocator_type const& a) + : unordered_flat_map(init, n, h, key_equal(), a) + { + } + + ~unordered_flat_map() = default; + + unordered_flat_map& operator=(unordered_flat_map const& other) + { + table_ = other.table_; + return *this; + } + + unordered_flat_map& operator=(unordered_flat_map&& other) noexcept( + noexcept(std::declval() = std::declval())) + { + table_ = std::move(other.table_); + return *this; + } + + allocator_type get_allocator() const noexcept + { + return table_.get_allocator(); + } + + /// Iterators + /// + + iterator begin() noexcept { return table_.begin(); } + const_iterator begin() const noexcept { return table_.begin(); } + const_iterator cbegin() const noexcept { return table_.cbegin(); } + + iterator end() noexcept { return table_.end(); } + const_iterator end() const noexcept { return table_.end(); } + const_iterator cend() const noexcept { return table_.cend(); } + + /// Capacity + /// + + BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept + { + return table_.empty(); + } + + size_type size() const noexcept { return table_.size(); } + + size_type max_size() const noexcept { return table_.max_size(); } + + /// Modifiers + /// + + void clear() noexcept { table_.clear(); } + + template + BOOST_FORCEINLINE auto insert(Ty&& value) + -> decltype(table_.insert(std::forward(value))) + { + return table_.insert(std::forward(value)); + } + + BOOST_FORCEINLINE std::pair insert(init_type&& value) + { + return table_.insert(std::move(value)); + } + + template + BOOST_FORCEINLINE auto insert(const_iterator, Ty&& value) + -> decltype(table_.insert(std::forward(value)).first) + { + return table_.insert(std::forward(value)).first; + } + + BOOST_FORCEINLINE iterator insert(const_iterator, init_type&& value) + { + return table_.insert(std::move(value)).first; + } + + template + BOOST_FORCEINLINE void insert(InputIterator first, InputIterator last) + { + for (auto pos = first; pos != last; ++pos) { + table_.emplace(*pos); + } + } + + void insert(std::initializer_list ilist) + { + this->insert(ilist.begin(), ilist.end()); + } + + template + std::pair insert_or_assign(key_type const& key, M&& obj) + { + auto iter_bool_pair = table_.try_emplace(key, std::forward(obj)); + if (iter_bool_pair.second) { + return iter_bool_pair; + } + iter_bool_pair.first->second = std::forward(obj); + return iter_bool_pair; + } + + template + std::pair insert_or_assign(key_type&& key, M&& obj) + { + auto iter_bool_pair = + table_.try_emplace(std::move(key), std::forward(obj)); + if (iter_bool_pair.second) { + return iter_bool_pair; + } + iter_bool_pair.first->second = std::forward(obj); + return iter_bool_pair; + } + + template + iterator insert_or_assign(const_iterator, key_type const& key, M&& obj) + { + return this->insert_or_assign(key, std::forward(obj)).first; + } + + template + iterator insert_or_assign(const_iterator, key_type&& key, M&& obj) + { + return this->insert_or_assign(std::move(key), std::forward(obj)) + .first; + } + + template + BOOST_FORCEINLINE std::pair emplace(Args&&... args) + { + return table_.emplace(std::forward(args)...); + } + + template + BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args) + { + return table_.emplace(std::forward(args)...).first; + } + + template + BOOST_FORCEINLINE std::pair try_emplace( + key_type const& key, Args&&... args) + { + return table_.try_emplace(key, std::forward(args)...); + } + + template + BOOST_FORCEINLINE std::pair try_emplace( + key_type&& key, Args&&... args) + { + return table_.try_emplace(std::move(key), std::forward(args)...); + } + + template + BOOST_FORCEINLINE iterator try_emplace( + const_iterator, key_type const& key, Args&&... args) + { + return table_.try_emplace(key, std::forward(args)...).first; + } + + template + BOOST_FORCEINLINE iterator try_emplace( + const_iterator, key_type&& key, Args&&... args) + { + return table_.try_emplace(std::move(key), std::forward(args)...) + .first; + } + + BOOST_FORCEINLINE void erase(iterator pos) { table_.erase(pos); } + BOOST_FORCEINLINE void erase(const_iterator pos) + { + return table_.erase(pos); + } + iterator erase(const_iterator first, const_iterator last) + { + while (first != last) { + this->erase(first++); + } + return iterator{detail::foa::const_iterator_cast_tag{}, last}; + } + + BOOST_FORCEINLINE size_type erase(key_type const& key) + { + return table_.erase(key); + } + + template + BOOST_FORCEINLINE typename std::enable_if< + detail::transparent_non_iterable::value, + size_type>::type + erase(K const& key) + { + return table_.erase(key); + } + + void swap(unordered_flat_map& rhs) noexcept( + noexcept(std::declval().swap(std::declval()))) + { + table_.swap(rhs.table_); + } + + template + void merge( + unordered_flat_map& + source) + { + table_.merge(source.table_); + } + + template + void merge( + unordered_flat_map&& + source) + { + table_.merge(std::move(source.table_)); + } + + /// Lookup + /// + + mapped_type& at(key_type const& key) + { + auto pos = table_.find(key); + if (pos != table_.end()) { + return pos->second; + } + // TODO: someday refactor this to conditionally serialize the key and + // include it in the error message + // + boost::throw_exception( + std::out_of_range("key was not found in unordered_flat_map")); + } + + mapped_type const& at(key_type const& key) const + { + auto pos = table_.find(key); + if (pos != table_.end()) { + return pos->second; + } + boost::throw_exception( + std::out_of_range("key was not found in unordered_flat_map")); + } + + mapped_type& operator[](key_type const& key) + { + return table_.try_emplace(key).first->second; + } + + mapped_type& operator[](key_type&& key) + { + return table_.try_emplace(std::move(key)).first->second; + } + + size_type count(key_type const& key) const + { + auto pos = table_.find(key); + return pos != table_.end() ? 1 : 0; + } + + template + typename std::enable_if< + detail::are_transparent::value, size_type>::type + count(K const& key) const + { + auto pos = table_.find(key); + return pos != table_.end() ? 1 : 0; + } + + BOOST_FORCEINLINE iterator find(key_type const& key) + { + return table_.find(key); + } + + BOOST_FORCEINLINE const_iterator find(key_type const& key) const + { + return table_.find(key); + } + + template + BOOST_FORCEINLINE typename std::enable_if< + boost::unordered::detail::are_transparent::value, + iterator>::type + find(K const& key) + { + return table_.find(key); + } + + template + BOOST_FORCEINLINE typename std::enable_if< + boost::unordered::detail::are_transparent::value, + const_iterator>::type + find(K const& key) const + { + return table_.find(key); + } + + bool contains(key_type const& key) const + { + return this->find(key) != this->end(); + } + + template + typename std::enable_if< + boost::unordered::detail::are_transparent::value, + bool>::type + contains(K const& key) const + { + return this->find(key) != this->end(); + } + + std::pair equal_range(key_type const& key) + { + auto pos = table_.find(key); + if (pos == table_.end()) { + return {pos, pos}; + } + + auto next = pos; + ++next; + return {pos, next}; + } + + std::pair equal_range( + key_type const& key) const + { + auto pos = table_.find(key); + if (pos == table_.end()) { + return {pos, pos}; + } + + auto next = pos; + ++next; + return {pos, next}; + } + + template + typename std::enable_if< + detail::are_transparent::value, + std::pair >::type + equal_range(K const& key) + { + auto pos = table_.find(key); + if (pos == table_.end()) { + return {pos, pos}; + } + + auto next = pos; + ++next; + return {pos, next}; + } + + template + typename std::enable_if< + detail::are_transparent::value, + std::pair >::type + equal_range(K const& key) const + { + auto pos = table_.find(key); + if (pos == table_.end()) { + return {pos, pos}; + } + + auto next = pos; + ++next; + return {pos, next}; + } + + /// Hash Policy + /// + + size_type bucket_count() const noexcept { return table_.capacity(); } + + float load_factor() const noexcept { return table_.load_factor(); } + + float max_load_factor() const noexcept + { + return table_.max_load_factor(); + } + + void max_load_factor(float) {} + + void rehash(size_type n) { table_.rehash(n); } + + void reserve(size_type n) { table_.reserve(n); } + + /// Observers + /// + + hasher hash_function() const { return table_.hash_function(); } + + key_equal key_eq() const { return table_.key_eq(); } + }; + + template + bool operator==( + unordered_flat_map const& lhs, + unordered_flat_map const& rhs) + { + if (&lhs == &rhs) { + return true; + } + + return (lhs.size() == rhs.size()) && ([&] { + for (auto const& kvp : lhs) { + auto pos = rhs.find(kvp.first); + if ((pos == rhs.end()) || (*pos != kvp)) { + return false; + } + } + return true; + })(); + } + + template + bool operator!=( + unordered_flat_map const& lhs, + unordered_flat_map const& rhs) + { + return !(lhs == rhs); + } + + template + void swap(unordered_flat_map& lhs, + unordered_flat_map& rhs) + noexcept(noexcept(lhs.swap(rhs))) + { + lhs.swap(rhs); + } + + template + typename unordered_flat_map::size_type + erase_if( + unordered_flat_map& map, Pred pred) + { + return erase_if(map.table_, pred); + } + +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4714 */ +#endif + + } // namespace unordered +} // namespace boost + +#endif diff --git a/include/boost/unordered/unordered_flat_map_fwd.hpp b/include/boost/unordered/unordered_flat_map_fwd.hpp new file mode 100644 index 00000000..2c34d0fb --- /dev/null +++ b/include/boost/unordered/unordered_flat_map_fwd.hpp @@ -0,0 +1,49 @@ + +// Copyright (C) 2022 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_FLAT_MAP_FWD_HPP_INCLUDED +#define BOOST_UNORDERED_FLAT_MAP_FWD_HPP_INCLUDED + +#include +#if defined(BOOST_HAS_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + +namespace boost { + namespace unordered { + template , + class KeyEqual = std::equal_to, + class Allocator = std::allocator > > + class unordered_flat_map; + + template + bool operator==( + unordered_flat_map const& lhs, + unordered_flat_map const& rhs); + + template + bool operator!=( + unordered_flat_map const& lhs, + unordered_flat_map const& rhs); + + template + void swap(unordered_flat_map& lhs, + unordered_flat_map& rhs) + noexcept(noexcept(lhs.swap(rhs))); + } // namespace unordered + + using boost::unordered::unordered_flat_map; + + using boost::unordered::swap; + using boost::unordered::operator==; + using boost::unordered::operator!=; +} // namespace boost + +#endif diff --git a/include/boost/unordered/unordered_flat_set.hpp b/include/boost/unordered/unordered_flat_set.hpp new file mode 100644 index 00000000..1950dfb3 --- /dev/null +++ b/include/boost/unordered/unordered_flat_set.hpp @@ -0,0 +1,494 @@ +// Copyright (C) 2022 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_UNORDERED_FLAT_SET_HPP_INCLUDED +#define BOOST_UNORDERED_UNORDERED_FLAT_SET_HPP_INCLUDED + +#include +#if defined(BOOST_HAS_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace boost { + namespace unordered { + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable : 4714) /* marked as __forceinline not inlined */ +#endif + + template + class unordered_flat_set + { + struct set_types + { + using key_type = Key; + using init_type = Key; + using value_type = Key; + static Key const& extract(value_type const& key) { return key; } + static Key&& move(value_type& x) { return std::move(x); } + }; + + using table_type = detail::foa::table::type>; + + table_type table_; + + template + typename unordered_flat_set::size_type friend erase_if( + unordered_flat_set& set, Pred pred); + + public: + using key_type = Key; + using value_type = typename set_types::value_type; + using init_type = typename set_types::init_type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = KeyEqual; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = value_type const&; + using pointer = typename boost::allocator_pointer::type; + using const_pointer = + typename boost::allocator_const_pointer::type; + using iterator = typename table_type::iterator; + using const_iterator = typename table_type::const_iterator; + + unordered_flat_set() : unordered_flat_set(0) {} + + explicit unordered_flat_set(size_type n, hasher const& h = hasher(), + key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : table_(n, h, pred, a) + { + } + + unordered_flat_set(size_type n, allocator_type const& a) + : unordered_flat_set(n, hasher(), key_equal(), a) + { + } + + unordered_flat_set(size_type n, hasher const& h, allocator_type const& a) + : unordered_flat_set(n, h, key_equal(), a) + { + } + + explicit unordered_flat_set(allocator_type const& a) + : unordered_flat_set(0, a) + { + } + + template + unordered_flat_set(Iterator first, Iterator last, size_type n = 0, + hasher const& h = hasher(), key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : unordered_flat_set(n, h, pred, a) + { + this->insert(first, last); + } + + template + unordered_flat_set( + InputIt first, InputIt last, size_type n, allocator_type const& a) + : unordered_flat_set(first, last, n, hasher(), key_equal(), a) + { + } + + template + unordered_flat_set(Iterator first, Iterator last, size_type n, + hasher const& h, allocator_type const& a) + : unordered_flat_set(first, last, n, h, key_equal(), a) + { + } + + unordered_flat_set(unordered_flat_set const& other) : table_(other.table_) + { + } + + unordered_flat_set( + unordered_flat_set const& other, allocator_type const& a) + : table_(other.table_, a) + { + } + + unordered_flat_set(unordered_flat_set&& other) + noexcept(std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value) + : table_(std::move(other.table_)) + { + } + + unordered_flat_set(unordered_flat_set&& other, allocator_type const& al) + : table_(std::move(other.table_), al) + { + } + + unordered_flat_set(std::initializer_list ilist, + size_type n = 0, hasher const& h = hasher(), + key_equal const& pred = key_equal(), + allocator_type const& a = allocator_type()) + : unordered_flat_set(ilist.begin(), ilist.end(), n, h, pred, a) + { + } + + unordered_flat_set(std::initializer_list init, size_type n, + allocator_type const& a) + : unordered_flat_set(init, n, hasher(), key_equal(), a) + { + } + + unordered_flat_set(std::initializer_list init, size_type n, + hasher const& h, allocator_type const& a) + : unordered_flat_set(init, n, h, key_equal(), a) + { + } + + ~unordered_flat_set() = default; + + unordered_flat_set& operator=(unordered_flat_set const& other) + { + table_ = other.table_; + return *this; + } + + unordered_flat_set& operator=(unordered_flat_set&& other) noexcept( + noexcept(std::declval() = std::declval())) + { + table_ = std::move(other.table_); + return *this; + } + + allocator_type get_allocator() const noexcept + { + return table_.get_allocator(); + } + + /// Iterators + /// + + iterator begin() noexcept { return table_.begin(); } + const_iterator begin() const noexcept { return table_.begin(); } + const_iterator cbegin() const noexcept { return table_.cbegin(); } + + iterator end() noexcept { return table_.end(); } + const_iterator end() const noexcept { return table_.end(); } + const_iterator cend() const noexcept { return table_.cend(); } + + /// Capacity + /// + + BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept + { + return table_.empty(); + } + + size_type size() const noexcept { return table_.size(); } + + size_type max_size() const noexcept { return table_.max_size(); } + + /// Modifiers + /// + + void clear() noexcept { table_.clear(); } + + BOOST_FORCEINLINE std::pair insert( + value_type const& value) + { + return table_.insert(value); + } + + BOOST_FORCEINLINE std::pair insert(value_type&& value) + { + return table_.insert(std::move(value)); + } + + BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value) + { + return table_.insert(value).first; + } + + BOOST_FORCEINLINE iterator insert(const_iterator, value_type&& value) + { + return table_.insert(std::move(value)).first; + } + + template + void insert(InputIterator first, InputIterator last) + { + for (auto pos = first; pos != last; ++pos) { + table_.emplace(*pos); + } + } + + void insert(std::initializer_list ilist) + { + this->insert(ilist.begin(), ilist.end()); + } + + template + BOOST_FORCEINLINE std::pair emplace(Args&&... args) + { + return table_.emplace(std::forward(args)...); + } + + template + BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args) + { + return table_.emplace(std::forward(args)...).first; + } + + BOOST_FORCEINLINE void erase(const_iterator pos) + { + return table_.erase(pos); + } + iterator erase(const_iterator first, const_iterator last) + { + while (first != last) { + this->erase(first++); + } + return iterator{detail::foa::const_iterator_cast_tag{}, last}; + } + + BOOST_FORCEINLINE size_type erase(key_type const& key) + { + return table_.erase(key); + } + + template + BOOST_FORCEINLINE typename std::enable_if< + detail::transparent_non_iterable::value, + size_type>::type + erase(K const& key) + { + return table_.erase(key); + } + + void swap(unordered_flat_set& rhs) noexcept( + noexcept(std::declval().swap(std::declval()))) + { + table_.swap(rhs.table_); + } + + template + void merge(unordered_flat_set& source) + { + table_.merge(source.table_); + } + + template + void merge(unordered_flat_set&& source) + { + table_.merge(std::move(source.table_)); + } + + /// Lookup + /// + + size_type count(key_type const& key) const + { + auto pos = table_.find(key); + return pos != table_.end() ? 1 : 0; + } + + template + typename std::enable_if< + detail::are_transparent::value, size_type>::type + count(K const& key) const + { + auto pos = table_.find(key); + return pos != table_.end() ? 1 : 0; + } + + BOOST_FORCEINLINE iterator find(key_type const& key) + { + return table_.find(key); + } + + BOOST_FORCEINLINE const_iterator find(key_type const& key) const + { + return table_.find(key); + } + + template + BOOST_FORCEINLINE typename std::enable_if< + boost::unordered::detail::are_transparent::value, + iterator>::type + find(K const& key) + { + return table_.find(key); + } + + template + BOOST_FORCEINLINE typename std::enable_if< + boost::unordered::detail::are_transparent::value, + const_iterator>::type + find(K const& key) const + { + return table_.find(key); + } + + bool contains(key_type const& key) const + { + return this->find(key) != this->end(); + } + + template + typename std::enable_if< + boost::unordered::detail::are_transparent::value, + bool>::type + contains(K const& key) const + { + return this->find(key) != this->end(); + } + + std::pair equal_range(key_type const& key) + { + auto pos = table_.find(key); + if (pos == table_.end()) { + return {pos, pos}; + } + + auto next = pos; + ++next; + return {pos, next}; + } + + std::pair equal_range( + key_type const& key) const + { + auto pos = table_.find(key); + if (pos == table_.end()) { + return {pos, pos}; + } + + auto next = pos; + ++next; + return {pos, next}; + } + + template + typename std::enable_if< + detail::are_transparent::value, + std::pair >::type + equal_range(K const& key) + { + auto pos = table_.find(key); + if (pos == table_.end()) { + return {pos, pos}; + } + + auto next = pos; + ++next; + return {pos, next}; + } + + template + typename std::enable_if< + detail::are_transparent::value, + std::pair >::type + equal_range(K const& key) const + { + auto pos = table_.find(key); + if (pos == table_.end()) { + return {pos, pos}; + } + + auto next = pos; + ++next; + return {pos, next}; + } + + /// Hash Policy + /// + + size_type bucket_count() const noexcept { return table_.capacity(); } + + float load_factor() const noexcept { return table_.load_factor(); } + + float max_load_factor() const noexcept + { + return table_.max_load_factor(); + } + + void max_load_factor(float) {} + + void rehash(size_type n) { table_.rehash(n); } + + void reserve(size_type n) { table_.reserve(n); } + + /// Observers + /// + + hasher hash_function() const { return table_.hash_function(); } + + key_equal key_eq() const { return table_.key_eq(); } + }; + + template + bool operator==( + unordered_flat_set const& lhs, + unordered_flat_set const& rhs) + { + if (&lhs == &rhs) { + return true; + } + + return (lhs.size() == rhs.size()) && ([&] { + for (auto const& key : lhs) { + auto pos = rhs.find(key); + if ((pos == rhs.end()) || (key != *pos)) { + return false; + } + } + return true; + })(); + } + + template + bool operator!=( + unordered_flat_set const& lhs, + unordered_flat_set const& rhs) + { + return !(lhs == rhs); + } + + template + void swap(unordered_flat_set& lhs, + unordered_flat_set& rhs) + noexcept(noexcept(lhs.swap(rhs))) + { + lhs.swap(rhs); + } + + template + typename unordered_flat_set::size_type + erase_if(unordered_flat_set& set, Pred pred) + { + return erase_if(set.table_, pred); + } + +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4714 */ +#endif + + } // namespace unordered +} // namespace boost + +#endif diff --git a/include/boost/unordered/unordered_flat_set_fwd.hpp b/include/boost/unordered/unordered_flat_set_fwd.hpp new file mode 100644 index 00000000..51f534ef --- /dev/null +++ b/include/boost/unordered/unordered_flat_set_fwd.hpp @@ -0,0 +1,49 @@ + +// Copyright (C) 2022 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_FLAT_SET_FWD_HPP_INCLUDED +#define BOOST_UNORDERED_FLAT_SET_FWD_HPP_INCLUDED + +#include +#if defined(BOOST_HAS_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include + +namespace boost { + namespace unordered { + template , + class KeyEqual = std::equal_to, + class Allocator = std::allocator > + class unordered_flat_set; + + template + bool operator==( + unordered_flat_set const& lhs, + unordered_flat_set const& rhs); + + template + bool operator!=( + unordered_flat_set const& lhs, + unordered_flat_set const& rhs); + + template + void swap(unordered_flat_set& lhs, + unordered_flat_set& rhs) + noexcept(noexcept(lhs.swap(rhs))); + } // namespace unordered + + using boost::unordered::unordered_flat_set; + + using boost::unordered::swap; + using boost::unordered::operator==; + using boost::unordered::operator!=; +} // namespace boost + +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 421dc4fe..200976ad 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -47,6 +47,7 @@ run unordered/equivalent_keys_tests.cpp ; run unordered/constructor_tests.cpp ; run unordered/copy_tests.cpp ; run unordered/move_tests.cpp ; +run unordered/post_move_tests.cpp ; run unordered/assign_tests.cpp ; run unordered/insert_tests.cpp ; run unordered/insert_stable_tests.cpp ; @@ -95,3 +96,55 @@ run exception/swap_exception_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD run exception/merge_exception_tests.cpp ; run quick.cpp ; + +import ../../config/checks/config : requires ; + +CPP11 = [ requires cxx11_constexpr cxx11_noexcept cxx11_decltype cxx11_alignas ] ; + +rule build_foa ( name ) +{ + run unordered/$(name).cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_$(name) ; +} + +build_foa fwd_set_test ; +build_foa fwd_map_test ; +build_foa compile_set ; +build_foa compile_map ; +build_foa noexcept_tests ; +run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_link_test ; +build_foa incomplete_test ; +build_foa simple_tests ; +build_foa equivalent_keys_tests ; +build_foa constructor_tests ; +build_foa copy_tests ; +build_foa move_tests ; +build_foa post_move_tests ; +build_foa assign_tests ; +build_foa insert_tests ; +build_foa insert_hint_tests ; +build_foa emplace_tests ; +build_foa erase_tests ; +build_foa merge_tests ; +build_foa find_tests ; +build_foa at_tests ; +build_foa load_factor_tests ; +build_foa rehash_tests ; +build_foa equality_tests ; +build_foa swap_tests ; +run unordered/scoped_allocator.cpp : : : $(CPP11) msvc-14.0:no BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ; +build_foa transparent_tests ; +build_foa reserve_tests ; +build_foa contains_tests ; +build_foa erase_if ; +build_foa scary_tests ; +build_foa init_type_insert_tests ; + +run exception/constructor_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ; +run exception/copy_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ; +run exception/assign_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ; +run exception/move_assign_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_move_assign_exception_tests ; +run exception/insert_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_insert_exception_tests ; +run exception/erase_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_erase_exception_tests ; +run exception/rehash_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ; +run exception/swap_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ; +run exception/merge_exception_tests.cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ; diff --git a/test/exception/containers.hpp b/test/exception/containers.hpp index c3b34be2..e6eb267b 100644 --- a/test/exception/containers.hpp +++ b/test/exception/containers.hpp @@ -1,17 +1,33 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../objects/exception.hpp" +#ifdef BOOST_UNORDERED_FOA_TESTS +typedef boost::unordered_flat_set > + test_set; + +typedef boost::unordered_flat_map > + test_map; + +typedef boost::unordered_flat_set< + std::pair, + test::exception::hash, test::exception::equal_to, + test::exception::allocator > + test_pair_set; + +#define CONTAINER_SEQ (test_set)(test_map) +#define CONTAINER_PAIR_SEQ (test_pair_set)(test_map) +#else typedef boost::unordered_set > @@ -42,3 +58,5 @@ typedef boost::unordered_multiset< #define CONTAINER_SEQ (test_set)(test_multiset)(test_map)(test_multimap) #define CONTAINER_PAIR_SEQ \ (test_pair_set)(test_pair_multiset)(test_map)(test_multimap) + +#endif diff --git a/test/exception/insert_exception_tests.cpp b/test/exception/insert_exception_tests.cpp index 0f98e0c4..27f95f7f 100644 --- a/test/exception/insert_exception_tests.cpp +++ b/test/exception/insert_exception_tests.cpp @@ -1,5 +1,6 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include "./containers.hpp" @@ -221,16 +222,41 @@ struct emplace_lvalue_pos_type } emplace_lvalue_pos; // Run the exception tests in various combinations. +using test::default_generator; +using test::limited_range; +using test::generate_collisions; +#ifdef BOOST_UNORDERED_FOA_TESTS +test_set* test_set_; +test_map* test_map_; + +// clang-format off +UNORDERED_TEST(insert_exception_test, + ((test_set_)(test_map_)) + ((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end) + (insert_lvalue_pos)(insert_single_item_range) + (emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end) + (emplace_lvalue_pos) + ) + ((default_generator)(limited_range)(generate_collisions)) +) + +UNORDERED_TEST(insert_rehash_exception_test, + ((test_set_)(test_map_)) + ((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end) + (insert_lvalue_pos)(insert_single_item_range) + (emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end) + (emplace_lvalue_pos) + ) + ((default_generator)(limited_range)(generate_collisions)) +) +// clang-format on +#else test_set* test_set_; test_multiset* test_multiset_; test_map* test_map_; test_multimap* test_multimap_; -using test::default_generator; -using test::limited_range; -using test::generate_collisions; - // clang-format off UNORDERED_TEST(insert_exception_test, ((test_set_)(test_multiset_)(test_map_)(test_multimap_)) @@ -252,6 +278,7 @@ UNORDERED_TEST(insert_rehash_exception_test, ((default_generator)(limited_range)(generate_collisions)) ) // clang-format on +#endif // Repeat insert tests with pairs @@ -259,8 +286,13 @@ struct pair_emplace_type : inserter_base { template void operator()(T& x, Iterator it) { +#ifdef BOOST_UNORDERED_FOA_TESTS + x.emplace(std::piecewise_construct, std::make_tuple(it->first), + std::make_tuple(it->second)); +#else x.emplace(boost::unordered::piecewise_construct, boost::make_tuple(it->first), boost::make_tuple(it->second)); +#endif } } pair_emplace; @@ -268,12 +300,34 @@ struct pair_emplace2_type : inserter_base { template void operator()(T& x, Iterator it) { +#ifdef BOOST_UNORDERED_FOA_TESTS + x.emplace_hint(x.begin(), std::piecewise_construct, + std::make_tuple(it->first), + std::make_tuple(it->second.tag1_, it->second.tag2_)); +#else x.emplace_hint(x.begin(), boost::unordered::piecewise_construct, boost::make_tuple(it->first), boost::make_tuple(it->second.tag1_, it->second.tag2_)); +#endif } } pair_emplace2; +#ifdef BOOST_UNORDERED_FOA_TESTS +test_pair_set* test_pair_set_; + +// clang-format off +UNORDERED_TEST(insert_exception_test, + ((test_pair_set_)(test_map_)) + ((pair_emplace)(pair_emplace2)) + ((default_generator)(limited_range)(generate_collisions)) +) +UNORDERED_TEST(insert_rehash_exception_test, + ((test_pair_set_)(test_map_)) + ((pair_emplace)(pair_emplace2)) + ((default_generator)(limited_range)(generate_collisions)) +) +// clang-format on +#else test_pair_set* test_pair_set_; test_pair_multiset* test_pair_multiset_; @@ -289,6 +343,7 @@ UNORDERED_TEST(insert_rehash_exception_test, ((default_generator)(limited_range)(generate_collisions)) ) // clang-format on +#endif // Test inserting using operator[] @@ -401,6 +456,19 @@ void insert_range_rehash_exception_test(T*, test::random_generator gen) } } +#ifdef BOOST_UNORDERED_FOA_TESTS +// clang-format off +UNORDERED_TEST(insert_range_exception_test, + ((test_set_)(test_map_)) + ((default_generator)(limited_range)(generate_collisions)) +) + +UNORDERED_TEST(insert_range_rehash_exception_test, + ((test_set_)(test_map_)) + ((default_generator)(limited_range)(generate_collisions)) +) +// clang-format on +#else // clang-format off UNORDERED_TEST(insert_range_exception_test, ((test_set_)(test_multiset_)(test_map_)(test_multimap_)) @@ -412,5 +480,6 @@ UNORDERED_TEST(insert_range_rehash_exception_test, ((default_generator)(limited_range)(generate_collisions)) ) // clang-format on +#endif RUN_TESTS() diff --git a/test/exception/merge_exception_tests.cpp b/test/exception/merge_exception_tests.cpp index 2cf7faf4..e824fdde 100644 --- a/test/exception/merge_exception_tests.cpp +++ b/test/exception/merge_exception_tests.cpp @@ -1,5 +1,6 @@ // Copyright 2017-2018 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -52,6 +53,53 @@ void merge_exception_test(T1 const*, T2 const*, std::size_t count12, int tag12, EXCEPTION_LOOP(merge_exception_test(x, y)) } +using test::default_generator; +using test::generate_collisions; +using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS +boost::unordered_flat_set >* test_set_; +boost::unordered_flat_map >* test_map_; + +// clang-format off +UNORDERED_MULTI_TEST(set_merge, merge_exception_test, + ((test_set_)) + ((test_set_)) + ((0x0000)(0x6400)(0x0064)(0x0a64)(0x3232)) + ((0x0000)(0x0001)(0x0102)) + ((default_generator)(limited_range)) + ((default_generator)(limited_range)) +) +UNORDERED_MULTI_TEST(map_merge, merge_exception_test, + ((test_map_)) + ((test_map_)) + ((0x0000)(0x6400)(0x0064)(0x0a64)(0x3232)) + ((0x0101)(0x0200)(0x0201)) + ((default_generator)(limited_range)) + ((default_generator)(limited_range)) +) +// Run fewer generate_collisions tests, as they're slow. +UNORDERED_MULTI_TEST(set_merge_collisions, merge_exception_test, + ((test_set_)) + ((test_set_)) + ((0x0a0a)) + ((0x0202)(0x0100)(0x0201)) + ((generate_collisions)) + ((generate_collisions)) +) +UNORDERED_MULTI_TEST(map_merge_collisions, merge_exception_test, + ((test_map_)) + ((test_map_)) + ((0x0a0a)) + ((0x0000)(0x0002)(0x0102)) + ((generate_collisions)) + ((generate_collisions)) +) +#else boost::unordered_set >* test_set_; @@ -65,10 +113,6 @@ boost::unordered_multimap >* test_multimap_; -using test::default_generator; -using test::generate_collisions; -using test::limited_range; - // clang-format off UNORDERED_MULTI_TEST(set_merge, merge_exception_test, ((test_set_)(test_multiset_)) @@ -104,5 +148,6 @@ UNORDERED_MULTI_TEST(map_merge_collisions, merge_exception_test, ((generate_collisions)) ) // clang-format on +#endif RUN_TESTS_QUIET() diff --git a/test/exception/swap_exception_tests.cpp b/test/exception/swap_exception_tests.cpp index 1569e814..545d607a 100644 --- a/test/exception/swap_exception_tests.cpp +++ b/test/exception/swap_exception_tests.cpp @@ -1,10 +1,15 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include "./containers.hpp" +#if defined(BOOST_UNORDERED_FOA_TESTS) +#define BOOST_UNORDERED_FOA_WEAK_GUARANTEE_SWAP_EXCEPTIONS_TESTS +#endif + #include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" #include "../helpers/tracker.hpp" diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index a555da60..7fbe25a5 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -1,5 +1,6 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -53,11 +54,22 @@ namespace test { if (test::has_unique_keys::value && count != 1) BOOST_ERROR("Non-unique key."); +#if !defined(BOOST_UNORDERED_FOA_WEAK_GUARANTEE_SWAP_EXCEPTIONS_TESTS) + // we conditionally compile this check because our FOA implementation only + // exhibits the weak guarantee when swapping throws + // + // in this case, the hasher may be changed before the predicate and the + // arrays are swapped in which case, we can can find an element by + // iteration but unfortunately, it's in the wrong slot according to the + // new hash function so count(key) can wind up returning nothing when + // there really is something if (x1.count(key) != count) { BOOST_ERROR("Incorrect output of count."); std::cerr << x1.count(key) << "," << count << "\n"; } +#endif +#ifndef BOOST_UNORDERED_FOA_TESTS // Check that the keys are in the correct bucket and are // adjacent in the bucket. typename X::size_type bucket = x1.bucket(key); @@ -86,6 +98,7 @@ namespace test { } } } +#endif }; // Check that size matches up. @@ -104,6 +117,7 @@ namespace test { if (fabs(x1.load_factor() - load_factor) > x1.load_factor() / 64) BOOST_ERROR("x1.load_factor() doesn't match actual load_factor."); +#ifndef BOOST_UNORDERED_FOA_TESTS // Check that size in the buckets matches up. typename X::size_type bucket_size = 0; @@ -120,6 +134,7 @@ namespace test { BOOST_ERROR("x1.size() doesn't match bucket size."); std::cout << x1.size() << "/" << bucket_size << std::endl; } +#endif } } diff --git a/test/helpers/unordered.hpp b/test/helpers/unordered.hpp new file mode 100644 index 00000000..a3af9452 --- /dev/null +++ b/test/helpers/unordered.hpp @@ -0,0 +1,22 @@ + +// Copyright 2022 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_UNORDERED_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_UNORDERED_HEADER + +// clang-format off +#include "prefix.hpp" +#ifdef BOOST_UNORDERED_FOA_TESTS +#include +#include +#include +#else +#include +#include +#endif +#include "postfix.hpp" +// clang-format on + +#endif diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index f5298265..cb1fe9b0 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -11,6 +11,7 @@ #if !defined(BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER) #define BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER +#include #include #include #include @@ -320,7 +321,7 @@ namespace test { bool operator!() const { return !ptr_; } static ptr pointer_to(T& p) { - return ptr(&p); + return ptr(boost::addressof(p)); } // I'm not using the safe bool idiom because the containers should be diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index fddb40c3..b000e42f 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/test.hpp" @@ -201,6 +197,54 @@ namespace assign_tests { } } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + + template bool is_propagate(T*) + { + return T::allocator_type::is_propagate_on_assign; + } + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map >* test_map_std_alloc; + + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map >* test_map; + + boost::unordered_flat_set >* + test_set_prop_assign; + boost::unordered_flat_map >* + test_map_prop_assign; + + boost::unordered_flat_set >* + test_set_no_prop_assign; + boost::unordered_flat_map >* + test_map_no_prop_assign; + + UNORDERED_AUTO_TEST (check_traits) { + BOOST_TEST(!is_propagate(test_set)); + BOOST_TEST(is_propagate(test_set_prop_assign)); + BOOST_TEST(!is_propagate(test_set_no_prop_assign)); + } + + UNORDERED_TEST(assign_tests1, + ((test_map_std_alloc)(test_set)(test_map)(test_set_prop_assign)(test_map_prop_assign)(test_set_no_prop_assign)(test_map_no_prop_assign))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(assign_tests2, + ((test_set)(test_map)(test_set_prop_assign)(test_map_prop_assign)(test_set_no_prop_assign)(test_map_no_prop_assign))( + (default_generator)(generate_collisions)(limited_range))) +#else boost::unordered_map >* test_map_std_alloc; @@ -241,15 +285,6 @@ namespace assign_tests { test::cxx11_allocator >* test_multimap_no_prop_assign; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - - template bool is_propagate(T*) - { - return T::allocator_type::is_propagate_on_assign; - } - UNORDERED_AUTO_TEST (check_traits) { BOOST_TEST(!is_propagate(test_set)); BOOST_TEST(is_propagate(test_set_prop_assign)); @@ -271,13 +306,18 @@ namespace assign_tests { test_set_no_prop_assign)(test_multiset_no_prop_assign)( test_map_no_prop_assign)(test_multimap_no_prop_assign))( (default_generator)(generate_collisions)(limited_range))) +#endif #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) UNORDERED_AUTO_TEST (assign_default_initializer_list) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Initializer List Tests\n"; std::initializer_list > init; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map x1; +#else boost::unordered_map x1; +#endif x1[25] = 3; x1[16] = 10; BOOST_TEST(!x1.empty()); @@ -291,7 +331,11 @@ namespace assign_tests { UNORDERED_AUTO_TEST (assign_initializer_list) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Initializer List Tests\n"; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set x; +#else boost::unordered_set x; +#endif x.insert(10); x.insert(20); x = {1, 2, -10}; diff --git a/test/unordered/at_tests.cpp b/test/unordered/at_tests.cpp index 25b0951a..b06dfddb 100644 --- a/test/unordered/at_tests.cpp +++ b/test/unordered/at_tests.cpp @@ -1,13 +1,10 @@ // Copyright 2007-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include @@ -17,8 +14,13 @@ namespace at_tests { UNORDERED_AUTO_TEST (at_tests) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Create Map" << std::endl; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map x; + boost::unordered_flat_map const& x_const(x); +#else boost::unordered_map x; boost::unordered_map const& x_const(x); +#endif BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Check empty container" << std::endl; diff --git a/test/unordered/compile_map.cpp b/test/unordered/compile_map.cpp index 6a84d595..c4c8d7ce 100644 --- a/test/unordered/compile_map.cpp +++ b/test/unordered/compile_map.cpp @@ -1,23 +1,41 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // This test creates the containers with members that meet their minimum // requirements. Makes sure everything compiles and is defined correctly. -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/minimal.hpp" #include "./compile_tests.hpp" // Explicit instantiation to catch compile-time errors +#ifdef BOOST_UNORDERED_FOA_TESTS +// emulates what was already done for previous tests but without leaking to +// the detail namespace +// +template +class instantiate_flat_map +{ + typedef boost::unordered_flat_map container; + container x; +}; + +template class instantiate_flat_map, + std::equal_to, test::minimal::allocator >; + +template class instantiate_flat_map, + test::minimal::equal_to, + test::minimal::allocator >; + +#else #define INSTANTIATE(type) \ template class boost::unordered::detail::instantiate_##type @@ -35,6 +53,7 @@ INSTANTIATE(multimap), test::minimal::equal_to, test::minimal::allocator >; +#endif UNORDERED_AUTO_TEST (test0) { test::minimal::constructor_param x; @@ -45,6 +64,19 @@ UNORDERED_AUTO_TEST (test0) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n"; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map int_map; + + boost::unordered_flat_map, std::equal_to, + test::minimal::cxx11_allocator > > + int_map2; + + boost::unordered_flat_map, + test::minimal::equal_to, + test::minimal::allocator > + map; +#else boost::unordered_map int_map; boost::unordered_map, std::equal_to, @@ -56,11 +88,13 @@ UNORDERED_AUTO_TEST (test0) { test::minimal::equal_to, test::minimal::allocator > map; +#endif container_test(int_map, std::pair(0, 0)); container_test(int_map2, std::pair(0, 0)); container_test(map, value); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n"; boost::unordered_multimap int_multimap; @@ -78,6 +112,7 @@ UNORDERED_AUTO_TEST (test0) { container_test(int_multimap, std::pair(0, 0)); container_test(int_multimap2, std::pair(0, 0)); container_test(multimap, value); +#endif } UNORDERED_AUTO_TEST (equality_tests) { @@ -85,6 +120,22 @@ UNORDERED_AUTO_TEST (equality_tests) { test::minimal::copy_constructible_equality_comparable> value_type; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map int_map; + + boost::unordered_flat_map, std::equal_to, + test::minimal::cxx11_allocator > > + int_map2; + + boost::unordered_flat_map< + test::minimal::copy_constructible_equality_comparable, + test::minimal::copy_constructible_equality_comparable, + test::minimal::hash, + test::minimal::equal_to< + test::minimal::copy_constructible_equality_comparable>, + test::minimal::allocator > + map; +#else boost::unordered_map int_map; boost::unordered_map, std::equal_to, @@ -98,11 +149,13 @@ UNORDERED_AUTO_TEST (equality_tests) { test::minimal::copy_constructible_equality_comparable>, test::minimal::allocator > map; +#endif equality_test(int_map); equality_test(int_map2); equality_test(map); +#ifndef BOOST_UNORDERED_FOA_TESTS boost::unordered_multimap int_multimap; boost::unordered_multimap, std::equal_to, @@ -121,6 +174,7 @@ UNORDERED_AUTO_TEST (equality_tests) { equality_test(int_multimap); equality_test(int_multimap2); equality_test(multimap); +#endif } UNORDERED_AUTO_TEST (test1) { @@ -131,11 +185,19 @@ UNORDERED_AUTO_TEST (test1) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n"; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map map; + + boost::unordered_flat_map, std::equal_to, + test::minimal::cxx11_allocator > > + map2; +#else boost::unordered_map map; boost::unordered_map, std::equal_to, test::minimal::cxx11_allocator > > map2; +#endif unordered_unique_test(map, map_value); unordered_map_test(map, value, value); @@ -147,6 +209,7 @@ UNORDERED_AUTO_TEST (test1) { unordered_copyable_test(map2, value, map_value, hash, equal_to); unordered_map_functions(map2, value, value); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n"; boost::unordered_multimap multimap; @@ -162,6 +225,7 @@ UNORDERED_AUTO_TEST (test1) { unordered_equivalent_test(multimap2, map_value); unordered_map_test(multimap2, value, value); unordered_copyable_test(multimap2, value, map_value, hash, equal_to); +#endif } UNORDERED_AUTO_TEST (test2) { @@ -178,28 +242,46 @@ UNORDERED_AUTO_TEST (test2) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n"; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map, + test::minimal::equal_to, + test::minimal::allocator > + map; +#else boost::unordered_map, test::minimal::equal_to, test::minimal::allocator > map; +#endif unordered_unique_test(map, map_value); unordered_map_test(map, assignable, assignable); unordered_copyable_test(map, assignable, map_value, hash, equal_to); unordered_map_member_test(map, map_value); +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map, + test::minimal::equal_to, + test::minimal::allocator > + map2; +#else boost::unordered_map, test::minimal::equal_to, test::minimal::allocator > map2; +#endif test::minimal::default_assignable default_assignable; unordered_map_functions(map2, assignable, default_assignable); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n"; boost::unordered_multimap x; + x.emplace(lwg2059_key(10), 5); + x.erase(x.begin()); +#else { boost::unordered_map x; x.emplace(lwg2059_key(10), 5); @@ -243,6 +331,7 @@ UNORDERED_AUTO_TEST (lwg2059) { x.emplace(lwg2059_key(10), 5); x.erase(x.begin()); } +#endif } RUN_TESTS() diff --git a/test/unordered/compile_set.cpp b/test/unordered/compile_set.cpp index 727294f3..aa3721c6 100644 --- a/test/unordered/compile_set.cpp +++ b/test/unordered/compile_set.cpp @@ -1,16 +1,13 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // This test creates the containers with members that meet their minimum // requirements. Makes sure everything compiles and is defined correctly. -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/minimal.hpp" @@ -18,6 +15,28 @@ // Explicit instantiation to catch compile-time errors +#ifdef BOOST_UNORDERED_FOA_TESTS + +// emulates what was already done for previous tests but without leaking to +// the detail namespace +// +template +class instantiate_flat_set +{ + typedef boost::unordered_flat_set container; + container x; +}; + +template class instantiate_flat_set, std::equal_to, + test::minimal::allocator >; + +template class instantiate_flat_set, + test::minimal::equal_to, + test::minimal::allocator >; + +#else + #define INSTANTIATE(type) \ template class boost::unordered::detail::instantiate_##type @@ -35,6 +54,21 @@ INSTANTIATE(multiset), test::minimal::allocator >; +#endif + +UNORDERED_AUTO_TEST (type_traits) { +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set set_type; +#else + typedef boost::unordered_set set_type; +#endif + + typedef set_type::iterator iterator; + + BOOST_STATIC_ASSERT(boost::is_same::reference>::value); +} + UNORDERED_AUTO_TEST (test0) { test::minimal::constructor_param x; @@ -42,6 +76,19 @@ UNORDERED_AUTO_TEST (test0) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set int_set; + + boost::unordered_flat_set, std::equal_to, + test::minimal::cxx11_allocator > + int_set2; + + boost::unordered_flat_set, + test::minimal::equal_to, + test::minimal::allocator > + set; +#else boost::unordered_set int_set; boost::unordered_set, std::equal_to, @@ -53,11 +100,13 @@ UNORDERED_AUTO_TEST (test0) { test::minimal::equal_to, test::minimal::allocator > set; +#endif container_test(int_set, 0); container_test(int_set2, 0); container_test(set, assignable); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset int_multiset; @@ -75,11 +124,27 @@ UNORDERED_AUTO_TEST (test0) { container_test(int_multiset, 0); container_test(int_multiset2, 0); container_test(multiset, assignable); +#endif } UNORDERED_AUTO_TEST (equality_tests) { typedef test::minimal::copy_constructible_equality_comparable value_type; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set int_set; + + boost::unordered_flat_set, std::equal_to, + test::minimal::cxx11_allocator > + int_set2; + + boost::unordered_flat_set< + test::minimal::copy_constructible_equality_comparable, + test::minimal::hash, + test::minimal::equal_to< + test::minimal::copy_constructible_equality_comparable>, + test::minimal::allocator > + set; +#else boost::unordered_set int_set; boost::unordered_set, std::equal_to, @@ -92,11 +157,13 @@ UNORDERED_AUTO_TEST (equality_tests) { test::minimal::copy_constructible_equality_comparable>, test::minimal::allocator > set; +#endif equality_test(int_set); equality_test(int_set2); equality_test(set); +#ifndef BOOST_UNORDERED_FOA_TESTS boost::unordered_multiset int_multiset; boost::unordered_multiset, std::equal_to, @@ -114,6 +181,7 @@ UNORDERED_AUTO_TEST (equality_tests) { equality_test(int_multiset); equality_test(int_multiset2); equality_test(multiset); +#endif } UNORDERED_AUTO_TEST (test1) { @@ -122,12 +190,19 @@ UNORDERED_AUTO_TEST (test1) { int value = 0; BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set." << std::endl; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set set; + boost::unordered_flat_set, std::equal_to, + test::minimal::cxx11_allocator > + set2; +#else boost::unordered_set set; boost::unordered_set, std::equal_to, test::minimal::cxx11_allocator > set2; +#endif unordered_unique_test(set, value); unordered_set_test(set, value); @@ -137,6 +212,7 @@ UNORDERED_AUTO_TEST (test1) { unordered_set_test(set2, value); unordered_copyable_test(set2, value, value, hash, equal_to); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset." << std::endl; boost::unordered_multiset multiset; @@ -152,6 +228,7 @@ UNORDERED_AUTO_TEST (test1) { unordered_equivalent_test(multiset2, value); unordered_set_test(multiset2, value); unordered_copyable_test(multiset2, value, value, hash, equal_to); +#endif } UNORDERED_AUTO_TEST (test2) { @@ -163,18 +240,26 @@ UNORDERED_AUTO_TEST (test2) { test::minimal::equal_to equal_to(x); BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; - +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set, + test::minimal::equal_to, + test::minimal::allocator > + set; +#else boost::unordered_set, test::minimal::equal_to, test::minimal::allocator > set; +#endif unordered_unique_test(set, assignable); unordered_set_test(set, assignable); unordered_copyable_test(set, assignable, assignable, hash, equal_to); unordered_set_member_test(set, assignable); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset equal_to(x); BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; - +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set, + test::minimal::equal_to, + test::minimal::allocator > + set; +#else boost::unordered_set, test::minimal::equal_to, test::minimal::allocator > set; +#endif // unordered_unique_test(set, movable1); unordered_set_test(set, movable1); unordered_movable_test(set, movable1, movable1, hash, equal_to); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset equal_to(x); BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; - +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set, + test::minimal::equal_to, + test::minimal::allocator > + set; +#else boost::unordered_set, test::minimal::equal_to, test::minimal::allocator > set; +#endif // unordered_unique_test(set, movable2); unordered_set_test(set, movable2); unordered_movable_test(set, movable2, movable2, hash, equal_to); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset equal_to(x); BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; - +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set, + test::minimal::equal_to > + set; +#else boost::unordered_set, test::minimal::equal_to > set; +#endif unordered_destructible_test(set); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset x; + x.emplace(lwg2059_key(10)); + x.erase(x.begin()); +#else { boost::unordered_set x; x.emplace(lwg2059_key(10)); @@ -308,6 +425,7 @@ UNORDERED_AUTO_TEST (lwg2059) { x.emplace(lwg2059_key(10)); x.erase(x.begin()); } +#endif } RUN_TESTS() diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index bb5256f8..e45a4570 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -1,5 +1,6 @@ // Copyright 2005-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -70,9 +71,19 @@ template void container_test(X& r, T const&) typedef typename X::reference reference; typedef typename X::const_reference const_reference; +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename X::node_type node_type; +#endif typedef typename X::allocator_type allocator_type; + typedef typename X::pointer pointer; + typedef typename X::const_pointer const_pointer; + + BOOST_STATIC_ASSERT((boost::is_same::type>::value)); + + BOOST_STATIC_ASSERT((boost::is_same::type>::value)); // value_type @@ -97,8 +108,10 @@ template void container_test(X& r, T const&) // node_type +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_STATIC_ASSERT(( boost::is_same::value)); +#endif // difference_type @@ -167,6 +180,7 @@ template void container_test(X& r, T const&) sink(X(rvalue(a_const), m)); X c3(rvalue(a_const), m); +#ifndef BOOST_UNORDERED_FOA_TESTS // node_type implicit_construct(); @@ -193,6 +207,7 @@ template void container_test(X& r, T const&) test::check_return_type::equals(n_const.empty()); TEST_NOEXCEPT_EXPR(!n_const); TEST_NOEXCEPT_EXPR(n_const.empty()); +#endif // Avoid unused variable warnings: @@ -262,24 +277,30 @@ template void unordered_set_test(X& r, Key const&) typedef typename X::iterator iterator; typedef typename X::const_iterator const_iterator; +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename X::local_iterator local_iterator; typedef typename X::const_local_iterator const_local_iterator; +#endif typedef typename std::iterator_traits::pointer iterator_pointer; typedef typename std::iterator_traits::pointer const_iterator_pointer; +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename std::iterator_traits::pointer local_iterator_pointer; typedef typename std::iterator_traits::pointer const_local_iterator_pointer; +#endif BOOST_STATIC_ASSERT( (boost::is_same::value)); BOOST_STATIC_ASSERT( (boost::is_same::value)); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_STATIC_ASSERT( (boost::is_same::value)); BOOST_STATIC_ASSERT( (boost::is_same::value)); +#endif // pointer_traits @@ -299,6 +320,8 @@ template void unordered_set_test(X& r, Key const&) BOOST_STATIC_ASSERT((boost::is_same::difference_type>::value)); + (void) r; +#ifndef BOOST_UNORDERED_FOA_TESTS // pointer_traits BOOST_STATIC_ASSERT((boost::is_same void unordered_set_test(X& r, Key const&) r.emplace(boost::move(k_lvalue)); node_type n1 = r.extract(r.begin()); test::check_return_type::equals_ref(n1.value()); +#endif } template @@ -345,23 +369,29 @@ void unordered_map_test(X& r, Key const& k, T const& v) typedef typename X::iterator iterator; typedef typename X::const_iterator const_iterator; +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename X::local_iterator local_iterator; typedef typename X::const_local_iterator const_local_iterator; +#endif typedef typename std::iterator_traits::pointer iterator_pointer; typedef typename std::iterator_traits::pointer const_iterator_pointer; +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename std::iterator_traits::pointer local_iterator_pointer; typedef typename std::iterator_traits::pointer const_local_iterator_pointer; +#endif BOOST_STATIC_ASSERT((boost::is_same::value)); BOOST_STATIC_ASSERT( (boost::is_same::value)); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_STATIC_ASSERT( (boost::is_same::value)); BOOST_STATIC_ASSERT( (boost::is_same::value)); +#endif // pointer_traits @@ -381,6 +411,7 @@ void unordered_map_test(X& r, Key const& k, T const& v) BOOST_STATIC_ASSERT((boost::is_same::difference_type>::value)); +#ifndef BOOST_UNORDERED_FOA_TESTS // pointer_traits BOOST_STATIC_ASSERT((boost::is_same::value)); // Superfluous,but just to make sure. BOOST_STATIC_ASSERT((!boost::is_const::value)); +#endif // Calling functions @@ -427,8 +459,12 @@ void unordered_map_test(X& r, Key const& k, T const& v) r.emplace(k_lvalue, v_lvalue); r.emplace(rvalue(k), rvalue(v)); +#ifdef BOOST_UNORDERED_FOA_TESTS + r.emplace(std::piecewise_construct, std::make_tuple(k), std::make_tuple(v)); +#else r.emplace(boost::unordered::piecewise_construct, boost::make_tuple(k), boost::make_tuple(v)); +#endif // Emplace with hint @@ -436,9 +472,15 @@ void unordered_map_test(X& r, Key const& k, T const& v) r.emplace_hint(r.begin(), k_lvalue, v_lvalue); r.emplace_hint(r.begin(), rvalue(k), rvalue(v)); +#ifdef BOOST_UNORDERED_FOA_TESTS + r.emplace_hint(r.begin(), std::piecewise_construct, std::make_tuple(k), + std::make_tuple(v)); +#else r.emplace_hint(r.begin(), boost::unordered::piecewise_construct, boost::make_tuple(k), boost::make_tuple(v)); +#endif +#ifndef BOOST_UNORDERED_FOA_TESTS // Extract test::check_return_type::equals(r.extract(r.begin())); @@ -461,6 +503,7 @@ void unordered_map_test(X& r, Key const& k, T const& v) node_type n = r.extract(r.begin()); test::check_return_type::equals_ref(n.key()); test::check_return_type::equals_ref(n.mapped()); +#endif } template void equality_test(X& r) @@ -479,6 +522,7 @@ template void unordered_unique_test(X& r, T const& t) test::check_return_type >::equals(r.insert(t)); test::check_return_type >::equals(r.emplace(t)); +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename X::node_type node_type; typedef typename X::insert_return_type insert_return_type; @@ -503,6 +547,7 @@ template void unordered_unique_test(X& r, T const& t) test::check_return_type::equals(insert_return.position); test::check_return_type::equals_ref(insert_return.node); boost::swap(insert_return, insert_return2); +#endif } template void unordered_equivalent_test(X& r, T const& t) @@ -554,6 +599,7 @@ void unordered_test(X& x, Key& k, Hash& hf, Pred& eq) typedef typename X::iterator iterator; typedef typename X::const_iterator const_iterator; +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename X::local_iterator local_iterator; typedef typename X::const_local_iterator const_local_iterator; @@ -590,6 +636,7 @@ void unordered_test(X& x, Key& k, Hash& hf, Pred& eq) const_local_iterator_pointer; typedef typename std::iterator_traits::reference const_local_iterator_reference; +#endif typedef typename X::allocator_type allocator_type; BOOST_STATIC_ASSERT((boost::is_same::value)); @@ -602,6 +649,7 @@ void unordered_test(X& x, Key& k, Hash& hf, Pred& eq) BOOST_STATIC_ASSERT((boost::is_same::value)); test::check_return_type::convertible(eq(k, k)); +#ifndef BOOST_UNORDERED_FOA_TESTS boost::function_requires >(); BOOST_STATIC_ASSERT( (boost::is_same::value)); @@ -622,6 +670,7 @@ void unordered_test(X& x, Key& k, Hash& hf, Pred& eq) const_iterator_pointer>::value)); BOOST_STATIC_ASSERT((boost::is_same::value)); +#endif X a; allocator_type m = a.get_allocator(); @@ -667,6 +716,7 @@ void unordered_test(X& x, Key& k, Hash& hf, Pred& eq) test::check_return_type >::equals( b.equal_range(k)); test::check_return_type::equals(b.bucket_count()); +#ifndef BOOST_UNORDERED_FOA_TESTS test::check_return_type::equals(b.max_bucket_count()); test::check_return_type::equals(b.bucket(k)); test::check_return_type::equals(b.bucket_size(0)); @@ -680,6 +730,7 @@ void unordered_test(X& x, Key& k, Hash& hf, Pred& eq) test::check_return_type::equals(b.cbegin(0)); test::check_return_type::equals(a.cend(0)); test::check_return_type::equals(b.cend(0)); +#endif test::check_return_type::equals(b.load_factor()); test::check_return_type::equals(b.max_load_factor()); @@ -792,7 +843,11 @@ void unordered_copyable_test(X& x, Key& k, T& t, Hash& hf, Pred& eq) X a10; a10.insert(t); q = a10.cbegin(); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_STATIC_ASSERT(std::is_same::value); +#else test::check_return_type::equals(a10.erase(q)); +#endif // Avoid unused variable warnings: @@ -807,10 +862,12 @@ void unordered_copyable_test(X& x, Key& k, T& t, Hash& hf, Pred& eq) sink(a7a); sink(a9a); +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename X::node_type node_type; typedef typename X::allocator_type allocator_type; node_type const n_const = a.extract(a.begin()); test::check_return_type::equals(n_const.get_allocator()); +#endif } template @@ -879,7 +936,11 @@ void unordered_movable_test(X& x, Key& k, T& /* t */, Hash& hf, Pred& eq) T v5(v); a10.insert(boost::move(v5)); q = a10.cbegin(); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_STATIC_ASSERT(std::is_same::value); +#else test::check_return_type::equals(a10.erase(q)); +#endif // Avoid unused variable warnings: diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index c2a43283..b68a7eb5 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2010 Daniel James. +// Copyright (C) 2022 Christian Mazakas // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/test.hpp" @@ -497,6 +493,35 @@ namespace constructor_tests { test::check_equivalent_keys(x); } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map >* test_map_std_alloc; + + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map >* test_map; + + UNORDERED_TEST(constructor_tests1, + ((test_map_std_alloc)(test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(constructor_tests2, + ((test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(map_constructor_test, + ((test_map_std_alloc)(test_map))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(no_alloc_default_construct_test, + ((test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) +#else boost::unordered_map >* test_map_std_alloc; @@ -509,10 +534,6 @@ namespace constructor_tests { boost::unordered_multimap >* test_multimap; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - UNORDERED_TEST(constructor_tests1, ((test_map_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) @@ -528,12 +549,17 @@ namespace constructor_tests { UNORDERED_TEST(no_alloc_default_construct_test, ((test_set)(test_multiset)(test_map)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) +#endif #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) UNORDERED_AUTO_TEST (test_default_initializer_list) { std::initializer_list init; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set x1 = init; +#else boost::unordered_set x1 = init; +#endif BOOST_TEST(x1.empty()); } @@ -542,7 +568,12 @@ namespace constructor_tests { #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) UNORDERED_AUTO_TEST (test_initializer_list) { +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set x1 = {2, 10, 45, -5}; +#else boost::unordered_set x1 = {2, 10, 45, -5}; +#endif + BOOST_TEST(x1.find(10) != x1.end()); BOOST_TEST(x1.find(46) == x1.end()); } diff --git a/test/unordered/contains_tests.cpp b/test/unordered/contains_tests.cpp index 01654479..870de2a3 100644 --- a/test/unordered/contains_tests.cpp +++ b/test/unordered/contains_tests.cpp @@ -1,13 +1,8 @@ -// Copyright 2021 Christian Mazakas. +// Copyright 2021-2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" @@ -129,6 +124,20 @@ template void test_map_non_transparent_contains() void test_map() { +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_map + transparent_map; + + typedef boost::unordered_flat_map + non_transparent_map1; + + typedef boost::unordered_flat_map + non_transparent_map2; + + typedef boost::unordered_flat_map + non_transparent_map3; +#else typedef boost::unordered_map transparent_map; @@ -141,6 +150,7 @@ void test_map() typedef boost::unordered_map non_transparent_map3; +#endif test_map_transparent_contains(); test_map_non_transparent_contains(); @@ -148,6 +158,7 @@ void test_map() test_map_non_transparent_contains(); } +#ifndef BOOST_UNORDERED_FOA_TESTS void test_multimap() { typedef boost::unordered_multimap(); test_map_non_transparent_contains(); } +#endif template void test_set_transparent_contains() { @@ -231,6 +243,18 @@ template void test_set_non_transparent_contains() void test_set() { +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set + transparent_set; + + typedef boost::unordered_flat_set + non_transparent_set1; + typedef boost::unordered_flat_set + non_transparent_set2; + typedef boost::unordered_flat_set + non_transparent_set3; +#else typedef boost::unordered_set transparent_set; @@ -239,6 +263,7 @@ void test_set() typedef boost::unordered_set non_transparent_set2; typedef boost::unordered_set non_transparent_set3; +#endif test_set_transparent_contains(); test_set_non_transparent_contains(); @@ -246,6 +271,7 @@ void test_set() test_set_non_transparent_contains(); } +#ifndef BOOST_UNORDERED_FOA_TESTS void test_multiset() { typedef boost::unordered_multiset(); test_set_non_transparent_contains(); } +#endif UNORDERED_AUTO_TEST (contains_) { // avoid -Wshadow warning with `bool contains` test_map(); - test_multimap(); test_set(); +#ifndef BOOST_UNORDERED_FOA_TESTS + test_multimap(); + test_multiset(); +#endif } RUN_TESTS() diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index f7c0ddd8..05c0f97a 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2009 Daniel James. +// Copyright (C) 2022 Christian Mazakas // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/test.hpp" @@ -229,6 +225,38 @@ namespace copy_tests { } } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map >* test_map; + + boost::unordered_flat_set >* + test_set_select_copy; + boost::unordered_flat_map >* + test_map_select_copy; + + boost::unordered_flat_set >* + test_set_no_select_copy; + boost::unordered_flat_map >* + test_map_no_select_copy; + + UNORDERED_TEST(copy_construct_tests1, + ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(copy_construct_tests2, + ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))( + (default_generator)(generate_collisions)(limited_range))) +#else boost::unordered_set >* test_set; boost::unordered_multiset >* test_multimap_no_select_copy; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - UNORDERED_TEST(copy_construct_tests1, - ((test_set)(test_multiset)(test_map)(test_multimap)(test_set_select_copy)( - test_multiset_select_copy)(test_map_select_copy)( - test_multimap_select_copy)(test_set_no_select_copy)( - test_multiset_no_select_copy)(test_map_no_select_copy)( - test_multimap_no_select_copy))( + ((test_set)(test_multiset)(test_map)(test_multimap)(test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy)(test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy))( (default_generator)(generate_collisions)(limited_range))) UNORDERED_TEST(copy_construct_tests2, - ((test_set)(test_multiset)(test_map)(test_multimap)(test_set_select_copy)( - test_multiset_select_copy)(test_map_select_copy)( - test_multimap_select_copy)(test_set_no_select_copy)( - test_multiset_no_select_copy)(test_map_no_select_copy)( - test_multimap_no_select_copy))( + ((test_set)(test_multiset)(test_map)(test_multimap)(test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy)(test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy))( (default_generator)(generate_collisions)(limited_range))) -} +#endif +} // namespace copy_tests RUN_TESTS() diff --git a/test/unordered/emplace_tests.cpp b/test/unordered/emplace_tests.cpp index 7f7a5d71..60aceb32 100644 --- a/test/unordered/emplace_tests.cpp +++ b/test/unordered/emplace_tests.cpp @@ -1,14 +1,10 @@ // // Copyright 2016 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include #include "../helpers/test.hpp" @@ -46,47 +42,56 @@ namespace emplace_tests { A8 a8; A9 a9; - emplace_value(A0 const& b0, A1 const& b1) : arg_count(2), a0(b0), a1(b1) {} + emplace_value(A0 const& b0, A1 const& b1) + : arg_count(2), a0(b0), a1(b1), a2('\0'), a3(-1), a4(-1), a5(-1), + a6(-1), a7(-1), a8(-1), a9(-1) + { + } emplace_value(A0 const& b0, A1 const& b1, A2 const& b2) - : arg_count(3), a0(b0), a1(b1), a2(b2) + : arg_count(3), a0(b0), a1(b1), a2(b2), a3(-1), a4(-1), a5(-1), a6(-1), + a7(-1), a8(-1), a9(-1) { } emplace_value(A0 const& b0, A1 const& b1, A2 const& b2, A3 const& b3) - : arg_count(4), a0(b0), a1(b1), a2(b2), a3(b3) + : arg_count(4), a0(b0), a1(b1), a2(b2), a3(b3), a4(-1), a5(-1), a6(-1), + a7(-1), a8(-1), a9(-1) { } emplace_value( A0 const& b0, A1 const& b1, A2 const& b2, A3 const& b3, A4 const& b4) - : arg_count(5), a0(b0), a1(b1), a2(b2), a3(b3), a4(b4) + : arg_count(5), a0(b0), a1(b1), a2(b2), a3(b3), a4(b4), a5(-1), a6(-1), + a7(-1), a8(-1), a9(-1) { } emplace_value(A0 const& b0, A1 const& b1, A2 const& b2, A3 const& b3, A4 const& b4, A5 const& b5) - : arg_count(6), a0(b0), a1(b1), a2(b2), a3(b3), a4(b4), a5(b5) + : arg_count(6), a0(b0), a1(b1), a2(b2), a3(b3), a4(b4), a5(b5), a6(-1), + a7(-1), a8(-1), a9(-1) { } emplace_value(A0 const& b0, A1 const& b1, A2 const& b2, A3 const& b3, A4 const& b4, A5 const& b5, A6 const& b6) - : arg_count(7), a0(b0), a1(b1), a2(b2), a3(b3), a4(b4), a5(b5), a6(b6) + : arg_count(7), a0(b0), a1(b1), a2(b2), a3(b3), a4(b4), a5(b5), a6(b6), + a7(-1), a8(-1), a9(-1) { } emplace_value(A0 const& b0, A1 const& b1, A2 const& b2, A3 const& b3, A4 const& b4, A5 const& b5, A6 const& b6, A7 const& b7) : arg_count(8), a0(b0), a1(b1), a2(b2), a3(b3), a4(b4), a5(b5), a6(b6), - a7(b7) + a7(b7), a8(-1), a9(-1) { } emplace_value(A0 const& b0, A1 const& b1, A2 const& b2, A3 const& b3, A4 const& b4, A5 const& b5, A6 const& b6, A7 const& b7, A8 const& b8) : arg_count(9), a0(b0), a1(b1), a2(b2), a3(b3), a4(b4), a5(b5), a6(b6), - a7(b7), a8(b8) + a7(b7), a8(b8), a9(-1) { } @@ -162,16 +167,29 @@ namespace emplace_tests { return true; } +#ifdef BOOST_UNORDERED_FOA_TESTS + emplace_value() = delete; + emplace_value(emplace_value const&) = default; + emplace_value(emplace_value&&) = default; +#else private: emplace_value(); emplace_value(emplace_value const&); +#endif + }; UNORDERED_AUTO_TEST (emplace_set) { test::check_instances check_; +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set > + container; +#else typedef boost::unordered_set > container; +#endif typedef container::iterator iterator; typedef std::pair return_type; container x(10); @@ -187,7 +205,15 @@ namespace emplace_tests { BOOST_TEST(*r1.first == v1); BOOST_TEST(r1.first == x.find(v1)); BOOST_TEST_EQ(check_.instances(), 2); +#ifdef BOOST_UNORDERED_FOA_TESTS + // 1 from v1 + // 1 from constructing the value_type() so we can pluck the key + // 1 from move-constructing which invokes the counted_object base + // constructor + BOOST_TEST_EQ(check_.constructions(), 3); +#else BOOST_TEST_EQ(check_.constructions(), 2); +#endif // 3 args @@ -198,7 +224,11 @@ namespace emplace_tests { BOOST_TEST(*r1.first == v2); BOOST_TEST(r1.first == x.find(v2)); BOOST_TEST_EQ(check_.instances(), 4); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST_EQ(check_.constructions(), 6); +#else BOOST_TEST_EQ(check_.constructions(), 4); +#endif // 7 args with hint + duplicate @@ -208,7 +238,11 @@ namespace emplace_tests { BOOST_TEST(*i1 == v3); BOOST_TEST(i1 == x.find(v3)); BOOST_TEST_EQ(check_.instances(), 6); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST_EQ(check_.constructions(), 9); +#else BOOST_TEST_EQ(check_.constructions(), 6); +#endif r2 = x.emplace(25, "something", 'z', 4, 5, 6, 7); BOOST_TEST_EQ(x.size(), 3u); @@ -218,7 +252,11 @@ namespace emplace_tests { // whether it can emplace, so there's an extra construction // here. BOOST_TEST_EQ(check_.instances(), 6); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST_EQ(check_.constructions(), 10); +#else BOOST_TEST_EQ(check_.constructions(), 7); +#endif // 10 args + hint duplicate @@ -230,7 +268,11 @@ namespace emplace_tests { BOOST_TEST(*r1.first == v4); BOOST_TEST(r1.first == x.find(v4)); BOOST_TEST_EQ(check_.instances(), 8); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST_EQ(check_.constructions(), 13); +#else BOOST_TEST_EQ(check_.constructions(), 9); +#endif BOOST_TEST( r1.first == x.emplace_hint(r1.first, 10, "", 'a', 4, 5, 6, 7, 8, 9, 10)); @@ -239,7 +281,11 @@ namespace emplace_tests { BOOST_TEST( r1.first == x.emplace_hint(x.end(), 10, "", 'a', 4, 5, 6, 7, 8, 9, 10)); BOOST_TEST_EQ(check_.instances(), 8); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST_EQ(check_.constructions(), 16); +#else BOOST_TEST_EQ(check_.constructions(), 12); +#endif BOOST_TEST_EQ(x.size(), 4u); BOOST_TEST(x.count(v1) == 1); @@ -248,6 +294,7 @@ namespace emplace_tests { BOOST_TEST(x.count(v4) == 1); } +#ifndef BOOST_UNORDERED_FOA_TESTS UNORDERED_AUTO_TEST (emplace_multiset) { test::check_instances check_; @@ -325,20 +372,79 @@ namespace emplace_tests { BOOST_TEST_EQ(x.count(v2), 2u); BOOST_TEST_EQ(x.count(v3), 2u); } +#endif UNORDERED_AUTO_TEST (emplace_map) { test::check_instances check_; - +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_map > + container; +#else typedef boost::unordered_map > container; +#endif typedef container::iterator iterator; typedef std::pair return_type; container x(10); return_type r1, r2; +#ifdef BOOST_UNORDERED_FOA_TESTS // 5/8 args + duplicate + emplace_value k1(5, "", 'b', 4, 5); + emplace_value m1(8, "xxx", 'z', 4, 5, 6, 7, 8); + r1 = x.emplace(std::piecewise_construct, std::make_tuple(5, "", 'b', 4, 5), + std::make_tuple(8, "xxx", 'z', 4, 5, 6, 7, 8)); + BOOST_TEST_EQ(x.size(), 1u); + BOOST_TEST(r1.second); + BOOST_TEST(x.find(k1) == r1.first); + BOOST_TEST(x.find(k1)->second == m1); + BOOST_TEST_EQ(check_.instances(), 4); + BOOST_TEST_EQ(check_.constructions(), 6); + r2 = x.emplace(std::piecewise_construct, std::make_tuple(5, "", 'b', 4, 5), + std::make_tuple(8, "xxx", 'z', 4, 5, 6, 7, 8)); + BOOST_TEST_EQ(x.size(), 1u); + BOOST_TEST(!r2.second); + BOOST_TEST(r1.first == r2.first); + BOOST_TEST(x.find(k1)->second == m1); + BOOST_TEST_EQ(check_.instances(), 4); + // constructions could possibly be 5 if the implementation only + // constructed the key. + BOOST_TEST_EQ(check_.constructions(), 8); + + // 9/3 args + duplicates with hints, different mapped value. + + emplace_value k2(9, "", 'b', 4, 5, 6, 7, 8, 9); + emplace_value m2(3, "aaa", 'm'); + r1 = x.emplace(std::piecewise_construct, + std::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9), + std::make_tuple(3, "aaa", 'm')); + BOOST_TEST_EQ(x.size(), 2u); + BOOST_TEST(r1.second); + BOOST_TEST(r1.first->first.arg_count == 9); + BOOST_TEST(r1.first->second.arg_count == 3); + BOOST_TEST(x.find(k2) == r1.first); + BOOST_TEST(x.find(k2)->second == m2); + BOOST_TEST_EQ(check_.instances(), 8); + BOOST_TEST_EQ(check_.constructions(), 14); + + BOOST_TEST(r1.first == x.emplace_hint(r1.first, std::piecewise_construct, + std::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9), + std::make_tuple(15, "jkjk"))); + BOOST_TEST(r1.first == x.emplace_hint(r2.first, std::piecewise_construct, + std::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9), + std::make_tuple(275, "xxx", 'm', 6))); + BOOST_TEST(r1.first == x.emplace_hint(x.end(), std::piecewise_construct, + std::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9), + std::make_tuple(-10, "blah blah", '\0'))); + BOOST_TEST_EQ(x.size(), 2u); + BOOST_TEST(x.find(k2)->second == m2); + BOOST_TEST_EQ(check_.instances(), 8); + BOOST_TEST_EQ(check_.constructions(), 20); +#else + // 5/8 args + duplicate emplace_value k1(5, "", 'b', 4, 5); emplace_value m1(8, "xxx", 'z', 4, 5, 6, 7, 8); r1 = x.emplace(boost::unordered::piecewise_construct, @@ -395,8 +501,10 @@ namespace emplace_tests { BOOST_TEST(x.find(k2)->second == m2); BOOST_TEST_EQ(check_.instances(), 8); BOOST_TEST_EQ(check_.constructions(), 16); +#endif } +#ifndef BOOST_UNORDERED_FOA_TESTS UNORDERED_AUTO_TEST (emplace_multimap) { test::check_instances check_; @@ -461,11 +569,15 @@ namespace emplace_tests { BOOST_TEST_EQ(check_.instances(), 20); BOOST_TEST_EQ(check_.constructions(), 20); } +#endif UNORDERED_AUTO_TEST (try_emplace) { test::check_instances check_; - +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_map container; +#else typedef boost::unordered_map container; +#endif typedef container::iterator iterator; typedef std::pair return_type; container x(10); diff --git a/test/unordered/equality_tests.cpp b/test/unordered/equality_tests.cpp index b1029af4..d10d07f2 100644 --- a/test/unordered/equality_tests.cpp +++ b/test/unordered/equality_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2008-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include #include @@ -30,14 +26,27 @@ namespace equality_tests { } }; -#define UNORDERED_EQUALITY_SET_TEST(seq1, op, seq2) \ +#ifdef BOOST_UNORDERED_FOA_TESTS + using boost_unordered_set = + boost::unordered_flat_set; + + using boost_unordered_map = + boost::unordered_flat_map; + +#define UNORDERED_EQUALITY_MULTISET_TEST(seq1, op, seq2) \ { \ - boost::unordered_set set1, set2; \ - BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set1, seq1) \ - BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set2, seq2) \ - BOOST_TEST(set1 op set2); \ } +#define UNORDERED_EQUALITY_MULTIMAP_TEST(seq1, op, seq2) \ + { \ + } +#else + typedef boost::unordered_set + boost_unordered_set; + + typedef boost::unordered_map + boost_unordered_map; + #define UNORDERED_EQUALITY_MULTISET_TEST(seq1, op, seq2) \ { \ boost::unordered_multiset set1, set2; \ @@ -46,17 +55,26 @@ namespace equality_tests { BOOST_TEST(set1 op set2); \ } -#define UNORDERED_EQUALITY_MAP_TEST(seq1, op, seq2) \ +#define UNORDERED_EQUALITY_MULTIMAP_TEST(seq1, op, seq2) \ { \ - boost::unordered_map map1, map2; \ + boost::unordered_multimap map1, map2; \ BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map1, seq1) \ BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map2, seq2) \ BOOST_TEST(map1 op map2); \ } +#endif -#define UNORDERED_EQUALITY_MULTIMAP_TEST(seq1, op, seq2) \ +#define UNORDERED_EQUALITY_SET_TEST(seq1, op, seq2) \ { \ - boost::unordered_multimap map1, map2; \ + boost_unordered_set set1, set2; \ + BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set1, seq1) \ + BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set2, seq2) \ + BOOST_TEST(set1 op set2); \ + } + +#define UNORDERED_EQUALITY_MAP_TEST(seq1, op, seq2) \ + { \ + boost_unordered_map map1, map2; \ BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map1, seq1) \ BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map2, seq2) \ BOOST_TEST(map1 op map2); \ @@ -67,7 +85,11 @@ namespace equality_tests { map.insert(std::pair BOOST_PP_SEQ_TO_TUPLE(item)); UNORDERED_AUTO_TEST (equality_size_tests) { +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set x1, x2; +#else boost::unordered_set x1, x2; +#endif BOOST_TEST(x1 == x2); BOOST_TEST(!(x1 != x2)); @@ -134,7 +156,7 @@ namespace equality_tests { // different hash functions but the same equality predicate. UNORDERED_AUTO_TEST (equality_different_hash_test) { - typedef boost::unordered_set set; + typedef boost_unordered_set set; set set1(0, mod_compare(false), mod_compare(false)); set set2(0, mod_compare(true), mod_compare(true)); BOOST_TEST(set1 == set2); diff --git a/test/unordered/equivalent_keys_tests.cpp b/test/unordered/equivalent_keys_tests.cpp index f040819d..b279a8c6 100644 --- a/test/unordered/equivalent_keys_tests.cpp +++ b/test/unordered/equivalent_keys_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include @@ -38,8 +34,11 @@ void test_equal_insertion(Iterator begin, Iterator end) UNORDERED_AUTO_TEST (set_tests) { int values[][5] = {{1}, {54, 23}, {-13, 65}, {77, 77}, {986, 25, 986}}; +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set set; +#else typedef boost::unordered_set set; - typedef boost::unordered_multiset multiset; +#endif test_equal_insertion(values[0], values[0] + 1); test_equal_insertion(values[1], values[1] + 2); @@ -47,6 +46,7 @@ UNORDERED_AUTO_TEST (set_tests) { test_equal_insertion(values[3], values[3] + 2); test_equal_insertion(values[4], values[4] + 3); + typedef boost::unordered_multiset multiset; test_equal_insertion(values[0], values[0] + 1); test_equal_insertion(values[1], values[1] + 2); test_equal_insertion(values[2], values[2] + 2); @@ -66,12 +66,17 @@ UNORDERED_AUTO_TEST (map_tests) { v[2].push_back(std::pair(432, 24)); for (int i = 0; i < 5; ++i) +#ifdef BOOST_UNORDERED_FOA_TESTS + test_equal_insertion >( + v[i].begin(), v[i].end()); +#else test_equal_insertion >( v[i].begin(), v[i].end()); for (int i2 = 0; i2 < 5; ++i2) test_equal_insertion >( v[i2].begin(), v[i2].end()); +#endif } RUN_TESTS() diff --git a/test/unordered/erase_if.cpp b/test/unordered/erase_if.cpp index fcea06fa..6033b752 100644 --- a/test/unordered/erase_if.cpp +++ b/test/unordered/erase_if.cpp @@ -2,12 +2,7 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" @@ -23,36 +18,13 @@ namespace test { struct is_even { - is_even() {} - -#if BOOST_CXX_VERSION >= 201703L - // immovable for C++17 - is_even(is_even const&) = delete; - is_even(is_even&&) = delete; - - is_even& operator=(is_even const&) = delete; - is_even& operator=(is_even&&) = delete; -#elif BOOST_CXX_VERSION >= 201103L - // move-only for C++11 - is_even(is_even const&) = delete; - is_even(is_even&&) = default; - - is_even& operator=(is_even const&) = delete; - is_even& operator=(is_even&&) = default; -#else - // copyable otherwise - is_even(is_even const&) {} - is_even& operator=(is_even const&) { return *this; } -#endif - - bool operator()( - std::pair& key_value) UNORDERED_LVALUE_QUAL + bool operator()(std::pair& key_value) { int const v = key_value.second; return (v % 2 == 0); } - bool operator()(int const& value) UNORDERED_LVALUE_QUAL + bool operator()(int const& value) { int const v = value; return (v % 2 == 0); @@ -61,37 +33,13 @@ namespace test { struct is_too_large { - is_too_large() {} - -#if BOOST_CXX_VERSION >= 201703L - // immovable for C++17 - is_too_large(is_too_large const&) = delete; - is_too_large(is_too_large&&) = delete; - - is_too_large& operator=(is_too_large const&) = delete; - is_too_large& operator=(is_too_large&&) = delete; -#elif BOOST_CXX_VERSION >= 201103L - // move-only for C++11 - is_too_large(is_too_large const&) = delete; - is_too_large(is_too_large&&) = default; - - is_too_large& operator=(is_too_large const&) = delete; - is_too_large& operator=(is_too_large&&) = default; -#else - // copyable otherwise - is_too_large(is_too_large const&) {} - - is_too_large& operator=(is_too_large const&) { return *this; } -#endif - - bool operator()( - std::pair& key_value) UNORDERED_LVALUE_QUAL + bool operator()(std::pair& key_value) { int const v = key_value.second; return v >= 1000; } - bool operator()(int const& value) UNORDERED_LVALUE_QUAL + bool operator()(int const& value) { int const v = value; return v >= 1000; @@ -159,11 +107,16 @@ template void test_set_erase_if() } UNORDERED_AUTO_TEST (unordered_erase_if) { +#ifdef BOOST_UNORDERED_FOA_TESTS + test_map_erase_if >(); + test_set_erase_if >(); +#else test_map_erase_if >(); test_map_erase_if >(); test_set_erase_if >(); test_set_erase_if >(); +#endif } RUN_TESTS() diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index fecb7dc0..d030c6aa 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/test.hpp" @@ -27,7 +23,9 @@ namespace erase_tests { template void erase_tests1(Container*, test::random_generator generator) { +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename Container::iterator iterator; +#endif typedef typename Container::const_iterator c_iterator; BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Erase by key.\n"; @@ -61,9 +59,13 @@ namespace erase_tests { while (size > 0 && !x.empty()) { typename Container::key_type key = test::get_key(*x.begin()); std::size_t count = x.count(key); +#ifdef BOOST_UNORDERED_FOA_TESTS + x.erase(x.begin()); +#else iterator pos = x.erase(x.begin()); - --size; BOOST_TEST(pos == x.begin()); +#endif + --size; BOOST_TEST(x.count(key) == count - 1); BOOST_TEST(x.size() == size); if (++iterations % 20 == 0) @@ -93,10 +95,15 @@ namespace erase_tests { typename Container::key_type key = test::get_key(*pos); std::size_t count = x.count(key); BOOST_TEST(count > 0); +#ifdef BOOST_UNORDERED_FOA_TESTS + x.erase(pos); + --size; +#else BOOST_TEST(next == x.erase(pos)); --size; if (size > 0) BOOST_TEST(index == 0 ? next == x.begin() : next == test::next(prev)); +#endif BOOST_TEST(x.count(key) == count - 1); if (x.count(key) != count - 1) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << count << " => " << x.count(key) @@ -185,7 +192,11 @@ namespace erase_tests { while (size > 0 && !x.empty()) { typename Container::key_type key = test::get_key(*x.begin()); std::size_t count = x.count(key); +#ifdef BOOST_UNORDERED_FOA_TESTS + x.erase(x.begin()); +#else x.quick_erase(x.begin()); +#endif --size; BOOST_TEST(x.count(key) == count - 1); BOOST_TEST(x.size() == size); @@ -216,7 +227,11 @@ namespace erase_tests { typename Container::key_type key = test::get_key(*pos); std::size_t count = x.count(key); BOOST_TEST(count > 0); +#ifdef BOOST_UNORDERED_FOA_TESTS + x.erase(pos); +#else x.quick_erase(pos); +#endif --size; if (size > 0) BOOST_TEST(index == 0 ? next == x.begin() : next == test::next(prev)); @@ -246,6 +261,20 @@ namespace erase_tests { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\n"; } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map >* test_map; + + UNORDERED_TEST( + erase_tests1, ((test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) +#else boost::unordered_set >* test_set; boost::unordered_multiset >* test_multimap; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; UNORDERED_TEST( erase_tests1, ((test_set)(test_multiset)(test_map)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) +#endif } RUN_TESTS() diff --git a/test/unordered/find_tests.cpp b/test/unordered/find_tests.cpp index 031371c3..50dfd79e 100644 --- a/test/unordered/find_tests.cpp +++ b/test/unordered/find_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2009 Daniel James. +// Copyright (C) 2022 Christian Mazakas // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/test.hpp" @@ -133,6 +129,20 @@ namespace find_tests { } } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map >* test_map; + + UNORDERED_TEST( + find_tests1, ((test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) +#else boost::unordered_set >* test_set; boost::unordered_multiset >* test_multimap; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - UNORDERED_TEST( find_tests1, ((test_set)(test_multiset)(test_map)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) UNORDERED_TEST(find_compatible_keys_test, ((test_set)(test_multiset)(test_map)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) +#endif } RUN_TESTS() diff --git a/test/unordered/fwd_map_test.cpp b/test/unordered/fwd_map_test.cpp index 0c7df04f..5ce1fb94 100644 --- a/test/unordered/fwd_map_test.cpp +++ b/test/unordered/fwd_map_test.cpp @@ -1,14 +1,44 @@ // Copyright 2008-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // clang-format off #include "../helpers/prefix.hpp" +#ifdef BOOST_UNORDERED_FOA_TESTS +#include +#include +#else #include +#endif #include "../helpers/postfix.hpp" // clang-format on +#ifdef BOOST_UNORDERED_FOA_TESTS +template +void call_swap( + boost::unordered_flat_map& x, boost::unordered_flat_map& y) +{ + swap(x, y); +} + +template +bool call_equals( + boost::unordered_flat_map& x, boost::unordered_flat_map& y) +{ + return x == y; +} + +template +bool call_not_equals( + boost::unordered_flat_map& x, boost::unordered_flat_map& y) +{ + return x != y; +} + +#include +#else template void call_swap(boost::unordered_map& x, boost::unordered_map& y) { @@ -50,10 +80,15 @@ bool call_not_equals( } #include +#endif #include "../helpers/test.hpp" +#ifdef BOOST_UNORDERED_FOA_TESTS +typedef boost::unordered_flat_map int_map; +#else typedef boost::unordered_map int_map; typedef boost::unordered_multimap int_multimap; +#endif UNORDERED_AUTO_TEST (use_map_fwd_declared_function) { int_map x, y; @@ -71,11 +106,13 @@ UNORDERED_AUTO_TEST (use_map_fwd_declared_function) { BOOST_TEST(call_not_equals(x, y)); } +#ifndef BOOST_UNORDERED_FOA_TESTS UNORDERED_AUTO_TEST (use_multimap_fwd_declared_function) { int_multimap x, y; call_swap(x, y); BOOST_TEST(call_equals(x, y)); BOOST_TEST(!call_not_equals(x, y)); } +#endif RUN_TESTS() diff --git a/test/unordered/fwd_set_test.cpp b/test/unordered/fwd_set_test.cpp index 6512522a..86f4a9d1 100644 --- a/test/unordered/fwd_set_test.cpp +++ b/test/unordered/fwd_set_test.cpp @@ -1,11 +1,17 @@ // Copyright 2008-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // clang-format off #include "../helpers/prefix.hpp" +#ifdef BOOST_UNORDERED_FOA_TESTS +#include +#include +#else #include +#endif #include "../helpers/postfix.hpp" // clang-format on @@ -20,6 +26,31 @@ struct false_type false_type is_unordered_set_impl(void*); +#ifdef BOOST_UNORDERED_FOA_TESTS +template +true_type is_unordered_set_impl( + boost::unordered_flat_set*); + +template +void call_swap(boost::unordered_flat_set& x, boost::unordered_flat_set& y) +{ + swap(x, y); +} + +template +bool call_equals( + boost::unordered_flat_set& x, boost::unordered_flat_set& y) +{ + return x == y; +} + +template +bool call_not_equals( + boost::unordered_flat_set& x, boost::unordered_flat_set& y) +{ + return x != y; +} +#else template true_type is_unordered_set_impl( boost::unordered_set*); @@ -41,7 +72,9 @@ bool call_not_equals(boost::unordered_set& x, boost::unordered_set& y) { return x != y; } +#endif +#ifndef BOOST_UNORDERED_FOA_TESTS template void call_swap(boost::unordered_multiset& x, boost::unordered_multiset& y) { @@ -61,20 +94,29 @@ bool call_not_equals( { return x != y; } +#endif #include "../helpers/test.hpp" +#ifdef BOOST_UNORDERED_FOA_TESTS +typedef boost::unordered_flat_set int_set; +#else typedef boost::unordered_set int_set; typedef boost::unordered_multiset int_multiset; +#endif UNORDERED_AUTO_TEST (use_fwd_declared_trait_without_definition) { BOOST_TEST(sizeof(is_unordered_set_impl((int_set*)0)) == sizeof(true_type)); } +#ifdef BOOST_UNORDERED_FOA_TESTS +#include +#else #include +#endif UNORDERED_AUTO_TEST (use_fwd_declared_trait) { - boost::unordered_set x; + int_set x; BOOST_TEST(sizeof(is_unordered_set_impl(&x)) == sizeof(true_type)); BOOST_TEST(sizeof(is_unordered_set_impl((int*)0)) == sizeof(false_type)); @@ -96,11 +138,13 @@ UNORDERED_AUTO_TEST (use_set_fwd_declared_function) { BOOST_TEST(call_not_equals(x, y)); } +#ifndef BOOST_UNORDERED_FOA_TESTS UNORDERED_AUTO_TEST (use_multiset_fwd_declared_function) { int_multiset x, y; call_swap(x, y); BOOST_TEST(call_equals(x, y)); BOOST_TEST(!call_not_equals(x, y)); } +#endif RUN_TESTS() diff --git a/test/unordered/incomplete_test.cpp b/test/unordered/incomplete_test.cpp index 09afcba6..3c5b09fa 100644 --- a/test/unordered/incomplete_test.cpp +++ b/test/unordered/incomplete_test.cpp @@ -1,21 +1,21 @@ // Copyright 2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include namespace x { struct D { +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map x; +#else boost::unordered_map x; +#endif }; } @@ -29,6 +29,17 @@ namespace incomplete_test { // Declare some instances +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_map > > + map; + typedef boost::unordered_flat_map > > + multimap; + typedef boost::unordered_flat_set > set; + typedef boost::unordered_flat_set > + multiset; +#else typedef boost::unordered_map > > map; @@ -38,6 +49,7 @@ namespace incomplete_test { typedef boost::unordered_set > set; typedef boost::unordered_multiset > multiset; +#endif // Now define the types which are stored as members, as they are needed for // declaring struct members. @@ -76,7 +88,28 @@ namespace incomplete_test { // // Incomplete hash, equals and allocator aren't here supported at the // moment. - +#ifdef BOOST_UNORDERED_FOA_TESTS + struct struct1 + { + boost::unordered_flat_map > > + x; + }; + struct struct2 + { + boost::unordered_flat_map > > + x; + }; + struct struct3 + { + boost::unordered_flat_set > x; + }; + struct struct4 + { + boost::unordered_flat_set > x; + }; +#else struct struct1 { boost::unordered_map > x; }; - +#endif // Now define the value type. struct value diff --git a/test/unordered/init_type_insert_tests.cpp b/test/unordered/init_type_insert_tests.cpp new file mode 100644 index 00000000..e9920d38 --- /dev/null +++ b/test/unordered/init_type_insert_tests.cpp @@ -0,0 +1,244 @@ + +// Copyright 2022 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(BOOST_UNORDERED_FOA_TESTS) +#error "This test is only for the FOA-style conatiners" +#endif + +#include "../helpers/unordered.hpp" + +#include "../helpers/test.hpp" + +struct move_only +{ + int x_ = -1; + + move_only() = default; + move_only(int x) : x_{x} {} + move_only(move_only const&) = delete; + move_only(move_only&&) = default; + + friend bool operator==(move_only const& lhs, move_only const& rhs) + { + return lhs.x_ == rhs.x_; + } +}; + +namespace std{ + +template <> struct hash +{ + std::size_t operator()(move_only const& mo) const noexcept + { + return std::hash()(mo.x_); + } +}; + +} // namespace std + +struct raii_tracker +{ + static unsigned move_constructs; + static unsigned copy_constructs; + + int x_ = -1; + + static void reset_counts() + { + move_constructs = 0; + copy_constructs = 0; + } + + raii_tracker() {} + raii_tracker(int x) : x_{x} {} + raii_tracker(raii_tracker const& rhs) : x_{rhs.x_} { ++copy_constructs; } + + raii_tracker(raii_tracker&& rhs) noexcept : x_{rhs.x_} + { + rhs.x_ = -1; + + ++move_constructs; + } + + friend bool operator==(raii_tracker const& lhs, raii_tracker const& rhs) + { + return lhs.x_ == rhs.x_; + } +}; + +namespace std{ + +template <> struct hash +{ + std::size_t operator()(raii_tracker const& rt) const noexcept + { + return std::hash()(rt.x_); + } +}; + +} // namespace std + +unsigned raii_tracker::move_constructs = 0; +unsigned raii_tracker::copy_constructs = 0; + +static void test_move_only() +{ + int const v = 128; + + boost::unordered_flat_map > map; + + using init_type = decltype(map)::init_type; + static_assert( + std::is_same::value, + ""); + + map.insert(std::make_pair(move_only(1), v)); + map.insert({move_only(2), v}); + + BOOST_TEST_EQ(map.size(), 2u); + + map.rehash(1024); + BOOST_TEST_GE(map.bucket_count(), 1024u); +} + +static void test_insert_tracking() +{ + raii_tracker::reset_counts(); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 0u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 0u); + + boost::unordered_flat_map > + map; + + { + std::pair value{1, 2}; + + map.insert(value); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 2u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 0u); + } + + { + std::pair value{2, 3}; + + map.insert(std::move(value)); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 2u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 2u); + } + + { + std::pair value{3, 4}; + + map.insert(value); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 4u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 2u); + } + + { + std::pair value{4, 5}; + + map.insert(std::move(value)); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 5u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 3u); + } + + { + map.insert(std::make_pair(5, 6)); + BOOST_TEST_EQ(raii_tracker::copy_constructs, 5u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 5u); + } + + { + map.insert({6, 7}); + BOOST_TEST_EQ(raii_tracker::copy_constructs, 5u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 7u); + } + + BOOST_TEST_EQ(map.size(), 6u); + + map.rehash(1024); + BOOST_TEST_EQ(raii_tracker::copy_constructs, 5u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 7u + 2u * map.size()); +} + +static void test_insert_hint_tracking() +{ + raii_tracker::reset_counts(); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 0u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 0u); + + boost::unordered_flat_map > + map; + + { + std::pair value{1, 2}; + + map.insert(map.begin(), value); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 2u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 0u); + } + + { + std::pair value{2, 3}; + + map.insert(std::move(value)); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 2u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 2u); + } + + { + std::pair value{3, 4}; + + map.insert(map.begin(), value); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 4u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 2u); + } + + { + std::pair value{4, 5}; + + map.insert(map.begin(), std::move(value)); + + BOOST_TEST_EQ(raii_tracker::copy_constructs, 5u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 3u); + } + + { + map.insert(map.begin(), std::make_pair(5, 6)); + BOOST_TEST_EQ(raii_tracker::copy_constructs, 5u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 5u); + } + + { + map.insert(map.begin(), {6, 7}); + BOOST_TEST_EQ(raii_tracker::copy_constructs, 5u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 7u); + } + + BOOST_TEST_EQ(map.size(), 6u); + + map.rehash(1024); + BOOST_TEST_EQ(raii_tracker::copy_constructs, 5u); + BOOST_TEST_EQ(raii_tracker::move_constructs, 7u + 2u * map.size()); +} + +int main() +{ + test_move_only(); + test_insert_tracking(); + test_insert_hint_tracking(); + return boost::report_errors(); +} diff --git a/test/unordered/insert_hint_tests.cpp b/test/unordered/insert_hint_tests.cpp index c77fc2dc..ebde259c 100644 --- a/test/unordered/insert_hint_tests.cpp +++ b/test/unordered/insert_hint_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2016 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../helpers/invariants.hpp" @@ -17,6 +13,7 @@ #include namespace insert_hint { +#ifndef BOOST_UNORDERED_FOA_TESTS UNORDERED_AUTO_TEST (insert_hint_empty) { typedef boost::unordered_multiset container; container x; @@ -90,9 +87,13 @@ namespace insert_hint { } } } - +#endif UNORDERED_AUTO_TEST (insert_hint_unique) { +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set container; +#else typedef boost::unordered_set container; +#endif container x; x.insert(x.cbegin(), 10); BOOST_TEST_EQ(x.size(), 1u); @@ -101,7 +102,11 @@ namespace insert_hint { } UNORDERED_AUTO_TEST (insert_hint_unique_single) { +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set container; +#else typedef boost::unordered_set container; +#endif container x; x.insert(10); diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 7ddb41fd..f59d5acd 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -1,16 +1,12 @@ // Copyright 2006-2010 Daniel James. +// Copyright (C) 2022 Christian Mazakas // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #if !defined(PIECEWISE_TEST_NAME) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/test.hpp" @@ -21,6 +17,8 @@ #include "../helpers/input_iterator.hpp" #include "../helpers/helpers.hpp" +#include + namespace insert_tests { test::seed_t initialize_seed(243432); @@ -678,6 +676,23 @@ namespace insert_tests { } } + template void set_tests(X*, test::random_generator) + { + // prove that our insert(iterator, iterator) implementation honors + // Cpp17EmplaceConstructible + // + + X x; + std::vector v; + v.reserve(1000); + for (unsigned i = 0; i < 1000; ++i) { + v.push_back(static_cast(i)); + } + + x.insert(v.begin(), v.end()); + BOOST_TEST_EQ(x.size(), 1000u); + } + template void try_emplace_tests(X*, test::random_generator generator) { @@ -870,6 +885,53 @@ namespace insert_tests { test::check_equivalent_keys(x); } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set >* test_set_std_alloc; + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map >* test_map; + + UNORDERED_TEST(unique_insert_tests1, + ((test_set_std_alloc)(test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST( + insert_tests2, ((test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(unique_emplace_tests1, + ((test_set_std_alloc)(test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(move_emplace_tests, + ((test_set_std_alloc)(test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(default_emplace_tests, + ((test_set_std_alloc)(test_set)(test_map))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(map_tests, + ((test_map))((default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST( + map_tests2, ((test_map))((default_generator)(generate_collisions))) + + UNORDERED_TEST(map_insert_range_test1, + ((test_map))((default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(map_insert_range_test2, + ((test_map))((default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST( + set_tests, ((test_set_std_alloc)(test_set))((default_generator))) +#else boost::unordered_set >* test_set_std_alloc; boost::unordered_multimap >* test_multimap; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - UNORDERED_TEST(unique_insert_tests1, ((test_set_std_alloc)(test_set)(test_map))( (default_generator)(generate_collisions)(limited_range))) @@ -897,8 +955,8 @@ namespace insert_tests { (default_generator)(generate_collisions)(limited_range))) UNORDERED_TEST(insert_tests2, - ((test_multimap_std_alloc)(test_set)(test_multiset)(test_map)( - test_multimap))((default_generator)(generate_collisions)(limited_range))) + ((test_multimap_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap))( + (default_generator)(generate_collisions)(limited_range))) UNORDERED_TEST(unique_emplace_tests1, ((test_set_std_alloc)(test_set)(test_map))( @@ -909,13 +967,11 @@ namespace insert_tests { (default_generator)(generate_collisions)(limited_range))) UNORDERED_TEST(move_emplace_tests, - ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_map)( - test_multiset)(test_multimap))( + ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_map)(test_multiset)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) UNORDERED_TEST(default_emplace_tests, - ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_map)( - test_multiset)(test_multimap))( + ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_map)(test_multiset)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) UNORDERED_TEST(map_tests, @@ -932,6 +988,10 @@ namespace insert_tests { ((test_multimap_std_alloc)(test_map)(test_multimap))( (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST( + set_tests, ((test_set_std_alloc)(test_set)(test_multiset))((default_generator))) +#endif + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) struct initialize_from_two_ints @@ -950,13 +1010,21 @@ namespace insert_tests { }; UNORDERED_AUTO_TEST (insert_initializer_list_set) { +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set set; +#else boost::unordered_set set; +#endif set.insert({1, 2, 3, 1}); BOOST_TEST_EQ(set.size(), 3u); BOOST_TEST(set.find(1) != set.end()); BOOST_TEST(set.find(4) == set.end()); +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set set2; +#else boost::unordered_set set2; +#endif #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)) set2.insert({{1, 2}}); @@ -985,6 +1053,7 @@ namespace insert_tests { BOOST_TEST(set2.find({8, 7}) == set2.end()); } +#ifndef BOOST_UNORDERED_FOA_TESTS #if !BOOST_WORKAROUND(BOOST_MSVC, == 1800) UNORDERED_AUTO_TEST (insert_initializer_list_multiset) { @@ -1002,16 +1071,22 @@ namespace insert_tests { BOOST_TEST_EQ(multiset.count("c"), 0u); } +#endif #endif UNORDERED_AUTO_TEST (insert_initializer_list_map) { +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map map; +#else boost::unordered_map map; + #endif // map.insert({}); BOOST_TEST(map.empty()); map.insert({{"a", "b"}, {"a", "b"}, {"d", ""}}); BOOST_TEST_EQ(map.size(), 2u); } +#ifndef BOOST_UNORDERED_FOA_TESTS UNORDERED_AUTO_TEST (insert_initializer_list_multimap) { boost::unordered_multimap multimap; // multimap.insert({}); @@ -1020,6 +1095,7 @@ namespace insert_tests { BOOST_TEST_EQ(multimap.size(), 3u); BOOST_TEST_EQ(multimap.count("a"), 2u); } +#endif #endif @@ -1050,10 +1126,18 @@ namespace insert_tests { UNORDERED_AUTO_TEST (map_emplace_test) { { +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map > > + x; +#else + boost::unordered_map > > x; +#endif #if !BOOST_UNORDERED_SUN_WORKAROUNDS1 x.emplace(); @@ -1070,6 +1154,7 @@ namespace insert_tests { x.find(5) != x.end() && x.find(5)->second == overloaded_constructor()); } +#ifndef BOOST_UNORDERED_FOA_TESTS { boost::unordered_multimapsecond == overloaded_constructor(3)); } +#endif } UNORDERED_AUTO_TEST (set_emplace_test) { +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set x; + overloaded_constructor check; +#else boost::unordered_set x; overloaded_constructor check; +#endif #if !BOOST_UNORDERED_SUN_WORKAROUNDS1 x.emplace(); @@ -1136,6 +1227,7 @@ namespace insert_tests { } }; +#ifndef BOOST_UNORDERED_FOA_TESTS UNORDERED_AUTO_TEST (map_emplace_test2) { // Emulating piecewise construction with boost::tuple bypasses the // allocator's construct method, but still uses test destroy method. @@ -1259,10 +1351,13 @@ namespace insert_tests { std::make_pair(overloaded_constructor(1), overloaded_constructor(2, 3)); BOOST_TEST(x.find(check) != x.end() && *x.find(check) == check); } +#endif // Use the preprocessor to generate tests using different combinations of // boost/std piecewise_construct_t/tuple. +#ifndef BOOST_UNORDERED_FOA_TESTS + #define PIECEWISE_TEST_NAME boost_tuple_piecewise_tests #define PIECEWISE_NAMESPACE boost::unordered #define TUPLE_NAMESPACE boost @@ -1289,6 +1384,8 @@ namespace insert_tests { #endif +#endif + #if !defined(BOOST_NO_CXX11_HDR_TUPLE) && \ BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT @@ -1311,12 +1408,21 @@ UNORDERED_AUTO_TEST (PIECEWISE_TEST_NAME) { #endif { +#if defined(BOOST_UNORDERED_FOA_TESTS) + boost::unordered_flat_map, + std::equal_to, + test::allocator1< + std::pair > > + x; +#else boost::unordered_map, std::equal_to, test::allocator1< std::pair > > x; +#endif x.emplace(PIECEWISE_NAMESPACE::piecewise_construct, TUPLE_NAMESPACE::make_tuple(), TUPLE_NAMESPACE::make_tuple()); @@ -1360,6 +1466,7 @@ UNORDERED_AUTO_TEST (PIECEWISE_TEST_NAME) { x.find(overloaded_constructor(2, 3))->second == overloaded_constructor(4, 5, 6)); } +#ifndef BOOST_UNORDERED_FOA_TESTS { boost::unordered_multimap, @@ -1393,6 +1500,7 @@ UNORDERED_AUTO_TEST (PIECEWISE_TEST_NAME) { x.find(overloaded_constructor(9, 3, 1))->second == overloaded_constructor(10)); } +#endif } UNORDERED_AUTO_TEST (BOOST_PP_CAT(PIECEWISE_TEST_NAME, 2)) { @@ -1400,9 +1508,15 @@ UNORDERED_AUTO_TEST (BOOST_PP_CAT(PIECEWISE_TEST_NAME, 2)) { test::detail::disable_construction_tracking _scoped; #endif +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set< + std::pair > + x; +#else boost::unordered_set< std::pair > x; +#endif std::pair check; x.emplace(PIECEWISE_NAMESPACE::piecewise_construct, diff --git a/test/unordered/link_test_1.cpp b/test/unordered/link_test_1.cpp index c32c3ac9..d8032e73 100644 --- a/test/unordered/link_test_1.cpp +++ b/test/unordered/link_test_1.cpp @@ -1,15 +1,24 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" +#ifdef BOOST_UNORDERED_FOA_TESTS +void foo(boost::unordered_flat_set&, boost::unordered_flat_map&); + +int main() +{ + boost::unordered_flat_set x1; + boost::unordered_flat_map x2; + + foo(x1, x2); + + return 0; +} +#else void foo(boost::unordered_set&, boost::unordered_map&, boost::unordered_multiset&, boost::unordered_multimap&); @@ -24,3 +33,4 @@ int main() return 0; } +#endif diff --git a/test/unordered/link_test_2.cpp b/test/unordered/link_test_2.cpp index 1c3ed766..74de87c9 100644 --- a/test/unordered/link_test_2.cpp +++ b/test/unordered/link_test_2.cpp @@ -1,15 +1,27 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" +#ifdef BOOST_UNORDERED_FOA_TESTS +void foo( + boost::unordered_flat_set& x1, boost::unordered_flat_map& x2) +{ +#if BOOST_WORKAROUND(BOOST_CODEGEARC, BOOST_TESTED_AT(0x0613)) + struct dummy + { + boost::unordered_flat_set x1; + boost::unordered_flat_map x2; + }; +#endif + + x1.insert(1); + x2[2] = 2; +} +#else void foo(boost::unordered_set& x1, boost::unordered_map& x2, boost::unordered_multiset& x3, boost::unordered_multimap& x4) { @@ -28,3 +40,4 @@ void foo(boost::unordered_set& x1, boost::unordered_map& x2, x3.insert(3); x4.insert(std::make_pair(4, 5)); } +#endif diff --git a/test/unordered/load_factor_tests.cpp b/test/unordered/load_factor_tests.cpp index 9006c1bd..461f883d 100644 --- a/test/unordered/load_factor_tests.cpp +++ b/test/unordered/load_factor_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include @@ -26,7 +22,17 @@ namespace load_factor_tests { template void set_load_factor_tests(X*) { X x; +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(x.max_load_factor() == boost::unordered::detail::foa::mlf); + BOOST_TEST(x.load_factor() == 0); + // A valid implementation could fail these tests, but I think they're + // reasonable. + x.max_load_factor(2.0); + BOOST_TEST(x.max_load_factor() == boost::unordered::detail::foa::mlf); + x.max_load_factor(0.5); + BOOST_TEST(x.max_load_factor() == boost::unordered::detail::foa::mlf); +#else BOOST_TEST(x.max_load_factor() == 1.0); BOOST_TEST(x.load_factor() == 0); @@ -36,6 +42,7 @@ namespace load_factor_tests { BOOST_TEST(x.max_load_factor() == 2.0); x.max_load_factor(0.5); BOOST_TEST(x.max_load_factor() == 0.5); +#endif } template @@ -72,21 +79,32 @@ namespace load_factor_tests { insert_test(ptr, std::numeric_limits::infinity(), generator); } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set* int_set_ptr; + boost::unordered_flat_map* int_map_ptr; + + UNORDERED_TEST(set_load_factor_tests, ((int_set_ptr)(int_map_ptr))) + + UNORDERED_TEST(load_factor_insert_tests, + ((int_set_ptr)(int_map_ptr))( + (default_generator)(generate_collisions)(limited_range))) +#else boost::unordered_set* int_set_ptr; boost::unordered_multiset* int_multiset_ptr; boost::unordered_map* int_map_ptr; boost::unordered_multimap* int_multimap_ptr; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - UNORDERED_TEST(set_load_factor_tests, ((int_set_ptr)(int_multiset_ptr)(int_map_ptr)(int_multimap_ptr))) UNORDERED_TEST(load_factor_insert_tests, ((int_set_ptr)(int_multiset_ptr)(int_map_ptr)(int_multimap_ptr))( (default_generator)(generate_collisions)(limited_range))) +#endif } RUN_TESTS() diff --git a/test/unordered/merge_tests.cpp b/test/unordered/merge_tests.cpp index 897abb34..32f4d685 100644 --- a/test/unordered/merge_tests.cpp +++ b/test/unordered/merge_tests.cpp @@ -1,12 +1,10 @@ // Copyright 2016 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include "../helpers/postfix.hpp" -#include "../helpers/prefix.hpp" -#include -#include +#include "../helpers/unordered.hpp" #include "../helpers/count.hpp" #include "../helpers/helpers.hpp" @@ -19,8 +17,13 @@ namespace merge_tests { UNORDERED_AUTO_TEST (merge_set) { +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set x; + boost::unordered_flat_set y; +#else boost::unordered_set x; boost::unordered_set y; +#endif x.merge(y); BOOST_TEST(x.empty()); @@ -57,6 +60,7 @@ namespace merge_tests { test::check_equivalent_keys(y); } +#ifndef BOOST_UNORDERED_FOA_TESTS UNORDERED_AUTO_TEST (merge_multiset) { boost::unordered_multiset x; boost::unordered_multiset y; @@ -134,6 +138,7 @@ namespace merge_tests { test::check_equivalent_keys(x); test::check_equivalent_keys(y); } +#endif template void merge_empty_test(X1*, X2*, test::random_generator generator) @@ -228,6 +233,82 @@ namespace merge_tests { test::check_equivalent_keys(x2); } + using test::default_generator; + using test::generate_collisions; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set >* test_set_std_alloc; + + + boost::unordered_flat_map >* test_map_std_alloc; + + + boost::unordered_flat_set >* test_set; + + + boost::unordered_flat_map >* test_map; + + // clang-format off +UNORDERED_TEST(merge_empty_test, + ((test_set_std_alloc)) + ((test_set_std_alloc)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_empty_test, + ((test_map_std_alloc)) + ((test_map_std_alloc)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_empty_test, + ((test_set)) + ((test_set)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_empty_test, + ((test_map)) + ((test_map)) + ((default_generator)(generate_collisions))) + +UNORDERED_TEST(merge_into_empty_test, + ((test_set_std_alloc)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_empty_test, + ((test_map_std_alloc)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_empty_test, + ((test_set)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_empty_test, + ((test_map)) + ((default_generator)(generate_collisions))) + +UNORDERED_TEST(merge_into_unique_keys_test, + ((test_set_std_alloc)) + ((test_set_std_alloc)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_unique_keys_test, + ((test_map_std_alloc)) + ((test_map_std_alloc)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_unique_keys_test, + ((test_set)) + ((test_set)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) +UNORDERED_TEST(merge_into_unique_keys_test, + ((test_map)) + ((test_map)) + ((0)(1)(2)) + ((0)(1)(2)) + ((default_generator)(generate_collisions))) + // clang-format on +#else boost::unordered_set >* test_set_std_alloc; boost::unordered_multiset >* test_multimap; - using test::default_generator; - using test::generate_collisions; - // clang-format off UNORDERED_TEST(merge_empty_test, ((test_set_std_alloc)(test_multiset_std_alloc)) @@ -332,6 +410,8 @@ UNORDERED_TEST(merge_into_equiv_keys_test, ((0)(1)(2)) ((default_generator)(generate_collisions))) // clang-format on +#endif + } RUN_TESTS() diff --git a/test/unordered/move_tests.cpp b/test/unordered/move_tests.cpp index 21b2e90c..53cac2ce 100644 --- a/test/unordered/move_tests.cpp +++ b/test/unordered/move_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2008-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../objects/test.hpp" @@ -71,7 +67,11 @@ namespace move_tests { BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 1.0); +#endif test::check_equivalent_keys(y); #if defined(BOOST_UNORDERED_USE_MOVE) || \ !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -146,7 +146,11 @@ namespace move_tests { BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 0.5); // Not necessarily required. +#endif test::check_equivalent_keys(y); } @@ -162,7 +166,11 @@ namespace move_tests { BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al2)); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 2.0); // Not necessarily required. +#endif test::check_equivalent_keys(y); } @@ -192,7 +200,11 @@ namespace move_tests { BOOST_TEST(test::equivalent(y.hash_function(), hf)); BOOST_TEST(test::equivalent(y.key_eq(), eq)); BOOST_TEST(test::equivalent(y.get_allocator(), al)); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 1.0); // Not necessarily required. +#endif test::check_equivalent_keys(y); } } @@ -215,7 +227,11 @@ namespace move_tests { BOOST_TEST(y.empty()); test::check_container(y, v2); test::check_equivalent_keys(y); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 2.0); +#endif #if defined(BOOST_HAS_NRVO) if (BOOST_UNORDERED_TEST_MOVING @@ -240,7 +256,11 @@ namespace move_tests { #endif test::check_container(y, v); test::check_equivalent_keys(y); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 0.5); +#endif #if defined(BOOST_HAS_NRVO) if (BOOST_UNORDERED_TEST_MOVING @@ -268,7 +288,11 @@ namespace move_tests { #endif test::check_container(y, v); test::check_equivalent_keys(y); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 0.25); +#endif if (BOOST_UNORDERED_TEST_MOVING ? (bool)allocator_type::is_propagate_on_move @@ -296,7 +320,11 @@ namespace move_tests { } test::check_container(y, v); test::check_equivalent_keys(y); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 0.25); +#endif if (BOOST_UNORDERED_TEST_MOVING ? (bool)allocator_type::is_propagate_on_move @@ -332,7 +360,11 @@ namespace move_tests { test::check_container(y, v2); test::check_equivalent_keys(y); +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.max_load_factor() == 0.875); +#else BOOST_TEST(y.max_load_factor() == 0.5); +#endif if (BOOST_UNORDERED_TEST_MOVING ? (bool)allocator_type::is_propagate_on_move @@ -344,519 +376,51 @@ namespace move_tests { } } - template T const& get_key(T const& t) { return t; } - - template K const& get_key(std::pair const& kv) - { - return kv.first; - } - - template T const& get_value(T const& t) { return t; } - - template K const& get_value(std::pair const& kv) - { - return kv.second; - } - - template - static void insert_range(T& y, test::random_values const& v) - { - y.insert(v.begin(), v.end()); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template - static void insert_single(T& y, test::random_values const& v) - { - y.insert(*v.begin()); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template - static void insert_single_hint(T& y, test::random_values const& v) - { - y.insert(y.end(), *v.begin()); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template struct insert_or_assign_invoker - { - void operator()(T&, test::random_values const&) {} - }; - - template - struct insert_or_assign_invoker< - boost::unordered_map > - { - void operator()(boost::unordered_map& y, - test::random_values< - boost::unordered_map > const& v) - { - typedef typename boost::unordered_map::size_type size_type; - - y.insert_or_assign(get_key(*v.begin()), get_value(*v.begin())); - BOOST_TEST_EQ( - y.size(), static_cast(std::distance(y.begin(), y.end()))); - } - }; - - template - static void insert_or_assign(T& y, test::random_values const& v) - { - insert_or_assign_invoker()(y, v); - } - - template struct insert_or_assign_hint_invoker - { - void operator()(T&, test::random_values const&) {} - }; - - template - struct insert_or_assign_hint_invoker< - boost::unordered_map > - { - void operator()(boost::unordered_map& y, - test::random_values< - boost::unordered_map > const& v) - { - typedef typename boost::unordered_map::size_type size_type; - y.insert_or_assign(y.end(), get_key(*v.begin()), get_value(*v.begin())); - BOOST_TEST_EQ( - y.size(), static_cast(std::distance(y.begin(), y.end()))); - } - }; - - template - static void insert_or_assign_hint(T& y, test::random_values const& v) - { - insert_or_assign_hint_invoker()(y, v); - } - - template struct try_emplace_invoker - { - void operator()(T&, test::random_values const&) {} - }; - - template - struct try_emplace_invoker< - boost::unordered_map > - { - void operator()(boost::unordered_map& y, - test::random_values< - boost::unordered_map > const& v) - { - typedef typename boost::unordered_map::size_type size_type; - y.try_emplace(get_key(*v.begin()), get_value(*v.begin())); - BOOST_TEST_EQ( - y.size(), static_cast(std::distance(y.begin(), y.end()))); - } - }; - - template - static void try_emplace(T& y, test::random_values const& v) - { - try_emplace_invoker()(y, v); - } - - template struct try_emplace_hint_invoker - { - void operator()(T&, test::random_values const&) {} - }; - - template - struct try_emplace_hint_invoker< - boost::unordered_map > - { - void operator()(boost::unordered_map& y, - test::random_values< - boost::unordered_map > const& v) - { - typedef typename boost::unordered_map::size_type size_type; - y.try_emplace(y.end(), get_key(*v.begin()), get_value(*v.begin())); - BOOST_TEST_EQ( - y.size(), static_cast(std::distance(y.begin(), y.end()))); - } - }; - - template - static void try_emplace_hint(T& y, test::random_values const& v) - { - try_emplace_hint_invoker()(y, v); - } - - template struct at_invoker - { - void operator()(T&, test::random_values const&) {} - }; - - template - struct at_invoker > - { - void operator()(boost::unordered_map& y, - test::random_values< - boost::unordered_map > const& v) - { - BOOST_TRY { y.at(get_key(*v.begin())); } - BOOST_CATCH(...) {} - BOOST_CATCH_END - } - }; - - template static void at(T& y, test::random_values const& v) - { - at_invoker()(y, v); - } - - template struct index_operator_invoker - { - void operator()(T&, test::random_values const&) {} - }; - - template - struct index_operator_invoker< - boost::unordered_map > - { - void operator()(boost::unordered_map& y, - test::random_values< - boost::unordered_map > const& v) - { - typedef typename boost::unordered_map::size_type size_type; - y[get_key(*v.begin())] = get_value(*v.begin()); - BOOST_TEST_EQ( - y.size(), static_cast(std::distance(y.begin(), y.end()))); - } - }; - - template - static void index_operator(T& y, test::random_values const& v) - { - index_operator_invoker()(y, v); - } - - template static void clear(T& y, test::random_values const&) - { - y.clear(); - BOOST_TEST(y.empty()); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template static void capacity(T& y, test::random_values const&) - { - (void)y.empty(); - (void)y.size(); - (void)y.max_size(); - (void)y.load_factor(); - (void)y.max_load_factor(); - (void)y.hash_function(); - (void)y.key_eq(); - (void)y.get_allocator(); - } - - template static void iterators(T& y, test::random_values const&) - { - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template - static void erase_range(T& y, test::random_values const&) - { - y.erase(y.begin(), y.end()); - BOOST_TEST(y.empty()); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template - static void erase_key(T& y, test::random_values const& v) - { - y.erase(get_key(*v.begin())); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template static void lookup(T& y, test::random_values const& v) - { - (void)y.count(get_key(*v.begin())); - (void)y.find(get_key(*v.begin())); - (void)y.contains(get_key(*v.begin())); - (void)y.equal_range(get_key(*v.begin())); - } - - template static void reserve(T& y, test::random_values const&) - { - y.reserve(1337); - BOOST_TEST_GT(y.bucket_count(), 1337u); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template - static void copy_assignment(T& y, test::random_values const& v) - { - T x(v.begin(), v.end()); - y = x; - BOOST_TEST_EQ(y.size(), x.size()); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template - static void move_assignment(T& y, test::random_values const& v) - { - T x(v.begin(), v.end()); - std::size_t const size = x.size(); - y = boost::move(x); - BOOST_TEST_GE(y.size(), size); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template static void equal(T& y, test::random_values const& v) - { - T x(v.begin(), v.end()); - (void)(y == x); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template static void extract(T& y, test::random_values const& v) - { - (void)y.extract(get_key(*v.begin())); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template static void merge(T& y, test::random_values const& v) - { - T x(v.begin(), v.end()); - if (y.get_allocator() == x.get_allocator()) { - y.merge(x); - } - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template bool pred(X const&) { return true; } - - template - static void erase_with_pred(T& y, test::random_values const&) - { - erase_if(y, pred); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template - static void container_swap(T& y, test::random_values const& v) - { - T x(v.begin(), v.end()); - if (boost::allocator_propagate_on_container_swap< - typename T::allocator_type>::type::value || - x.get_allocator() == y.get_allocator()) { - y.swap(x); - } - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - } - - template static void buckets(T& y, test::random_values const& v) - { - (void)y.begin(0); - (void)y.end(0); - (void)y.bucket_count(); - (void)y.max_bucket_count(); - (void)y.bucket_size(0); - (void)y.bucket(get_key(*v.begin())); - } - - template - static void double_move_construct(T& y, test::random_values const&) - { - T x = boost::move(y); - x.clear(); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - BOOST_TEST_EQ(x.size(), - static_cast(std::distance(x.begin(), x.end()))); - } - - template - static void double_move_assign(T& y, test::random_values const&) - { - T x; - x = boost::move(y); - x.clear(); - BOOST_TEST_EQ(y.size(), - static_cast(std::distance(y.begin(), y.end()))); - BOOST_TEST_EQ(x.size(), - static_cast(std::distance(x.begin(), x.end()))); - } - - template - static void post_move_tests(T* ptr, test::random_generator const& generator) - { - // clang-format off - void (*fps[])(T&, test::random_values const&) = { - insert_range, - insert_single, - insert_single_hint, - insert_or_assign, - insert_or_assign_hint, - try_emplace, - try_emplace_hint, - at, - index_operator, - clear, - capacity, - iterators, - erase_range, - erase_key, - lookup, - reserve, - copy_assignment, - move_assignment, - equal, - extract, - merge, - erase_with_pred, - container_swap, - buckets, - double_move_construct, - double_move_assign - }; - // clang-format on - - std::size_t const len = (sizeof(fps) / sizeof(*(fps))); - - for (std::size_t i = 0; i < len; ++i) { - test::check_instances check_; - - test::random_values const v(1000, generator); - test::object_count count; - T y(create(v, count)); - - unsigned num_allocs = test::detail::tracker.count_allocations; - (void)num_allocs; - - T x(boost::move(y)); - -#if defined(BOOST_UNORDERED_USE_MOVE) || \ - !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - BOOST_TEST(y.empty()); - BOOST_TEST(y.begin() == y.end()); - BOOST_TEST_EQ(y.bucket_count(), 0u); - BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); -#endif - - fps[i](y, v); - - test::check_container(x, v); - test::check_equivalent_keys(x); - } - - for (std::size_t i = 0; i < len; ++i) { - typename T::hasher hf(1); - typename T::key_equal eq(1); - typename T::allocator_type al1(1); - typename T::allocator_type al2(2); - - test::check_instances check_; - - test::random_values v(1000, generator); - test::object_count count; - T y(v.begin(), v.end(), 0, hf, eq, al1); - T x(boost::move(y), al2); - - BOOST_TEST_NOT(y.empty()); - BOOST_TEST(y.begin() != y.end()); - - fps[i](y, v); - - test::check_container(x, v); - test::check_equivalent_keys(x); - } - - for (std::size_t i = 0; i < len; ++i) { - test::check_instances check_; - - test::random_values v(1000, generator); - test::object_count count; - T y(create(v, count)); - - unsigned num_allocs = test::detail::tracker.count_allocations; - (void)num_allocs; - - T x(empty(ptr)); - x = boost::move(y); - -#if defined(BOOST_UNORDERED_USE_MOVE) || \ - !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - BOOST_TEST(y.empty()); - BOOST_TEST(y.begin() == y.end()); - BOOST_TEST_EQ(y.bucket_count(), 0u); - BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); -#endif - - fps[i](y, v); - - test::check_container(x, v); - test::check_equivalent_keys(x); - } - - for (std::size_t i = 0; i < len; ++i) { - typename T::hasher hf(1); - typename T::key_equal eq(1); - typename T::allocator_type al1(1); - typename T::allocator_type al2(2); - - test::check_instances check_; - - test::random_values v(1000, generator); - test::object_count count; - T y(v.begin(), v.end(), 0, hf, eq, al1); - - unsigned num_allocs = test::detail::tracker.count_allocations; - (void)num_allocs; - - T x(al2); - x = boost::move(y); - - bool b = boost::allocator_propagate_on_container_move_assignment< - typename T::allocator_type>::type::value; - if (b) { -#if defined(BOOST_UNORDERED_USE_MOVE) || \ - !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - BOOST_TEST(y.empty()); - BOOST_TEST(y.begin() == y.end()); - BOOST_TEST_EQ(y.bucket_count(), 0u); - BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map > >* + test_map_std_alloc; + + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map > >* test_map; + + boost::unordered_flat_set >* + test_set_prop_move; + boost::unordered_flat_map, + test::propagate_move> >* test_map_prop_move; + + boost::unordered_flat_set >* + test_set_no_prop_move; + boost::unordered_flat_map, + test::no_propagate_move> >* test_map_no_prop_move; + + UNORDERED_TEST(move_construct_tests1, + ((test_map_std_alloc)(test_set)(test_map)(test_set_prop_move)(test_map_prop_move)(test_set_no_prop_move)(test_map_no_prop_move))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(move_assign_tests1, + ((test_map_std_alloc)(test_set)(test_map)(test_set_prop_move)(test_map_prop_move)(test_set_no_prop_move)(test_map_no_prop_move))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(move_construct_tests2, + ((test_set)(test_map)(test_set_prop_move)(test_map_prop_move)(test_set_no_prop_move)(test_map_no_prop_move))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(move_assign_tests2, + ((test_set)(test_map)(test_set_prop_move)(test_map_prop_move)(test_set_no_prop_move)(test_map_no_prop_move))( + (default_generator)(generate_collisions)(limited_range))) #else - BOOST_TEST_NOT(y.empty()); - BOOST_TEST(y.begin() != y.end()); - -#endif - } else { - BOOST_TEST_NOT(y.empty()); - BOOST_TEST(y.begin() != y.end()); - } - - fps[i](y, v); - - test::check_container(x, v); - test::check_equivalent_keys(x); - } - } - boost::unordered_map > >* test_map_std_alloc; @@ -900,10 +464,6 @@ namespace move_tests { test::cxx11_allocator, test::no_propagate_move> >* test_multimap_no_prop_move; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - UNORDERED_TEST(move_construct_tests1, ((test_map_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap)( test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)( @@ -930,12 +490,7 @@ namespace move_tests { test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)( test_multimap_no_prop_move))( (default_generator)(generate_collisions)(limited_range))) - UNORDERED_TEST(post_move_tests, - ((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)( - test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)( - test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)( - test_multimap_no_prop_move))( - (default_generator)(generate_collisions)(limited_range))) +#endif } RUN_TESTS() diff --git a/test/unordered/noexcept_tests.cpp b/test/unordered/noexcept_tests.cpp index bc1207a1..0cb4b521 100644 --- a/test/unordered/noexcept_tests.cpp +++ b/test/unordered/noexcept_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2013 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../helpers/fwd.hpp" @@ -202,6 +198,12 @@ namespace noexcept_tests { UNORDERED_AUTO_TEST (test_noexcept) { if (have_is_nothrow_move) { +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST((boost::is_nothrow_move_constructible< + boost::unordered_flat_set >::value)); + BOOST_TEST((boost::is_nothrow_move_constructible< + boost::unordered_flat_map >::value)); +#else BOOST_TEST((boost::is_nothrow_move_constructible< boost::unordered_set >::value)); BOOST_TEST((boost::is_nothrow_move_constructible< @@ -210,19 +212,35 @@ namespace noexcept_tests { boost::unordered_map >::value)); BOOST_TEST((boost::is_nothrow_move_constructible< boost::unordered_multimap >::value)); +#endif } +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST( + (!boost::is_nothrow_move_constructible< + boost::unordered_flat_set >::value)); + BOOST_TEST( + (!boost::is_nothrow_move_constructible, equal_to_possible_exception> >::value)); +#else BOOST_TEST((!boost::is_nothrow_move_constructible< boost::unordered_set >::value)); BOOST_TEST( (!boost::is_nothrow_move_constructible, equal_to_possible_exception> >::value)); +#endif } UNORDERED_AUTO_TEST (test_nothrow_move_when_noexcept) { +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set + throwing_set; +#else typedef boost::unordered_set throwing_set; +#endif if (have_is_nothrow_move) { BOOST_TEST(boost::is_nothrow_move_constructible::value); @@ -310,8 +328,14 @@ namespace noexcept_tests { } UNORDERED_AUTO_TEST (test_nothrow_swap_when_noexcept) { +#if BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set + throwing_set; +#else typedef boost::unordered_set throwing_set; +#endif if (have_is_nothrow_swap) { BOOST_TEST(boost::is_nothrow_swappable::value); @@ -410,6 +434,20 @@ UNORDERED_AUTO_TEST (prelim_allocator_checks) { allocator2 >::type::value); } +using test::default_generator; + +#ifdef BOOST_UNORDERED_FOA_TESTS +boost::unordered_flat_set >* + throwing_set_alloc1; + +boost::unordered_flat_set >* + throwing_set_alloc2; + +UNORDERED_TEST(test_nothrow_move_assign_when_noexcept, + ((throwing_set_alloc1)(throwing_set_alloc2))((default_generator))) +#else boost::unordered_set >* throwing_set_alloc1; @@ -418,9 +456,8 @@ boost::unordered_set >* throwing_set_alloc2; -using test::default_generator; - UNORDERED_TEST(test_nothrow_move_assign_when_noexcept, ((throwing_set_alloc1)(throwing_set_alloc2))((default_generator))) +#endif RUN_TESTS() diff --git a/test/unordered/post_move_tests.cpp b/test/unordered/post_move_tests.cpp new file mode 100644 index 00000000..c0bb0aa7 --- /dev/null +++ b/test/unordered/post_move_tests.cpp @@ -0,0 +1,666 @@ + +// Copyright (C) 2022 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt) + +#include "../helpers/unordered.hpp" +#include "../helpers/test.hpp" +#include "../objects/test.hpp" +#include "../objects/cxx11_allocator.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" +#include "../helpers/invariants.hpp" + +#include +#include + +#if defined(BOOST_MSVC) +#pragma warning(disable : 4127) // conditional expression is constant +#endif + +namespace move_tests { + test::seed_t initialize_seed(98624); +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#define BOOST_UNORDERED_TEST_MOVING 1 +#else +#define BOOST_UNORDERED_TEST_MOVING 0 +#endif + + template T empty(T*) { return T(); } + + template + T create(test::random_values const& v, test::object_count& count) + { + T x(v.begin(), v.end()); + count = test::global_object_count; + return x; + } + + template + T create(test::random_values const& v, test::object_count& count, + typename T::hasher hf, typename T::key_equal eq, + typename T::allocator_type al, float mlf) + { + T x(0, hf, eq, al); + x.max_load_factor(mlf); + x.insert(v.begin(), v.end()); + count = test::global_object_count; + return x; + } + + template T const& get_key(T const& t) { return t; } + + template K const& get_key(std::pair const& kv) + { + return kv.first; + } + + template T const& get_value(T const& t) { return t; } + + template K const& get_value(std::pair const& kv) + { + return kv.second; + } + + template + static void insert_range(T& y, test::random_values const& v) + { + y.insert(v.begin(), v.end()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void insert_single(T& y, test::random_values const& v) + { + y.insert(*v.begin()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void insert_single_hint(T& y, test::random_values const& v) + { + y.insert(y.end(), *v.begin()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template struct insert_or_assign_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct insert_or_assign_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + + y.insert_or_assign(get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void insert_or_assign(T& y, test::random_values const& v) + { + insert_or_assign_invoker()(y, v); + } + + template struct insert_or_assign_hint_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct insert_or_assign_hint_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.insert_or_assign(y.end(), get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void insert_or_assign_hint(T& y, test::random_values const& v) + { + insert_or_assign_hint_invoker()(y, v); + } + + template struct try_emplace_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct try_emplace_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.try_emplace(get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void try_emplace(T& y, test::random_values const& v) + { + try_emplace_invoker()(y, v); + } + + template struct try_emplace_hint_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct try_emplace_hint_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y.try_emplace(y.end(), get_key(*v.begin()), get_value(*v.begin())); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void try_emplace_hint(T& y, test::random_values const& v) + { + try_emplace_hint_invoker()(y, v); + } + + template struct at_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct at_invoker > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + BOOST_TRY { y.at(get_key(*v.begin())); } + BOOST_CATCH(...) {} + BOOST_CATCH_END + } + }; + + template static void at(T& y, test::random_values const& v) + { + at_invoker()(y, v); + } + + template struct index_operator_invoker + { + void operator()(T&, test::random_values const&) {} + }; + + template + struct index_operator_invoker< + boost::unordered_map > + { + void operator()(boost::unordered_map& y, + test::random_values< + boost::unordered_map > const& v) + { + typedef typename boost::unordered_map::size_type size_type; + y[get_key(*v.begin())] = get_value(*v.begin()); + BOOST_TEST_EQ( + y.size(), static_cast(std::distance(y.begin(), y.end()))); + } + }; + + template + static void index_operator(T& y, test::random_values const& v) + { + index_operator_invoker()(y, v); + } + + template static void clear(T& y, test::random_values const&) + { + y.clear(); + BOOST_TEST(y.empty()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void capacity(T& y, test::random_values const&) + { + (void)y.empty(); + (void)y.size(); + (void)y.max_size(); + (void)y.load_factor(); + (void)y.max_load_factor(); + (void)y.hash_function(); + (void)y.key_eq(); + (void)y.get_allocator(); + } + + template static void iterators(T& y, test::random_values const&) + { + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void erase_range(T& y, test::random_values const&) + { + y.erase(y.begin(), y.end()); + BOOST_TEST(y.empty()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void erase_key(T& y, test::random_values const& v) + { + y.erase(get_key(*v.begin())); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void lookup(T& y, test::random_values const& v) + { + (void)y.count(get_key(*v.begin())); + (void)y.find(get_key(*v.begin())); + (void)y.contains(get_key(*v.begin())); + (void)y.equal_range(get_key(*v.begin())); + } + + template static void reserve(T& y, test::random_values const&) + { + y.reserve(1337); + BOOST_TEST_GT(y.bucket_count(), 1337u); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void copy_assignment(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + y = x; + BOOST_TEST_EQ(y.size(), x.size()); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void move_assignment(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + std::size_t const size = x.size(); + y = boost::move(x); + BOOST_TEST_GE(y.size(), size); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void equal(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + (void)(y == x); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void extract(T& y, test::random_values const& v) + { + (void)y.extract(get_key(*v.begin())); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void merge(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + if (y.get_allocator() == x.get_allocator()) { + y.merge(x); + } + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template bool pred(X const&) { return true; } + + template + static void erase_with_pred(T& y, test::random_values const&) + { + erase_if(y, pred); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template + static void container_swap(T& y, test::random_values const& v) + { + T x(v.begin(), v.end()); + if (boost::allocator_propagate_on_container_swap< + typename T::allocator_type>::type::value || + x.get_allocator() == y.get_allocator()) { + y.swap(x); + } + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + } + + template static void buckets(T& y, test::random_values const& v) + { + (void)y.begin(0); + (void)y.end(0); + (void)y.bucket_count(); + (void)y.max_bucket_count(); + (void)y.bucket_size(0); + (void)y.bucket(get_key(*v.begin())); + } + + template + static void double_move_construct(T& y, test::random_values const&) + { + T x = boost::move(y); + x.clear(); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + BOOST_TEST_EQ(x.size(), + static_cast(std::distance(x.begin(), x.end()))); + } + + template + static void double_move_assign(T& y, test::random_values const&) + { + T x; + x = boost::move(y); + x.clear(); + BOOST_TEST_EQ(y.size(), + static_cast(std::distance(y.begin(), y.end()))); + BOOST_TEST_EQ(x.size(), + static_cast(std::distance(x.begin(), x.end()))); + } + + template + static void post_move_tests(T* ptr, test::random_generator const& generator) + { + // clang-format off + void (*fps[])(T&, test::random_values const&) = { + insert_range, + insert_single, + insert_single_hint, + insert_or_assign, + insert_or_assign_hint, + try_emplace, + try_emplace_hint, + at, + index_operator, + clear, + capacity, + iterators, + erase_range, + erase_key, + lookup, + reserve, + copy_assignment, + move_assignment, + equal, + #ifndef BOOST_UNORDERED_FOA_TESTS + extract, + buckets, + #endif + merge, + erase_with_pred, + container_swap, + double_move_construct, + double_move_assign + }; + // clang-format on + + std::size_t const len = (sizeof(fps) / sizeof(*(fps))); + + for (std::size_t i = 0; i < len; ++i) { + test::check_instances check_; + + test::random_values const v(1000, generator); + test::object_count count; + T y(create(v, count)); + + unsigned num_allocs = test::detail::tracker.count_allocations; + (void)num_allocs; + + T x(boost::move(y)); + +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); + BOOST_TEST_EQ(y.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); +#endif + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + typename T::hasher hf(1); + typename T::key_equal eq(1); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); + + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(v.begin(), v.end(), 0, hf, eq, al1); + T x(boost::move(y), al2); + +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); +#else + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); +#endif + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(create(v, count)); + + unsigned num_allocs = test::detail::tracker.count_allocations; + (void)num_allocs; + + T x(empty(ptr)); + x = boost::move(y); + +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); + BOOST_TEST_EQ(y.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); +#endif + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + for (std::size_t i = 0; i < len; ++i) { + typename T::hasher hf(1); + typename T::key_equal eq(1); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); + + test::check_instances check_; + + test::random_values v(1000, generator); + test::object_count count; + T y(v.begin(), v.end(), 0, hf, eq, al1); + + unsigned num_allocs = test::detail::tracker.count_allocations; + (void)num_allocs; + + T x(al2); + x = boost::move(y); + + bool b = boost::allocator_propagate_on_container_move_assignment< + typename T::allocator_type>::type::value; + if (b) { +#if defined(BOOST_UNORDERED_USE_MOVE) || \ + !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); + BOOST_TEST_EQ(y.bucket_count(), 0u); + BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); +#else + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); + +#endif + } else { +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST(y.empty()); + BOOST_TEST(y.begin() == y.end()); +#else + BOOST_TEST_NOT(y.empty()); + BOOST_TEST(y.begin() != y.end()); +#endif + } + + fps[i](y, v); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + } + + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map > >* + test_map_std_alloc; + + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map > >* test_map; + + boost::unordered_flat_set >* + test_set_prop_move; + boost::unordered_flat_map, + test::propagate_move> >* test_map_prop_move; + + boost::unordered_flat_set >* + test_set_no_prop_move; + boost::unordered_flat_map, + test::no_propagate_move> >* test_map_no_prop_move; + + UNORDERED_TEST(post_move_tests, + ((test_set)(test_map)(test_set_prop_move)(test_map_prop_move)(test_set_no_prop_move)(test_map_no_prop_move))( + (default_generator)(generate_collisions)(limited_range))) +#else + boost::unordered_map > >* + test_map_std_alloc; + + boost::unordered_set >* test_set; + boost::unordered_multiset >* test_multiset; + boost::unordered_map > >* test_map; + boost::unordered_multimap > >* + test_multimap; + + boost::unordered_set >* + test_set_prop_move; + boost::unordered_multiset >* + test_multiset_prop_move; + boost::unordered_map, + test::propagate_move> >* test_map_prop_move; + boost::unordered_multimap, + test::propagate_move> >* test_multimap_prop_move; + + boost::unordered_set >* + test_set_no_prop_move; + boost::unordered_multiset >* + test_multiset_no_prop_move; + boost::unordered_map, + test::no_propagate_move> >* test_map_no_prop_move; + boost::unordered_multimap, + test::no_propagate_move> >* test_multimap_no_prop_move; + + UNORDERED_TEST(post_move_tests, + ((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)( + test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)( + test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)( + test_multimap_no_prop_move))( + (default_generator)(generate_collisions)(limited_range))) +#endif +} + +RUN_TESTS() diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp index 4eb5598b..d543cdda 100644 --- a/test/unordered/rehash_tests.cpp +++ b/test/unordered/rehash_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include "../helpers/random_values.hpp" @@ -151,8 +147,13 @@ namespace rehash_tests { BOOST_TEST_EQ(x.size(), 0u); BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u); +#ifdef BOOST_UNORDERED_FOA_TESTS + float const mlf = boost::unordered::detail::foa::mlf; + x.max_load_factor(max_load_factors[i]); +#else float const mlf = max_load_factors[i]; x.max_load_factor(mlf); +#endif { BOOST_TEST_EQ(x.bucket_count(), 0u); @@ -419,6 +420,15 @@ namespace rehash_tests { X x; x.max_load_factor(0.25); +#ifdef BOOST_UNORDERED_FOA_TESTS + x.reserve(10000); + BOOST_TEST(x.bucket_count() >= 10000); + + x.reserve(0); + + x.reserve(10000000); + BOOST_TEST(x.bucket_count() >= 10000000); +#else x.reserve(10000); BOOST_TEST(x.bucket_count() >= 40000); @@ -426,6 +436,7 @@ namespace rehash_tests { x.reserve(10000000); BOOST_TEST(x.bucket_count() >= 40000000); +#endif } template void reserve_test1(X*, test::random_generator generator) @@ -486,6 +497,47 @@ namespace rehash_tests { } } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set* int_set_ptr; + boost::unordered_flat_map >* test_map_ptr; + + boost::unordered_flat_set >* test_set_tracking; + boost::unordered_flat_map > >* + test_map_tracking; + + UNORDERED_TEST(rehash_empty_test1, ((int_set_ptr)(test_map_ptr))) + UNORDERED_TEST(rehash_empty_test2, + ((int_set_ptr)(test_map_ptr))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(rehash_empty_test3, + ((int_set_ptr)(test_map_ptr))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST( + rehash_test1, ((int_set_ptr)(test_map_ptr))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(reserve_empty_test1, ((int_set_ptr)(test_map_ptr))) + UNORDERED_TEST(reserve_empty_test2, ((int_set_ptr)(test_map_ptr))) + UNORDERED_TEST( + reserve_test1, ((int_set_ptr)(test_map_ptr))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST( + reserve_test2, ((int_set_ptr)(test_map_ptr))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(rehash_empty_tracking, + ((test_set_tracking)(test_map_tracking))( + (default_generator)(generate_collisions)(limited_range))) + UNORDERED_TEST(rehash_nonempty_tracking, + ((test_set_tracking)(test_map_tracking))( + (default_generator)(generate_collisions)(limited_range))) +#else boost::unordered_set* int_set_ptr; boost::unordered_multiset >* test_multiset_ptr; @@ -505,10 +557,6 @@ namespace rehash_tests { test::allocator1 > >* test_multimap_tracking; - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - UNORDERED_TEST(rehash_empty_test1, ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr))) UNORDERED_TEST(rehash_empty_test2, @@ -536,6 +584,7 @@ namespace rehash_tests { UNORDERED_TEST(rehash_nonempty_tracking, ((test_set_tracking)(test_multiset_tracking)(test_map_tracking)(test_multimap_tracking))( (default_generator)(generate_collisions)(limited_range))) +#endif } RUN_TESTS() diff --git a/test/unordered/reserve_tests.cpp b/test/unordered/reserve_tests.cpp index f0d83b91..ca7f9809 100644 --- a/test/unordered/reserve_tests.cpp +++ b/test/unordered/reserve_tests.cpp @@ -2,12 +2,7 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" @@ -113,8 +108,8 @@ template void reserve_tests() s.max_load_factor(0.37f); s.reserve(count); - std::size_t expected_bucket_count = - static_cast(std::ceil(static_cast(count) / 0.37f)); + std::size_t expected_bucket_count = static_cast( + std::ceil(static_cast(count) / s.max_load_factor())); BOOST_TEST_GE(total_allocation, expected_bucket_count * sizeof(void*)); BOOST_TEST_GE(s.bucket_count(), expected_bucket_count); @@ -173,6 +168,12 @@ template void rehash_tests() // note, the test is vulnerable to cases where the next calculated bucket // count can exceed `prev_count + count` // +#ifdef BOOST_UNORDERED_FOA_TESTS + BOOST_TEST_LT(s.bucket_count(), prev_count + count); + BOOST_TEST_LE(total_allocation, + (prev_count + count) * sizeof(typename UnorderedContainer::value_type) + + ((prev_count + count) / 15 + 1) * 16); +#else std::size_t const estimated_bucket_group_size = 3 * sizeof(void*) + sizeof(std::size_t); std::size_t const estimated_bucket_groups = @@ -182,6 +183,7 @@ template void rehash_tests() BOOST_TEST_LE(total_allocation, (prev_count + count) * sizeof(void*) + estimated_bucket_group_size * estimated_bucket_groups); +#endif } BOOST_TEST_GT(num_allocations, 0u); @@ -214,6 +216,28 @@ UNORDERED_AUTO_TEST (unordered_set_reserve) { BOOST_ASSERT(A(b) == a); } +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set, + std::equal_to, A > + unordered_set; + + typedef boost::unordered_flat_map, + std::equal_to, A > > + unordered_map; + + bucket_count_constructor(); + bucket_count_constructor(); + + range_bucket_constructor(); + range_bucket_constructor(); + + reserve_tests(); + reserve_tests(); + + rehash_tests(); + rehash_tests(); + +#else typedef boost::unordered_set, std::equal_to, A > unordered_set; @@ -249,6 +273,7 @@ UNORDERED_AUTO_TEST (unordered_set_reserve) { rehash_tests(); rehash_tests(); rehash_tests(); +#endif } RUN_TESTS() diff --git a/test/unordered/scary_tests.cpp b/test/unordered/scary_tests.cpp index 383af13a..f4508519 100644 --- a/test/unordered/scary_tests.cpp +++ b/test/unordered/scary_tests.cpp @@ -2,14 +2,8 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on - #include "../helpers/test.hpp" +#include "../helpers/unordered.hpp" #include "../objects/test.hpp" #include @@ -107,8 +101,7 @@ public: // ever using it with an int with has a trivial destructor so it eliminates // the code and generates a warning about an unused variable // - template - void destroy(U* p) + template void destroy(U* p) { (void)p; p->~U(); @@ -192,8 +185,7 @@ public: // ever using it with an int with has a trivial destructor so it eliminates // the code and generates a warning about an unused variable // - template - void destroy(U* p) + template void destroy(U* p) { (void)p; p->~U(); @@ -226,11 +218,13 @@ template void scary_test() BOOST_TEST_EQ(x.bucket_count(), 0u); +#ifndef BOOST_UNORDERED_FOA_TESTS typename C2::local_iterator lbegin(x.begin(0)); BOOST_TEST(lbegin == x.end(0)); typename C2::const_local_iterator clbegin(x.cbegin(0)); BOOST_TEST(clbegin == x.cend(0)); +#endif } template < @@ -316,11 +310,16 @@ void set_scary_test() } UNORDERED_AUTO_TEST (scary_tests) { +#ifdef BOOST_UNORDERED_FOA_TESTS + map_scary_test(); + set_scary_test(); +#else map_scary_test(); map_scary_test(); set_scary_test(); set_scary_test(); +#endif } RUN_TESTS() diff --git a/test/unordered/scoped_allocator.cpp b/test/unordered/scoped_allocator.cpp index a8e02c8b..f554deff 100644 --- a/test/unordered/scoped_allocator.cpp +++ b/test/unordered/scoped_allocator.cpp @@ -19,12 +19,7 @@ int main() {} // https://github.com/boostorg/unordered/issues/22 // -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" @@ -32,10 +27,10 @@ int main() {} #include +#include #include #include #include -#include namespace test { template struct allocator @@ -70,9 +65,15 @@ typedef std::scoped_allocator_adaptor, test::allocator > allocator_type; +#ifdef BOOST_UNORDERED_FOA_TESTS +typedef boost::unordered_flat_map, std::equal_to, allocator_type> + map_type; +#else typedef boost::unordered_map, std::equal_to, allocator_type> map_type; +#endif UNORDERED_AUTO_TEST (scoped_allocator) { allocator_type alloc( diff --git a/test/unordered/simple_tests.cpp b/test/unordered/simple_tests.cpp index d8a9ce41..dbbf7a48 100644 --- a/test/unordered/simple_tests.cpp +++ b/test/unordered/simple_tests.cpp @@ -1,16 +1,12 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // This test checks the runtime requirements of containers. -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" #include @@ -93,7 +89,11 @@ UNORDERED_AUTO_TEST (simple_tests) { srand(14878); BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n"; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_set set; +#else boost::unordered_set set; +#endif simple_test(set); set.insert(1); @@ -101,6 +101,7 @@ UNORDERED_AUTO_TEST (simple_tests) { set.insert(1456); simple_test(set); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n"; boost::unordered_multiset multiset; simple_test(multiset); @@ -111,15 +112,21 @@ UNORDERED_AUTO_TEST (simple_tests) { multiset.insert(index); } simple_test(multiset); +#endif BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n"; +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map map; +#else boost::unordered_map map; +#endif for (int i2 = 0; i2 < 1000; ++i2) { map.insert(std::pair(rand(), rand())); } simple_test(map); +#ifndef BOOST_UNORDERED_FOA_TESTS BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n"; boost::unordered_multimap multimap; @@ -129,6 +136,7 @@ UNORDERED_AUTO_TEST (simple_tests) { multimap.insert(std::pair(index, rand())); } simple_test(multimap); +#endif } RUN_TESTS() diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp index 9a7106ef..6f41c876 100644 --- a/test/unordered/swap_tests.cpp +++ b/test/unordered/swap_tests.cpp @@ -1,14 +1,10 @@ // Copyright 2006-2009 Daniel James. +// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include #include @@ -139,6 +135,53 @@ namespace swap_tests { } } + using test::default_generator; + using test::generate_collisions; + using test::limited_range; + + template bool is_propagate(T*) + { + return T::allocator_type::is_propagate_on_swap; + } + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_flat_map >* test_map_std_alloc; + + boost::unordered_flat_set >* test_set; + boost::unordered_flat_map >* test_map; + + boost::unordered_flat_set >* + test_set_prop_swap; + boost::unordered_flat_map >* + test_map_prop_swap; + + boost::unordered_flat_set >* + test_set_no_prop_swap; + boost::unordered_flat_map >* + test_map_no_prop_swap; + + UNORDERED_AUTO_TEST (check_traits) { + BOOST_TEST(!is_propagate(test_set)); + BOOST_TEST(is_propagate(test_set_prop_swap)); + BOOST_TEST(!is_propagate(test_set_no_prop_swap)); + } + + UNORDERED_TEST(swap_tests1, + ((test_map_std_alloc)(test_set)(test_map)(test_set_prop_swap)(test_map_prop_swap)(test_set_no_prop_swap)(test_map_no_prop_swap))( + (default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(swap_tests2, + ((test_set)(test_map)(test_set_prop_swap)(test_map_prop_swap)(test_set_no_prop_swap)(test_map_no_prop_swap))( + (default_generator)(generate_collisions)(limited_range))) +#else boost::unordered_map >* test_map_std_alloc; @@ -178,15 +221,6 @@ namespace swap_tests { test::cxx11_allocator >* test_multimap_no_prop_swap; - template bool is_propagate(T*) - { - return T::allocator_type::is_propagate_on_swap; - } - - using test::default_generator; - using test::generate_collisions; - using test::limited_range; - UNORDERED_AUTO_TEST (check_traits) { BOOST_TEST(!is_propagate(test_set)); BOOST_TEST(is_propagate(test_set_prop_swap)); @@ -207,5 +241,6 @@ namespace swap_tests { test_set_no_prop_swap)(test_multiset_no_prop_swap)(test_map_no_prop_swap)( test_multimap_no_prop_swap))( (default_generator)(generate_collisions)(limited_range))) +#endif } RUN_TESTS() diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 50f6bfdc..9852fa84 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -2,12 +2,7 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on +#include "../helpers/unordered.hpp" #include "../helpers/test.hpp" @@ -1053,14 +1048,25 @@ template struct convertible_to_const_iterator } }; +#ifdef BOOST_UNORDERED_FOA_TESTS +typedef boost::unordered_flat_map + transparent_unordered_map; +#else typedef boost::unordered_map transparent_unordered_map; +#endif // test that in the presence of the member function template `erase()`, we still // invoke the correct iterator overloads when the type is implicitly convertible // -transparent_unordered_map::iterator map_erase_overload_compile_test() +#ifdef BOOST_UNORDERED_FOA_TESTS +void +#else +transparent_unordered_map::iterator +#endif +map_erase_overload_compile_test() { convertible_to_iterator c; transparent_unordered_map map; @@ -1069,7 +1075,11 @@ transparent_unordered_map::iterator map_erase_overload_compile_test() return map.erase(c); } +#ifdef BOOST_UNORDERED_FOA_TESTS +void +#else transparent_unordered_map::const_iterator +#endif map_erase_const_overload_compile_test() { convertible_to_const_iterator c; @@ -1079,6 +1089,7 @@ map_erase_const_overload_compile_test() return map.erase(c); } +#ifndef BOOST_UNORDERED_FOA_TESTS typedef boost::unordered_multimap transparent_unordered_multimap; @@ -1101,6 +1112,7 @@ multimap_erase_const_overload_compile_test() pos = c; return map.erase(c); } +#endif template void test_map_transparent_erase() { @@ -1197,14 +1209,25 @@ template void test_map_non_transparent_erase() BOOST_TEST_EQ(key::count_, key_count); } +#if BOOST_UNORDERED_FOA_TESTS +typedef boost::unordered_flat_set + transparent_unordered_set; +#else typedef boost::unordered_set transparent_unordered_set; typedef boost::unordered_multiset transparent_unordered_multiset; +#endif -transparent_unordered_set::iterator set_erase_overload_compile_test() +#ifdef BOOST_UNORDERED_FOA_TESTS +void +#else +transparent_unordered_set::iterator +#endif +set_erase_overload_compile_test() { convertible_to_iterator c; transparent_unordered_set set; @@ -1213,7 +1236,11 @@ transparent_unordered_set::iterator set_erase_overload_compile_test() return set.erase(c); } +#ifdef BOOST_UNORDERED_FOA_TESTS +void +#else transparent_unordered_set::const_iterator +#endif set_erase_const_overload_compile_test() { convertible_to_const_iterator c; @@ -1223,6 +1250,7 @@ set_erase_const_overload_compile_test() return set.erase(c); } +#ifndef BOOST_UNORDERED_FOA_TESTS transparent_unordered_multiset::iterator multiset_erase_overload_compile_test() { convertible_to_iterator c; @@ -1241,6 +1269,7 @@ multiset_erase_const_overload_compile_test() pos = c; return set.erase(c); } +#endif template void test_set_transparent_erase() { @@ -1337,6 +1366,7 @@ template void test_set_non_transparent_erase() BOOST_TEST_EQ(key::count_, key_count); } +#ifndef BOOST_UNORDERED_FOA_TESTS // test that in the presence of the member function template `extract()`, we // still invoke the correct iterator overloads when the type is implicitly // convertible @@ -1359,9 +1389,11 @@ multimap_extract_const_overload_compile_test() pos = c; return map.extract(c); } +#endif template void test_map_transparent_extract() { +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename UnorderedMap::node_type node_type; typedef typename UnorderedMap::const_iterator const_iterator; typedef std::pair const_iterator_pair; @@ -1397,10 +1429,12 @@ template void test_map_transparent_extract() BOOST_TEST(nh.empty()); BOOST_TEST_EQ(key::count_, expected_key_count); +#endif } template void test_map_non_transparent_extract() { +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename UnorderedMap::node_type node_type; typedef typename UnorderedMap::const_iterator const_iterator; typedef std::pair const_iterator_pair; @@ -1441,8 +1475,10 @@ template void test_map_non_transparent_extract() ++key_count; BOOST_TEST(nh.empty()); BOOST_TEST_EQ(key::count_, key_count); +#endif } +#ifndef BOOST_UNORDERED_FOA_TESTS transparent_unordered_set::node_type set_extract_overload_compile_test() { convertible_to_iterator c; @@ -1480,9 +1516,11 @@ multiset_extract_const_overload_compile_test() pos = c; return set.extract(c); } +#endif template void test_set_transparent_extract() { +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename UnorderedSet::node_type node_type; count_reset(); @@ -1526,10 +1564,12 @@ template void test_set_transparent_extract() BOOST_TEST_EQ(set.size(), set_size); BOOST_TEST_EQ(key::count_, expected_key_count); +#endif } template void test_set_non_transparent_extract() { +#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename UnorderedSet::node_type node_type; count_reset(); @@ -1583,13 +1623,22 @@ template void test_set_non_transparent_extract() BOOST_TEST_EQ(set.size(), set_size); BOOST_TEST_EQ(key::count_, key_count); +#endif } +template struct map_type +{ +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_map type; +#else + typedef boost::unordered_map type; +#endif +}; + void test_unordered_map() { { - typedef boost::unordered_map + typedef map_type::type unordered_map; test_map_transparent_count(); @@ -1602,7 +1651,7 @@ void test_unordered_map() { // non-transparent Hash, non-transparent KeyEqual // - typedef boost::unordered_map unordered_map; + typedef map_type::type unordered_map; test_map_non_transparent_count(); test_map_non_transparent_find(); @@ -1614,7 +1663,7 @@ void test_unordered_map() { // transparent Hash, non-transparent KeyEqual // - typedef boost::unordered_map + typedef map_type::type unordered_map; test_map_non_transparent_count(); @@ -1627,7 +1676,7 @@ void test_unordered_map() { // non-transparent Hash, transparent KeyEqual // - typedef boost::unordered_map + typedef map_type::type unordered_map; test_map_non_transparent_count(); @@ -1638,6 +1687,7 @@ void test_unordered_map() } } +#ifndef BOOST_UNORDERED_FOA_TESTS void test_unordered_multimap() { { @@ -1691,11 +1741,21 @@ void test_unordered_multimap() test_map_non_transparent_extract(); } } +#endif + +template struct set_type +{ +#ifdef BOOST_UNORDERED_FOA_TESTS + typedef boost::unordered_flat_set type; +#else + typedef boost::unordered_set type; +#endif +}; void test_unordered_set() { { - typedef boost::unordered_set + typedef set_type::type unordered_set; test_set_transparent_count(); @@ -1708,7 +1768,7 @@ void test_unordered_set() { // non-transparent Hash, non-transparent KeyEqual // - typedef boost::unordered_set unordered_set; + typedef set_type::type unordered_set; test_set_non_transparent_count(); test_set_non_transparent_find(); @@ -1720,8 +1780,7 @@ void test_unordered_set() { // transparent Hash, non-transparent KeyEqual // - typedef boost::unordered_set - unordered_set; + typedef set_type::type unordered_set; test_set_non_transparent_count(); test_set_non_transparent_find(); @@ -1733,8 +1792,7 @@ void test_unordered_set() { // non-transparent Hash, transparent KeyEqual // - typedef boost::unordered_set - unordered_set; + typedef set_type::type unordered_set; test_set_non_transparent_count(); test_set_non_transparent_find(); @@ -1744,6 +1802,7 @@ void test_unordered_set() } } +#ifndef BOOST_UNORDERED_FOA_TESTS void test_unordered_multiset() { { @@ -1796,12 +1855,16 @@ void test_unordered_multiset() test_set_non_transparent_extract(); } } +#endif UNORDERED_AUTO_TEST (transparent_ops) { test_unordered_map(); - test_unordered_multimap(); test_unordered_set(); + +#ifndef BOOST_UNORDERED_FOA_TESTS + test_unordered_multimap(); test_unordered_multiset(); +#endif } RUN_TESTS()