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/.drone.jsonnet b/.drone.jsonnet index 41a73d36..70221837 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -138,31 +138,217 @@ local windows_pipeline(name, image, environment, arch = "amd64") = ), linux_pipeline( - "Linux 20.04 GCC 9* ARM64 32", + "Linux 16.04 GCC 5* 32/64", + "cppalliance/droneubuntu1604:1", + { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14', ADDRMD: '32,64' }, + ), + + linux_pipeline( + "Linux 18.04 GCC 6 32/64", + "cppalliance/droneubuntu1804:1", + { TOOLSET: 'gcc', COMPILER: 'g++-6', CXXSTD: '03,11,14', ADDRMD: '32,64' }, + "g++-6-multilib", + ), + + linux_pipeline( + "Linux 18.04 GCC 7* 32/64", + "cppalliance/droneubuntu1804:1", + { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17', ADDRMD: '32,64' }, + ), + + linux_pipeline( + "Linux 18.04 GCC 8 32/64", + "cppalliance/droneubuntu1804:1", + { TOOLSET: 'gcc', COMPILER: 'g++-8', CXXSTD: '03,11,14,17', ADDRMD: '32,64' }, + "g++-8-multilib", + ), + + linux_pipeline( + "Linux 20.04 GCC 9* 32/64", + "cppalliance/droneubuntu2004:1", + { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32,64' }, + ), + + linux_pipeline( + "Linux 20.04 GCC 9* ARM64", "cppalliance/droneubuntu2004:multiarch", - { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32' }, + { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' }, arch="arm64", ), linux_pipeline( - "Linux 20.04 GCC 9* ARM64 64", + "Linux 20.04 GCC 9* S390x", "cppalliance/droneubuntu2004:multiarch", - { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '64' }, - arch="arm64", - ), - - linux_pipeline( - "Linux 20.04 GCC 9* S390x 32", - "cppalliance/droneubuntu2004:multiarch", - { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32' }, + { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' }, arch="s390x", ), linux_pipeline( - "Linux 20.04 GCC 9* S390x 64", - "cppalliance/droneubuntu2004:multiarch", - { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '64' }, - arch="s390x", + "Linux 20.04 GCC 10 32/64", + "cppalliance/droneubuntu2004:1", + { TOOLSET: 'gcc', COMPILER: 'g++-10', CXXSTD: '03,11,14,17,20', ADDRMD: '32,64' }, + "g++-10-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 11* 32/64", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32,64' }, + ), + + linux_pipeline( + "Linux 22.04 GCC 12 32 ASAN (03,11,14)", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14', ADDRMD: '32' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 12 32 ASAN (17,20,2b)", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '17,20,2b', ADDRMD: '32' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 12 64 ASAN (03,11,14)", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14', ADDRMD: '64' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 12 64 ASAN (17,20,2b)", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '17,20,2b', ADDRMD: '64' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 16.04 Clang 3.5", + "cppalliance/droneubuntu1604:1", + { TOOLSET: 'clang', COMPILER: 'clang++-3.5', CXXSTD: '03,11' }, + "clang-3.5", + ), + + linux_pipeline( + "Linux 16.04 Clang 3.6", + "cppalliance/droneubuntu1604:1", + { TOOLSET: 'clang', COMPILER: 'clang++-3.6', CXXSTD: '03,11,14' }, + "clang-3.6", + ), + + linux_pipeline( + "Linux 16.04 Clang 3.7", + "cppalliance/droneubuntu1604:1", + { TOOLSET: 'clang', COMPILER: 'clang++-3.7', CXXSTD: '03,11,14' }, + "clang-3.7", + ), + + linux_pipeline( + "Linux 16.04 Clang 3.8", + "cppalliance/droneubuntu1604:1", + { TOOLSET: 'clang', COMPILER: 'clang++-3.8', CXXSTD: '03,11,14' }, + "clang-3.8", + ), + + linux_pipeline( + "Linux 18.04 Clang 3.9", + "cppalliance/droneubuntu1804:1", + { TOOLSET: 'clang', COMPILER: 'clang++-3.9', CXXSTD: '03,11,14' }, + "clang-3.9", + ), + + linux_pipeline( + "Linux 18.04 Clang 4.0", + "cppalliance/droneubuntu1804:1", + { TOOLSET: 'clang', COMPILER: 'clang++-4.0', CXXSTD: '03,11,14' }, + "clang-4.0", + ), + + linux_pipeline( + "Linux 18.04 Clang 5.0", + "cppalliance/droneubuntu1804:1", + { TOOLSET: 'clang', COMPILER: 'clang++-5.0', CXXSTD: '03,11,14,1z' }, + "clang-5.0", + ), + + linux_pipeline( + "Linux 18.04 Clang 6.0", + "cppalliance/droneubuntu1804:1", + { TOOLSET: 'clang', COMPILER: 'clang++-6.0', CXXSTD: '03,11,14,17' }, + "clang-6.0", + ), + + linux_pipeline( + "Linux 20.04 Clang 7", + "cppalliance/droneubuntu2004:1", + { TOOLSET: 'clang', COMPILER: 'clang++-7', CXXSTD: '03,11,14,17' }, + "clang-7", + ), + + linux_pipeline( + "Linux 20.04 Clang 8", + "cppalliance/droneubuntu2004:1", + { TOOLSET: 'clang', COMPILER: 'clang++-8', CXXSTD: '03,11,14,17' }, + "clang-8", + ), + + linux_pipeline( + "Linux 20.04 Clang 9", + "cppalliance/droneubuntu2004:1", + { TOOLSET: 'clang', COMPILER: 'clang++-9', CXXSTD: '03,11,14,17,2a' }, + "clang-9", + ), + + linux_pipeline( + "Linux 20.04 Clang 10", + "cppalliance/droneubuntu2004:1", + { TOOLSET: 'clang', COMPILER: 'clang++-10', CXXSTD: '03,11,14,17,2a' }, + "clang-10", + ), + + linux_pipeline( + "Linux 20.04 Clang 11", + "cppalliance/droneubuntu2004:1", + { TOOLSET: 'clang', COMPILER: 'clang++-11', CXXSTD: '03,11,14,17,2a' }, + "clang-11", + ), + + linux_pipeline( + "Linux 20.04 Clang 12", + "cppalliance/droneubuntu2004:1", + { TOOLSET: 'clang', COMPILER: 'clang++-12', CXXSTD: '03,11,14,17,2a' }, + "clang-12", + ), + + linux_pipeline( + "Linux 22.04 Clang 13", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'clang', COMPILER: 'clang++-13', CXXSTD: '03,11,14,17,20' }, + "clang-13", + ), + + linux_pipeline( + "Linux 22.04 Clang 14 UBSAN", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20' } + ubsan, + "clang-14", + ), + + linux_pipeline( + "Linux 22.04 Clang 14 ASAN", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20' } + asan, + "clang-14", + ), + + linux_pipeline( + "Linux 22.04 Clang 15", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'clang', COMPILER: 'clang++-15', CXXSTD: '03,11,14,17,20,2b' }, + "clang-15", + ["deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"], ), macos_pipeline( @@ -176,9 +362,27 @@ local windows_pipeline(name, image, environment, arch = "amd64") = xcode_version = "13.4.1", osx_version = "monterey", ), + windows_pipeline( + "Windows VS2015 msvc-14.0", + "cppalliance/dronevs2015", + { TOOLSET: 'msvc-14.0', CXXSTD: '14,latest' }, + ), + windows_pipeline( "Windows VS2017 msvc-14.1", "cppalliance/dronevs2017", { TOOLSET: 'msvc-14.1', CXXSTD: '14,17,latest' }, ), + + windows_pipeline( + "Windows VS2019 msvc-14.2", + "cppalliance/dronevs2019", + { TOOLSET: 'msvc-14.2', CXXSTD: '14,17,20,latest' }, + ), + + windows_pipeline( + "Windows VS2022 msvc-14.3", + "cppalliance/dronevs2022:1", + { TOOLSET: 'msvc-14.3', CXXSTD: '14,17,20,latest' }, + ), ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d75d225..d701cf6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,45 +42,37 @@ jobs: matrix: include: # Linux, gcc - - { compiler: gcc-4.8, cxxstd: '03,11', os: ubuntu-18.04, install: 'g++-4.8-multilib', address-model: '32,64' } - - { compiler: gcc-4.9, cxxstd: '03,11', os: ubuntu-20.04, container: 'ubuntu:16.04' } - - { compiler: gcc-5, cxxstd: '03,11,14,1z', os: ubuntu-18.04, install: 'g++-5-multilib', address-model: '32,64' } - - { compiler: gcc-6, cxxstd: '03,11,14,17', os: ubuntu-18.04, install: 'g++-6-multilib', address-model: '32,64' } - - { compiler: gcc-7, cxxstd: '03,11,14,17', os: ubuntu-18.04, install: 'g++-7-multilib', address-model: '32,64' } - - { compiler: gcc-8, cxxstd: '03,11,14,17,2a', os: ubuntu-18.04, install: 'g++-8-multilib', address-model: '32,64' } - - { compiler: gcc-9, cxxstd: '03,11,14,17,2a', os: ubuntu-18.04, install: 'g++-9-multilib', address-model: '32,64' } - - { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, install: 'g++-10-multilib', address-model: '32,64' } - - { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, install: 'g++-11-multilib', address-model: '32,64' } - - { compiler: gcc-12, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-12-multilib', address-model: '32,64' } - - { name: GCC w/ sanitizers, sanitize: yes, - compiler: gcc-12, cxxstd: '03,11,14,17,20', os: ubuntu-22.04 } + - { compiler: gcc-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-7' } + - { compiler: gcc-8, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-8' } + - { compiler: gcc-9, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-9' } + - { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-10' } + - { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-11' } + - { name: "gcc-12 w/ sanitizers (03,11,14)", sanitize: yes, + compiler: gcc-12, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" } + - { name: "gcc-12 w/ sanitizers (17,20,2b)", sanitize: yes, + compiler: gcc-12, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" } - { name: Collect coverage, coverage: yes, - compiler: gcc-8, cxxstd: '03,11', os: ubuntu-20.04, install: 'g++-8-multilib', address-model: '32,64' } + compiler: gcc-8, cxxstd: '03,11', os: ubuntu-20.04, install: 'g++-8-multilib', address-model: '32,64', ccache_key: "cov" } - # Linux, clang - - { compiler: clang-3.7, cxxstd: '03,11,14', os: ubuntu-20.04, container: 'ubuntu:16.04' } - - { compiler: clang-3.8, cxxstd: '03,11,14', os: ubuntu-20.04, container: 'ubuntu:16.04' } - - { compiler: clang-3.9, cxxstd: '03,11,14', os: ubuntu-18.04 } - - { compiler: clang-4.0, cxxstd: '03,11,14', os: ubuntu-18.04 } - - { compiler: clang-5.0, cxxstd: '03,11,14,1z', os: ubuntu-18.04 } - - { compiler: clang-6.0, cxxstd: '03,11,14,17', os: ubuntu-18.04 } - - { compiler: clang-7, cxxstd: '03,11,14,17', os: ubuntu-18.04 } - - { compiler: clang-8, cxxstd: '03,11,14,17', os: ubuntu-18.04 } - - { compiler: clang-9, cxxstd: '03,11,14,17,2a', os: ubuntu-20.04 } - - { compiler: clang-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04 } - - { compiler: clang-11, cxxstd: '03,11,14,17,20', os: ubuntu-20.04 } - - { compiler: clang-12, cxxstd: '03,11,14,17,20', os: ubuntu-20.04 } - - { compiler: clang-13, cxxstd: '03,11,14,17,20', os: ubuntu-22.04 } - - { compiler: clang-14, cxxstd: '03,11,14,17,20', os: ubuntu-22.04 } - - # libc++ - - { compiler: clang-6.0, cxxstd: '03,11,14', os: ubuntu-18.04, stdlib: libc++, install: 'clang-6.0 libc++-dev libc++abi-dev' } - - { compiler: clang-12, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev' } - - { name: Clang w/ sanitizers, sanitize: yes, - compiler: clang-12, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev' } + # Linux, clang, libc++ + - { compiler: clang-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' } + - { compiler: clang-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' } + - { compiler: clang-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, stdlib: libc++, install: 'clang-11 libc++-11-dev libc++abi-11-dev' } + # clang-12 doesn't work on ubuntu-22.04, the linker can't find -lunwind for some reason + - { name: "clang-12 w/ sanitizers (03,11,14)", sanitize: yes, + compiler: clang-12, cxxstd: '03,11,14', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san1" } + - { name: "clang-12 w/ sanitizers (17,20,2b)", sanitize: yes, + compiler: clang-12, cxxstd: '17,20,2b', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" } + - { compiler: clang-13, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' } + - { compiler: clang-14, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' } + # not using libc++ because of https://github.com/llvm/llvm-project/issues/52771 + - { name: "clang-14 w/ sanitizers (03,11,14)", sanitize: yes, + compiler: clang-14, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" } + - { name: "clang-14 w/ sanitizers (17,20,2b)", sanitize: yes, + compiler: clang-14, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" } # OSX, clang - - { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, sanitize: yes } + - { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, sanitize: yes } timeout-minutes: 120 runs-on: ${{matrix.os}} @@ -119,8 +111,8 @@ jobs: if: env.B2_USE_CCACHE with: path: ~/.ccache - key: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}-${{github.sha}} - restore-keys: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}- + key: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}-${{matrix.ccache_key}}-${{github.sha}} + restore-keys: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}-${{matrix.ccache_key}}- - name: Fetch Boost.CI uses: actions/checkout@v3 diff --git a/benchmark/string.cpp b/benchmark/string.cpp index 9e72246d..19ab4eb8 100644 --- a/benchmark/string.cpp +++ b/benchmark/string.cpp @@ -5,6 +5,7 @@ #define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING #include +#include #include #include #include @@ -294,6 +295,9 @@ template using std_unordered_map = template using boost_unordered_map = boost::unordered_map, std::equal_to, allocator_for>; +template using boost_unordered_flat_map = + boost::unordered_flat_map, std::equal_to, allocator_for>; + #ifdef HAVE_ABSEIL template using absl_node_hash_map = @@ -366,7 +370,10 @@ template<> struct fnv1a_hash_impl<64> } }; -struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits::digits > {}; +struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits::digits > +{ + using is_avalanching = void; +}; template using std_unordered_map_fnv1a = std::unordered_map, allocator_for>; @@ -374,6 +381,9 @@ std::unordered_map, allocator_for>; template using boost_unordered_map_fnv1a = boost::unordered_map, allocator_for>; +template using boost_unordered_flat_map_fnv1a = + boost::unordered_flat_map, allocator_for>; + template using multi_index_map_fnv1a = multi_index_container< pair, indexed_by< @@ -418,10 +428,11 @@ int main() { init_indices(); -#if 0 +#if 1 test( "std::unordered_map" ); test( "boost::unordered_map" ); + test( "boost::unordered_flat_map" ); test( "multi_index_map" ); #ifdef HAVE_ABSEIL @@ -449,6 +460,7 @@ int main() test( "std::unordered_map, FNV-1a" ); test( "boost::unordered_map, FNV-1a" ); + test( "boost::unordered_flat_map, FNV-1a" ); test( "multi_index_map, FNV-1a" ); #ifdef HAVE_ABSEIL @@ -476,7 +488,7 @@ int main() for( auto const& x: times ) { - std::cout << std::setw( 31 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; + std::cout << std::setw( 35 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; } } diff --git a/benchmark/string_view.cpp b/benchmark/string_view.cpp index 826c964f..a7236782 100644 --- a/benchmark/string_view.cpp +++ b/benchmark/string_view.cpp @@ -5,6 +5,7 @@ #define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING #include +#include #include #include #include @@ -295,6 +296,9 @@ template using std_unordered_map = template using boost_unordered_map = boost::unordered_map, std::equal_to, allocator_for>; +template using boost_unordered_flat_map = + boost::unordered_flat_map, std::equal_to, allocator_for>; + #ifdef HAVE_ABSEIL template using absl_node_hash_map = @@ -367,7 +371,10 @@ template<> struct fnv1a_hash_impl<64> } }; -struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits::digits > {}; +struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits::digits > +{ + using is_avalanching = void; +}; template using std_unordered_map_fnv1a = std::unordered_map, allocator_for>; @@ -375,6 +382,9 @@ std::unordered_map, allocator_for>; template using boost_unordered_map_fnv1a = boost::unordered_map, allocator_for>; +template using boost_unordered_flat_map_fnv1a = + boost::unordered_flat_map, allocator_for>; + template using multi_index_map_fnv1a = multi_index_container< pair, indexed_by< @@ -419,10 +429,11 @@ int main() { init_indices(); -#if 0 +#if 1 test( "std::unordered_map" ); test( "boost::unordered_map" ); + test( "boost::unordered_flat_map" ); test( "multi_index_map" ); #ifdef HAVE_ABSEIL @@ -450,6 +461,7 @@ int main() test( "std::unordered_map, FNV-1a" ); test( "boost::unordered_map, FNV-1a" ); + test( "boost::unordered_flat_map, FNV-1a" ); test( "multi_index_map, FNV-1a" ); #ifdef HAVE_ABSEIL @@ -477,7 +489,7 @@ int main() for( auto const& x: times ) { - std::cout << std::setw( 31 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; + std::cout << std::setw( 35 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; } } diff --git a/benchmark/uint32.cpp b/benchmark/uint32.cpp index 95a26bc4..986f9669 100644 --- a/benchmark/uint32.cpp +++ b/benchmark/uint32.cpp @@ -5,6 +5,7 @@ #define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING #include +#include #include #include #include @@ -311,6 +312,9 @@ template using std_unordered_map = template using boost_unordered_map = boost::unordered_map, std::equal_to, allocator_for>; +template using boost_unordered_flat_map = + boost::unordered_flat_map, std::equal_to, allocator_for>; + #ifdef HAVE_ABSEIL template using absl_node_hash_map = @@ -347,6 +351,7 @@ int main() test( "std::unordered_map" ); test( "boost::unordered_map" ); + test( "boost::unordered_flat_map" ); test( "multi_index_map" ); #ifdef HAVE_ABSEIL @@ -374,7 +379,7 @@ int main() for( auto const& x: times ) { - std::cout << std::setw( 25 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; + std::cout << std::setw( 27 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; } } diff --git a/benchmark/uint64.cpp b/benchmark/uint64.cpp index 4abc4a3e..e8b5161d 100644 --- a/benchmark/uint64.cpp +++ b/benchmark/uint64.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #ifdef HAVE_ABSEIL # include "absl/container/node_hash_map.h" # include "absl/container/flat_hash_map.h" @@ -313,28 +312,8 @@ template using std_unordered_map = template using boost_unordered_map = boost::unordered_map, std::equal_to, allocator_for>; -template struct map_types -{ - using key_type = Key; - using value_type = std::pair; - static auto& extract(const value_type& x) { return x.first; } -}; - -template -using boost_unordered_detail_foa_table = - boost::unordered::detail::foa::table, - absl::container_internal::hash_default_hash, std::equal_to, - allocator_for >; - -template -using boost_unordered_flat_map = boost::unordered::unordered_flat_map, std::equal_to, - allocator_for >; - -template -using rc15_flat_map = foa_unordered_rc_map, std::equal_to, - allocator_for >; +template using boost_unordered_flat_map = + boost::unordered_flat_map, std::equal_to, allocator_for>; #ifdef HAVE_ABSEIL @@ -372,9 +351,7 @@ int main() test( "std::unordered_map" ); test( "boost::unordered_map" ); - test( "boost_unordered_detail_foa_table" ); test( "boost::unordered_flat_map" ); - test( "rc15_flat_map" ); test( "multi_index_map" ); #ifdef HAVE_ABSEIL @@ -402,7 +379,7 @@ int main() for( auto const& x: times ) { - std::cout << std::setw( 34 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; + std::cout << std::setw( 27 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; } } diff --git a/benchmark/uuid.cpp b/benchmark/uuid.cpp new file mode 100644 index 00000000..494b1317 --- /dev/null +++ b/benchmark/uuid.cpp @@ -0,0 +1,402 @@ +// Copyright 2021, 2022 Peter Dimov. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ABSEIL +# include "absl/container/node_hash_map.h" +# include "absl/container/flat_hash_map.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint64_t s, std::size_t size ) +{ + auto t2 = std::chrono::steady_clock::now(); + + std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n"; + + t1 = t2; +} + +constexpr unsigned N = 2'000'000; +constexpr int K = 10; + +struct uuid +{ + unsigned char data[ 16 ]; + + uuid(): data() + { + } + + uuid( std::uint64_t low, std::uint64_t high ) noexcept + { + boost::endian::store_little_u64( data + 0, low ); + boost::endian::store_little_u64( data + 8, high ); + } + + inline friend std::size_t hash_value( uuid const& u ) noexcept + { + std::uint64_t low = boost::endian::load_little_u64( u.data + 0 ); + std::uint64_t high = boost::endian::load_little_u64( u.data + 8 ); + + std::size_t r = 0; + + boost::hash_combine( r, low ); + boost::hash_combine( r, high ); + + return r; + } + + inline friend bool operator==( uuid const& u1, uuid const& u2 ) noexcept + { + return std::memcmp( u1.data, u2.data, 16 ) == 0; + } +}; + +namespace std +{ + +template<> struct hash< ::uuid > +{ + std::size_t operator()( uuid const& u ) const noexcept + { + return hash_value( u ); + } +}; + +} // namespace std + +static std::vector< uuid > indices1, indices2, indices3; + +static void init_indices() +{ + indices1.push_back( {} ); + + for( unsigned i = 1; i <= N*2; ++i ) + { + indices1.push_back( { i, 0 } ); + } + + indices2.push_back( {} ); + + { + boost::detail::splitmix64 rng; + + for( unsigned i = 1; i <= N*2; ++i ) + { + indices2.push_back( { rng(), rng() } ); + } + } + + indices3.push_back( {} ); + + for( unsigned i = 1; i <= N*2; ++i ) + { + uuid k( i, 0 ); + std::reverse( k.data + 0, k.data + 16 ); + + indices3.push_back( k ); + } +} + +template BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 ) +{ + for( unsigned i = 1; i <= N; ++i ) + { + map.insert( { indices1[ i ], i } ); + } + + print_time( t1, "Consecutive insert", 0, map.size() ); + + for( unsigned i = 1; i <= N; ++i ) + { + map.insert( { indices2[ i ], i } ); + } + + print_time( t1, "Random insert", 0, map.size() ); + + for( unsigned i = 1; i <= N; ++i ) + { + map.insert( { indices3[ i ], i } ); + } + + print_time( t1, "Consecutive reversed insert", 0, map.size() ); + + std::cout << std::endl; +} + +template BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 ) +{ + std::uint64_t s; + + s = 0; + + for( int j = 0; j < K; ++j ) + { + for( unsigned i = 1; i <= N * 2; ++i ) + { + auto it = map.find( indices1[ i ] ); + if( it != map.end() ) s += it->second; + } + } + + print_time( t1, "Consecutive lookup", s, map.size() ); + + s = 0; + + for( int j = 0; j < K; ++j ) + { + for( unsigned i = 1; i <= N * 2; ++i ) + { + auto it = map.find( indices2[ i ] ); + if( it != map.end() ) s += it->second; + } + } + + print_time( t1, "Random lookup", s, map.size() ); + + s = 0; + + for( int j = 0; j < K; ++j ) + { + for( unsigned i = 1; i <= N * 2; ++i ) + { + auto it = map.find( indices3[ i ] ); + if( it != map.end() ) s += it->second; + } + } + + print_time( t1, "Consecutive reversed lookup", s, map.size() ); + + std::cout << std::endl; +} + +template BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 ) +{ + auto it = map.begin(); + + while( it != map.end() ) + { + if( it->second & 1 ) + { + if constexpr( std::is_void_v< decltype( map.erase( it ) ) > ) + { + map.erase( it++ ); + } + else + { + it = map.erase( it ); + } + } + else + { + ++it; + } + } + + print_time( t1, "Iterate and erase odd elements", 0, map.size() ); + + std::cout << std::endl; +} + +template BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 ) +{ + for( unsigned i = 1; i <= N; ++i ) + { + map.erase( indices1[ i ] ); + } + + print_time( t1, "Consecutive erase", 0, map.size() ); + + for( unsigned i = 1; i <= N; ++i ) + { + map.erase( indices2[ i ] ); + } + + print_time( t1, "Random erase", 0, map.size() ); + + for( unsigned i = 1; i <= N; ++i ) + { + map.erase( indices3[ i ] ); + } + + print_time( t1, "Consecutive reversed erase", 0, map.size() ); + + std::cout << std::endl; +} + +// counting allocator + +static std::size_t s_alloc_bytes = 0; +static std::size_t s_alloc_count = 0; + +template struct allocator +{ + using value_type = T; + + allocator() = default; + + template allocator( allocator const & ) noexcept + { + } + + template bool operator==( allocator const & ) const noexcept + { + return true; + } + + template bool operator!=( allocator const& ) const noexcept + { + return false; + } + + T* allocate( std::size_t n ) const + { + s_alloc_bytes += n * sizeof(T); + s_alloc_count++; + + return std::allocator().allocate( n ); + } + + void deallocate( T* p, std::size_t n ) const noexcept + { + s_alloc_bytes -= n * sizeof(T); + s_alloc_count--; + + std::allocator().deallocate( p, n ); + } +}; + +// + +struct record +{ + std::string label_; + long long time_; + std::size_t bytes_; + std::size_t count_; +}; + +static std::vector times; + +template class Map> BOOST_NOINLINE void test( char const* label ) +{ + std::cout << label << ":\n\n"; + + s_alloc_bytes = 0; + s_alloc_count = 0; + + Map map; + + auto t0 = std::chrono::steady_clock::now(); + auto t1 = t0; + + test_insert( map, t1 ); + + std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n"; + + record rec = { label, 0, s_alloc_bytes, s_alloc_count }; + + test_lookup( map, t1 ); + test_iteration( map, t1 ); + test_lookup( map, t1 ); + test_erase( map, t1 ); + + auto tN = std::chrono::steady_clock::now(); + std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n"; + + rec.time_ = ( tN - t0 ) / 1ms; + times.push_back( rec ); +} + +// multi_index emulation of unordered_map + +template struct pair +{ + K first; + mutable V second; +}; + +using namespace boost::multi_index; + +template using multi_index_map = multi_index_container< + pair, + indexed_by< + hashed_unique< member, K, &pair::first> > + >, + ::allocator< pair > +>; + +// aliases using the counting allocator + +template using allocator_for = ::allocator< std::pair >; + +template using std_unordered_map = + std::unordered_map, std::equal_to, allocator_for>; + +template using boost_unordered_map = + boost::unordered_map, std::equal_to, allocator_for>; + +template using boost_unordered_flat_map = + boost::unordered_flat_map, std::equal_to, allocator_for>; + +#ifdef HAVE_ABSEIL + +template using absl_node_hash_map = + absl::node_hash_map, absl::container_internal::hash_default_eq, allocator_for>; + +template using absl_flat_hash_map = + absl::flat_hash_map, absl::container_internal::hash_default_eq, allocator_for>; + +#endif + +int main() +{ + init_indices(); + + test( "std::unordered_map" ); + test( "boost::unordered_map" ); + test( "boost::unordered_flat_map" ); + test( "multi_index_map" ); + +#ifdef HAVE_ABSEIL + + test( "absl::node_hash_map" ); + test( "absl::flat_hash_map" ); + +#endif + + std::cout << "---\n\n"; + + for( auto const& x: times ) + { + std::cout << std::setw( 27 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n"; + } +} + +#ifdef HAVE_ABSEIL +# include "absl/container/internal/raw_hash_set.cc" +# include "absl/hash/internal/hash.cc" +# include "absl/hash/internal/low_level_hash.cc" +# include "absl/hash/internal/city.cc" +#endif diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index f2867074..29af58bf 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -114,7 +114,8 @@ namespace foa{ * 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. + * under high-load/high-erase conditions. It is critical that hash + * reduction is invariant under modulo 8 (see maybe_caused_overflow). * * 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, @@ -186,9 +187,20 @@ struct group15 inline void mark_overflow(std::size_t hash) { +#if BOOST_WORKAROUND(BOOST_GCC, >= 50000 && BOOST_GCC < 60000) + overflow() = static_cast( overflow() | static_cast(1<<(hash%8)) ); +#else overflow()|=static_cast(1<<(hash%8)); +#endif } + static inline bool maybe_caused_overflow(unsigned char* pc) + { + std::size_t pos=reinterpret_cast(pc)%sizeof(group15); + group15 *pg=reinterpret_cast(pc-pos); + return !pg->is_not_overflowed(*pc); + }; + inline int match_available()const { return _mm_movemask_epi8( @@ -213,7 +225,7 @@ private: { static constexpr boost::uint32_t word[]= { - 0x02020202u,0x03030303u,0x02020202u,0x03030303u,0x04040404u,0x05050505u,0x06060606u,0x07070707u, + 0x08080808u,0x09090909u,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, @@ -305,7 +317,7 @@ struct group15 inline bool is_sentinel(std::size_t pos)const { BOOST_ASSERT(pos(1<<(hash%8)); } + static inline bool maybe_caused_overflow(unsigned char* pc) + { + std::size_t pos=reinterpret_cast(pc)%sizeof(group15); + group15 *pg=reinterpret_cast(pc-pos); + return !pg->is_not_overflowed(*pc); + }; + inline int match_available()const { return simde_mm_movemask_epi8(vceqq_s8(m,vdupq_n_s8(0)))&0x7FFF; @@ -360,7 +379,7 @@ private: 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, + 8,9,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, @@ -491,6 +510,15 @@ struct group15 reinterpret_cast(m)[hash%8]|=0x8000u; } + static inline bool maybe_caused_overflow(unsigned char* pc) + { + std::size_t pos=reinterpret_cast(pc)%sizeof(group15); + group15 *pg=reinterpret_cast(pc-pos); + boost::uint64_t x=((pg->m[0])>>pos)&0x000100010001ull; + boost::uint32_t y=static_cast(x|(x>>15)|(x>>30)); + return !pg->is_not_overflowed(y); + }; + inline int match_available()const { boost::uint64_t x=~(m[0]|m[1]); @@ -519,7 +547,7 @@ private: 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, + 8,9,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, @@ -721,7 +749,7 @@ inline unsigned int unchecked_countr_zero(int x) _BitScanForward(&r,(unsigned long)x); return (unsigned int)r; #else - BOOST_UNORDERED_ASSUME(x); + BOOST_UNORDERED_ASSUME(x!=0); return (unsigned int)boost::core::countr_zero((unsigned int)x); #endif } @@ -1105,7 +1133,7 @@ public: 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()} + ml{initial_max_load()} {} table(const table& x): @@ -1123,7 +1151,7 @@ public: { x.size_=0; x.arrays=x.new_arrays(0); - x.ml=x.max_load(); + x.ml=x.initial_max_load(); } table(const table& x,const Allocator& al_): @@ -1299,14 +1327,15 @@ public: > 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_; + recover_slot(pos.pc); } template + BOOST_FORCEINLINE auto erase(Key&& x) -> typename std::enable_if< !std::is_convertible::value&& !std::is_convertible::value, std::size_t>::type @@ -1359,6 +1388,7 @@ public: } arrays.groups[arrays.groups_size_mask].set_sentinel(); size_=0; + ml=initial_max_load(); } } @@ -1405,6 +1435,8 @@ public: float max_load_factor()const noexcept{return mlf;} + std::size_t max_load()const noexcept{return ml;} + void rehash(std::size_t n) { auto m=size_t(std::ceil(float(size())/mlf)); @@ -1470,7 +1502,23 @@ private: value_type *p; }; - std::size_t max_load()const + void recover_slot(unsigned char* pc) + { + /* If this slot potentially caused overflow, we decrease the maximum load so + * that average probe length won't increase unboundedly in repeated + * insert/erase cycles (drift). + */ + ml-=group_type::maybe_caused_overflow(pc); + group_type::reset(pc); + --size_; + } + + void recover_slot(group_type* pg,std::size_t pos) + { + recover_slot(reinterpret_cast(pg)+pos); + } + + std::size_t initial_max_load()const { static constexpr std::size_t small_capacity=2*N-1; @@ -1593,24 +1641,10 @@ private: }; } else{ - /* strong exception guarantee -> try insertion before rehash */ - auto new_arrays_=new_arrays( - std::size_t(std::ceil(static_cast(size_+1)/mlf))); - 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,true}; + return { + unchecked_emplace_with_rehash(hash,std::forward(args)...), + true + }; } } @@ -1619,6 +1653,41 @@ private: return size_policy::size(size_index_for(n))*N-1; } + template + BOOST_NOINLINE iterator + unchecked_emplace_with_rehash(std::size_t hash,Args&&... args) + { + /* Due to the anti-drift mechanism (see recover_slot), new_arrays_ may be + * of the same size as the old arrays; in the limit, erasing one element at + * full load and then inserting could bring us back to the same capacity + * after a costly rehash. To avoid this, we jump to the next capacity level + * when the number of erased elements is <= 10% of total elements at full + * load, which is implemented by requesting additional F*size elements, + * with F = P * 10% / (1 - P * 10%), where P is the probability of an + * element having caused overflow; P has been measured as ~0.162 under + * ideal conditions, yielding F ~ 0.0165 ~ 1/61. + */ + auto new_arrays_=new_arrays(std::size_t( + std::ceil(static_cast(size_+size_/61+1)/mlf))); + iterator it; + BOOST_TRY{ + /* strong exception guarantee -> try insertion before rehash */ + 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); @@ -1635,12 +1704,11 @@ private: } 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); + recover_slot(pg,nz); if(!(--num_destroyed))goto continue_; mask&=mask-1; } @@ -1664,7 +1732,7 @@ private: } delete_arrays(arrays); arrays=new_arrays_; - ml=max_load(); + ml=initial_max_load(); } void noshrink_reserve(std::size_t n) @@ -1680,7 +1748,7 @@ private: auto new_arrays_=new_arrays(n); delete_arrays(arrays); arrays=new_arrays_; - ml=max_load(); + ml=initial_max_load(); } } } @@ -1736,37 +1804,6 @@ private: return res; } -#if 0 - template - iterator nosize_unchecked_emplace_at( - const arrays_type& arrays_,std::size_t pos0,std::size_t hash, - Args&&... args) - { - auto pn=insert_position(arrays_,pos0,hash); - auto &pos=pn.first; - auto &n=pn.second; - auto pg=arrays_.groups+pos; - auto p=arrays_.elements+pos*N+n; - construct_element(p,std::forward(args)...); - pg->set(n,hash); - return {pg,n,p}; - } - - std::pair - static insert_position( - const arrays_type& arrays_,std::size_t pos0,std::size_t hash) - { - 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)){ - return {pos,unchecked_countr_zero(mask)}; - } - else pg->mark_overflow(hash); - } - } -#else template iterator nosize_unchecked_emplace_at( const arrays_type& arrays_,std::size_t pos0,std::size_t hash, @@ -1786,7 +1823,6 @@ private: else pg->mark_overflow(hash); } } -#endif template std::size_t erase_if_impl(Predicate pr) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 4ee11748..48a0170d 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3411,8 +3411,8 @@ namespace boost { template inline void table::rehash(std::size_t num_buckets) { - num_buckets = (std::max)( - min_buckets(size_, mlf_), buckets_.bucket_count_for(num_buckets)); + num_buckets = buckets_.bucket_count_for( + (std::max)(min_buckets(size_, mlf_), num_buckets)); if (num_buckets != this->bucket_count()) { this->rehash_impl(num_buckets); diff --git a/include/boost/unordered/hash_traits.hpp b/include/boost/unordered/hash_traits.hpp index 5ab4a556..dfc2716f 100644 --- a/include/boost/unordered/hash_traits.hpp +++ b/include/boost/unordered/hash_traits.hpp @@ -12,7 +12,7 @@ #define BOOST_UNORDERED_HASH_TRAITS_HPP #include -#include +#include namespace boost{ namespace unordered{ @@ -20,11 +20,12 @@ namespace unordered{ namespace detail{ template -struct hash_is_avalanching_impl:std::false_type{}; +struct hash_is_avalanching_impl: boost::false_type{}; template -struct hash_is_avalanching_impl>: - std::true_type{}; +struct hash_is_avalanching_impl::type>: + boost::true_type{}; } /* namespace detail */ @@ -32,11 +33,11 @@ struct hash_is_avalanching_impl>: * 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. +/* hash_is_avalanching::value is true when the type Hash::is_avalanching + * is present, false otherwise. */ template -struct hash_is_avalanching:detail::hash_is_avalanching_impl::type{}; +struct hash_is_avalanching: detail::hash_is_avalanching_impl::type{}; } /* namespace unordered */ } /* namespace boost */ diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index 2cb4135e..7ba2ba28 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -26,6 +26,12 @@ 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 { @@ -75,6 +81,9 @@ namespace boost { 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; @@ -97,6 +106,13 @@ namespace boost { { } + template + unordered_flat_map( + InputIterator f, InputIterator l, allocator_type const& a) + : unordered_flat_map(f, l, size_type(0), hasher(), key_equal(), a) + { + } + explicit unordered_flat_map(allocator_type const& a) : unordered_flat_map(0, a) { @@ -156,6 +172,12 @@ namespace boost { { } + unordered_flat_map( + std::initializer_list il, allocator_type const& a) + : unordered_flat_map(il, size_type(0), hasher(), key_equal(), a) + { + } + unordered_flat_map(std::initializer_list init, size_type n, allocator_type const& a) : unordered_flat_map(init, n, hasher(), key_equal(), a) @@ -217,29 +239,31 @@ namespace boost { void clear() noexcept { table_.clear(); } template - auto insert(Ty&& value) + BOOST_FORCEINLINE auto insert(Ty&& value) -> decltype(table_.insert(std::forward(value))) { return table_.insert(std::forward(value)); } - std::pair insert(init_type&& value) + BOOST_FORCEINLINE std::pair insert(init_type&& value) { return table_.insert(std::move(value)); } - iterator insert(const_iterator, value_type const& value) + template + BOOST_FORCEINLINE auto insert(const_iterator, Ty&& value) + -> decltype(table_.insert(std::forward(value)).first) { - return table_.insert(value).first; + return table_.insert(std::forward(value)).first; } - iterator insert(const_iterator, value_type&& value) + BOOST_FORCEINLINE iterator insert(const_iterator, init_type&& value) { return table_.insert(std::move(value)).first; } template - void insert(InputIterator first, InputIterator last) + BOOST_FORCEINLINE void insert(InputIterator first, InputIterator last) { for (auto pos = first; pos != last; ++pos) { table_.emplace(*pos); @@ -287,44 +311,52 @@ namespace boost { .first; } - template std::pair emplace(Args&&... args) + template + BOOST_FORCEINLINE std::pair emplace(Args&&... args) { return table_.emplace(std::forward(args)...); } template - iterator emplace_hint(const_iterator, Args&&... args) + BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args) { - return this->emplace(std::forward(args)...).first; + return table_.emplace(std::forward(args)...).first; } template - std::pair try_emplace(key_type const& key, Args&&... args) + BOOST_FORCEINLINE std::pair try_emplace( + key_type const& key, Args&&... args) { return table_.try_emplace(key, std::forward(args)...); } template - std::pair try_emplace(key_type&& key, Args&&... args) + BOOST_FORCEINLINE std::pair try_emplace( + key_type&& key, Args&&... args) { return table_.try_emplace(std::move(key), std::forward(args)...); } template - iterator try_emplace(const_iterator, key_type const& key, Args&&... args) + BOOST_FORCEINLINE iterator try_emplace( + const_iterator, key_type const& key, Args&&... args) { return table_.try_emplace(key, std::forward(args)...).first; } template - iterator try_emplace(const_iterator, key_type&& key, Args&&... args) + BOOST_FORCEINLINE iterator try_emplace( + const_iterator, key_type&& key, Args&&... args) { return table_.try_emplace(std::move(key), std::forward(args)...) .first; } - void erase(iterator pos) { table_.erase(pos); } - void erase(const_iterator pos) { return table_.erase(pos); } + 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) { @@ -333,10 +365,13 @@ namespace boost { return iterator{detail::foa::const_iterator_cast_tag{}, last}; } - size_type erase(key_type const& key) { return table_.erase(key); } + BOOST_FORCEINLINE size_type erase(key_type const& key) + { + return table_.erase(key); + } template - typename std::enable_if< + BOOST_FORCEINLINE typename std::enable_if< detail::transparent_non_iterable::value, size_type>::type erase(K const& key) @@ -530,6 +565,8 @@ namespace boost { void max_load_factor(float) {} + size_type max_load() const noexcept { return table_.max_load(); } + void rehash(size_type n) { table_.rehash(n); } void reserve(size_type n) { table_.reserve(n); } @@ -586,6 +623,11 @@ namespace boost { { return erase_if(map.table_, pred); } + +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4714 */ +#endif + } // namespace unordered } // namespace boost diff --git a/include/boost/unordered/unordered_flat_set.hpp b/include/boost/unordered/unordered_flat_set.hpp index f0129485..ef96d4e1 100644 --- a/include/boost/unordered/unordered_flat_set.hpp +++ b/include/boost/unordered/unordered_flat_set.hpp @@ -24,6 +24,12 @@ 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 { @@ -57,6 +63,9 @@ namespace boost { 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; @@ -79,6 +88,13 @@ namespace boost { { } + template + unordered_flat_set( + InputIterator f, InputIterator l, allocator_type const& a) + : unordered_flat_set(f, l, size_type(0), hasher(), key_equal(), a) + { + } + explicit unordered_flat_set(allocator_type const& a) : unordered_flat_set(0, a) { @@ -138,6 +154,12 @@ namespace boost { { } + unordered_flat_set( + std::initializer_list il, allocator_type const& a) + : unordered_flat_set(il, size_type(0), hasher(), key_equal(), a) + { + } + unordered_flat_set(std::initializer_list init, size_type n, allocator_type const& a) : unordered_flat_set(init, n, hasher(), key_equal(), a) @@ -198,22 +220,23 @@ namespace boost { void clear() noexcept { table_.clear(); } - std::pair insert(value_type const& value) + BOOST_FORCEINLINE std::pair insert( + value_type const& value) { return table_.insert(value); } - std::pair insert(value_type&& value) + BOOST_FORCEINLINE std::pair insert(value_type&& value) { return table_.insert(std::move(value)); } - iterator insert(const_iterator, value_type const& value) + BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value) { return table_.insert(value).first; } - iterator insert(const_iterator, value_type&& value) + BOOST_FORCEINLINE iterator insert(const_iterator, value_type&& value) { return table_.insert(std::move(value)).first; } @@ -231,18 +254,22 @@ namespace boost { this->insert(ilist.begin(), ilist.end()); } - template std::pair emplace(Args&&... args) + template + BOOST_FORCEINLINE std::pair emplace(Args&&... args) { return table_.emplace(std::forward(args)...); } template - iterator emplace_hint(const_iterator, Args&&... args) + BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args) { - return this->emplace(std::forward(args)...).first; + return table_.emplace(std::forward(args)...).first; } - void erase(const_iterator pos) { return 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) { @@ -251,10 +278,13 @@ namespace boost { return iterator{detail::foa::const_iterator_cast_tag{}, last}; } - size_type erase(key_type const& key) { return table_.erase(key); } + BOOST_FORCEINLINE size_type erase(key_type const& key) + { + return table_.erase(key); + } template - typename std::enable_if< + BOOST_FORCEINLINE typename std::enable_if< detail::transparent_non_iterable::value, size_type>::type erase(K const& key) @@ -411,6 +441,8 @@ namespace boost { void max_load_factor(float) {} + size_type max_load() const noexcept { return table_.max_load(); } + void rehash(size_type n) { table_.rehash(n); } void reserve(size_type n) { table_.reserve(n); } @@ -466,6 +498,11 @@ namespace boost { { return erase_if(set.table_, pred); } + +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4714 */ +#endif + } // namespace unordered } // namespace boost diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index d66c5394..4a060358 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -123,6 +123,9 @@ namespace boost { explicit unordered_map(size_type, const hasher&, const allocator_type&); + template + unordered_map(InputIterator, InputIterator, const allocator_type&); + template unordered_map(InputIt, InputIt, size_type, const allocator_type&); @@ -131,6 +134,8 @@ namespace boost { InputIt, InputIt, size_type, const hasher&, const allocator_type&); #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + unordered_map(std::initializer_list, const allocator_type&); + unordered_map( std::initializer_list, size_type, const allocator_type&); @@ -1076,6 +1081,9 @@ namespace boost { explicit unordered_multimap( size_type, const hasher&, const allocator_type&); + template + unordered_multimap(InputIterator, InputIterator, const allocator_type&); + template unordered_multimap(InputIt, InputIt, size_type, const allocator_type&); @@ -1084,6 +1092,9 @@ namespace boost { InputIt, InputIt, size_type, const hasher&, const allocator_type&); #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + unordered_multimap( + std::initializer_list, const allocator_type&); + unordered_multimap( std::initializer_list, size_type, const allocator_type&); @@ -1751,6 +1762,17 @@ namespace boost { { } + template + template + unordered_map::unordered_map( + InputIterator f, InputIterator l, const allocator_type& a) + : table_(boost::unordered::detail::initial_size( + f, l, detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert(f, l); + } + template template unordered_map::unordered_map( @@ -1773,6 +1795,16 @@ namespace boost { #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + template + unordered_map::unordered_map( + std::initializer_list list, const allocator_type& a) + : table_(boost::unordered::detail::initial_size( + list.begin(), list.end(), detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert(list.begin(), list.end()); + } + template unordered_map::unordered_map( std::initializer_list list, size_type n, @@ -2234,6 +2266,17 @@ namespace boost { { } + template + template + unordered_multimap::unordered_multimap( + InputIterator f, InputIterator l, const allocator_type& a) + : table_(boost::unordered::detail::initial_size( + f, l, detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert(f, l); + } + template template unordered_multimap::unordered_multimap( @@ -2256,6 +2299,16 @@ namespace boost { #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + template + unordered_multimap::unordered_multimap( + std::initializer_list list, const allocator_type& a) + : table_(boost::unordered::detail::initial_size( + list.begin(), list.end(), detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert(list.begin(), list.end()); + } + template unordered_multimap::unordered_multimap( std::initializer_list list, size_type n, diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index ed736c21..b3e75134 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -121,6 +121,9 @@ namespace boost { explicit unordered_set(size_type, const hasher&, const allocator_type&); + template + unordered_set(InputIterator, InputIterator, const allocator_type&); + template unordered_set(InputIt, InputIt, size_type, const allocator_type&); @@ -129,6 +132,8 @@ namespace boost { InputIt, InputIt, size_type, const hasher&, const allocator_type&); #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + unordered_set(std::initializer_list, const allocator_type&); + unordered_set( std::initializer_list, size_type, const allocator_type&); @@ -739,6 +744,9 @@ namespace boost { explicit unordered_multiset( size_type, const hasher&, const allocator_type&); + template + unordered_multiset(InputIterator, InputIterator, const allocator_type&); + template unordered_multiset(InputIt, InputIt, size_type, const allocator_type&); @@ -747,6 +755,9 @@ namespace boost { InputIt, InputIt, size_type, const hasher&, const allocator_type&); #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + unordered_multiset( + std::initializer_list, const allocator_type&); + unordered_multiset( std::initializer_list, size_type, const allocator_type&); @@ -1352,6 +1363,17 @@ namespace boost { { } + template + template + unordered_set::unordered_set( + InputIterator f, InputIterator l, const allocator_type& a) + : table_(boost::unordered::detail::initial_size( + f, l, detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert(f, l); + } + template template unordered_set::unordered_set( @@ -1374,6 +1396,16 @@ namespace boost { #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + template + unordered_set::unordered_set( + std::initializer_list list, const allocator_type& a) + : table_(boost::unordered::detail::initial_size( + list.begin(), list.end(), detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert(list.begin(), list.end()); + } + template unordered_set::unordered_set( std::initializer_list list, size_type n, @@ -1750,6 +1782,17 @@ namespace boost { { } + template + template + unordered_multiset::unordered_multiset( + InputIterator f, InputIterator l, const allocator_type& a) + : table_(boost::unordered::detail::initial_size( + f, l, detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert(f, l); + } + template template unordered_multiset::unordered_multiset( @@ -1772,6 +1815,16 @@ namespace boost { #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + template + unordered_multiset::unordered_multiset( + std::initializer_list list, const allocator_type& a) + : table_(boost::unordered::detail::initial_size( + list.begin(), list.end(), detail::default_bucket_count), + hasher(), key_equal(), a) + { + this->insert(list.begin(), list.end()); + } + template unordered_multiset::unordered_multiset( std::initializer_list list, size_type n, diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 2c27334b..c3284098 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -97,9 +97,13 @@ 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 : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_$(name) ; + run unordered/$(name).cpp : : : $(CPP11) BOOST_UNORDERED_FOA_TESTS : foa_$(name) ; } build_foa fwd_set_test ; @@ -107,7 +111,7 @@ 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 : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_link_test ; +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 ; @@ -127,20 +131,23 @@ build_foa load_factor_tests ; build_foa rehash_tests ; build_foa equality_tests ; build_foa swap_tests ; -run unordered/scoped_allocator.cpp : : : 98:no 03:no 0x:no msvc-14.0:no BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ; +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 ; +build_foa max_load_tests ; -run exception/constructor_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ; -run exception/copy_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ; -run exception/assign_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ; -run exception/move_assign_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_move_assign_exception_tests ; -run exception/insert_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_insert_exception_tests ; -run exception/erase_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_erase_exception_tests ; -run exception/rehash_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ; -run exception/swap_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ; -run exception/merge_exception_tests.cpp : : : 98:no 03:no 0x:no BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ; +run unordered/hash_is_avalanching_test.cpp ; + +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/cmake_subdir_test/CMakeLists.txt b/test/cmake_subdir_test/CMakeLists.txt index 64e720db..80f81c9d 100644 --- a/test/cmake_subdir_test/CMakeLists.txt +++ b/test/cmake_subdir_test/CMakeLists.txt @@ -28,8 +28,7 @@ type_traits # Secondary dependencies -detail -integer +describe static_assert winapi ) diff --git a/test/exception/containers.hpp b/test/exception/containers.hpp index 9f78923d..e6eb267b 100644 --- a/test/exception/containers.hpp +++ b/test/exception/containers.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) diff --git a/test/exception/insert_exception_tests.cpp b/test/exception/insert_exception_tests.cpp index eee11183..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" diff --git a/test/exception/merge_exception_tests.cpp b/test/exception/merge_exception_tests.cpp index a6a7afc7..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) diff --git a/test/exception/swap_exception_tests.cpp b/test/exception/swap_exception_tests.cpp index 2745ede5..545d607a 100644 --- a/test/exception/swap_exception_tests.cpp +++ b/test/exception/swap_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) diff --git a/test/helpers/fwd.hpp b/test/helpers/fwd.hpp index a5657e01..a1a7cda4 100644 --- a/test/helpers/fwd.hpp +++ b/test/helpers/fwd.hpp @@ -12,7 +12,8 @@ namespace test { typedef enum { default_generator, generate_collisions, - limited_range + limited_range, + sequential } random_generator; int generate(int const*, random_generator); diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp index c9ed42bd..ba4f9e0b 100644 --- a/test/helpers/generators.hpp +++ b/test/helpers/generators.hpp @@ -12,7 +12,9 @@ #define BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER #include "./fwd.hpp" +#include #include +#include #include #include #include @@ -34,8 +36,18 @@ namespace test { return static_cast(rand()) % max; } + static int origin = 0; + + void reset_sequence() { origin = 0; } + inline int generate(int const*, random_generator g) { + if (g == sequential) { + BOOST_ASSERT_MSG( + origin < INT_MAX, "test::reset_sequence() should be invoked"); + return origin++; + } + using namespace std; int value = rand(); if (g == limited_range) { diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index 849fe983..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) diff --git a/test/helpers/unordered.hpp b/test/helpers/unordered.hpp index 5a2361ea..a3af9452 100644 --- a/test/helpers/unordered.hpp +++ b/test/helpers/unordered.hpp @@ -17,5 +17,6 @@ #include #endif #include "postfix.hpp" +// clang-format on #endif diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp index b937c57e..b000e42f 100644 --- a/test/unordered/assign_tests.cpp +++ b/test/unordered/assign_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) diff --git a/test/unordered/at_tests.cpp b/test/unordered/at_tests.cpp index bc5ad921..b06dfddb 100644 --- a/test/unordered/at_tests.cpp +++ b/test/unordered/at_tests.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/compile_map.cpp b/test/unordered/compile_map.cpp index f43476c0..c4c8d7ce 100644 --- a/test/unordered/compile_map.cpp +++ b/test/unordered/compile_map.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) diff --git a/test/unordered/compile_set.cpp b/test/unordered/compile_set.cpp index d51bc11e..aa3721c6 100644 --- a/test/unordered/compile_set.cpp +++ b/test/unordered/compile_set.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) diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index 65509e79..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) @@ -75,6 +76,14 @@ template void container_test(X& r, T const&) #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 @@ -509,13 +518,11 @@ template void equality_test(X& r) template void unordered_unique_test(X& r, T const& t) { - (void) r; - (void) t; -#ifndef BOOST_UNORDERED_FOA_TESTS typedef typename X::iterator iterator; 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; diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp index b68a7eb5..b610b5b1 100644 --- a/test/unordered/constructor_tests.cpp +++ b/test/unordered/constructor_tests.cpp @@ -14,6 +14,8 @@ #include "../helpers/input_iterator.hpp" #include "../helpers/invariants.hpp" +#include + namespace constructor_tests { test::seed_t initialize_seed(356730); @@ -171,6 +173,16 @@ namespace constructor_tests { BOOST_TEST(test::equivalent(x.get_allocator(), al)); test::check_equivalent_keys(x); } + + UNORDERED_SUB_TEST("Construct 12") + { + test::check_instances check_; + + test::random_values v(1000, generator); + T x(v.begin(), v.end(), al); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, v); + } } template @@ -317,66 +329,206 @@ namespace constructor_tests { } #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) - std::initializer_list list; + typedef typename T::value_type value_type; + + std::initializer_list list; + + test::random_values v(3, generator); + std::vector vec(v.begin(), v.end()); + BOOST_ASSERT(vec.size() >= 3); + + // create a new vector here because erase() requires assignability which is + // deleted for some of the test types + // + std::vector expected(vec.begin(), vec.begin() + 3); UNORDERED_SUB_TEST("Initializer list construct 1") { test::check_instances check_; - T x(list); - BOOST_TEST(x.empty()); - BOOST_TEST_EQ(x.bucket_count(), 0u); - BOOST_TEST(test::equivalent(x.hash_function(), hf)); - BOOST_TEST(test::equivalent(x.key_eq(), eq)); - BOOST_TEST(test::equivalent(x.get_allocator(), al)); + { + T x(list); + BOOST_TEST(x.empty()); + BOOST_TEST_EQ(x.bucket_count(), 0u); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + { + T x{vec[0], vec[1], vec[2]}; + BOOST_TEST_NOT(x.empty()); + BOOST_TEST_GT(x.bucket_count(), 0u); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, expected); + } } UNORDERED_SUB_TEST("Initializer list construct 2") { test::check_instances check_; - T x(list, 1000); - BOOST_TEST(x.empty()); - BOOST_TEST(x.bucket_count() >= 1000); - BOOST_TEST(test::equivalent(x.hash_function(), hf)); - BOOST_TEST(test::equivalent(x.key_eq(), eq)); - BOOST_TEST(test::equivalent(x.get_allocator(), al)); + { + T x(list, 1000); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 1000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + { + T x({vec[0], vec[1], vec[2]}, 1000); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 1000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, expected); + } } UNORDERED_SUB_TEST("Initializer list construct 3") { - test::check_instances check_; + { + test::check_instances check_; - T x(list, 10, hf1); - BOOST_TEST(x.empty()); - BOOST_TEST(x.bucket_count() >= 10); - BOOST_TEST(test::equivalent(x.hash_function(), hf1)); - BOOST_TEST(test::equivalent(x.key_eq(), eq)); - BOOST_TEST(test::equivalent(x.get_allocator(), al)); + T x(list, 10, hf1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + { + test::check_instances check_; + + T x({vec[0], vec[1], vec[2]}, 10, hf1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, expected); + } } UNORDERED_SUB_TEST("Initializer list construct 4") { - test::check_instances check_; + { + test::check_instances check_; - T x(list, 10, hf1, eq1); - BOOST_TEST(x.empty()); - BOOST_TEST(x.bucket_count() >= 10); - BOOST_TEST(test::equivalent(x.hash_function(), hf1)); - BOOST_TEST(test::equivalent(x.key_eq(), eq1)); - BOOST_TEST(test::equivalent(x.get_allocator(), al)); + T x(list, 10, hf1, eq1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + } + + { + test::check_instances check_; + + T x({vec[0], vec[1], vec[2]}, 10, hf1, eq1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, expected); + } } UNORDERED_SUB_TEST("Initializer list construct 5") + { + { + test::check_instances check_; + + T x(list, 10, hf1, eq1, al1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + } + + { + test::check_instances check_; + + T x({vec[0], vec[1], vec[2]}, 10, hf1, eq1, al1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Initializer list construct 6") + { + { + test::check_instances check_; + + T x(list, 10, al1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + } + + { + test::check_instances check_; + + T x({vec[0], vec[1], vec[2]}, 10, al1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Initializer list construct 7") + { + { + test::check_instances check_; + + T x(list, 10, hf1, al1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + } + + { + test::check_instances check_; + + T x({vec[0], vec[1], vec[2]}, 10, hf1, al1); + BOOST_TEST_NOT(x.empty()); + BOOST_TEST(x.bucket_count() >= 10); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + test::check_container(x, expected); + } + } + + UNORDERED_SUB_TEST("Initializer list construct 8") { test::check_instances check_; - T x(list, 10, hf1, eq1, al1); - BOOST_TEST(x.empty()); - BOOST_TEST(x.bucket_count() >= 10); - BOOST_TEST(test::equivalent(x.hash_function(), hf1)); - BOOST_TEST(test::equivalent(x.key_eq(), eq1)); - BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + { + T x(list, al1); + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + } + + { + T x({vec[0], vec[1], vec[2]}, al1); + BOOST_TEST(test::equivalent(x.get_allocator(), al1)); + test::check_container(x, expected); + } } #endif } diff --git a/test/unordered/contains_tests.cpp b/test/unordered/contains_tests.cpp index 782e1915..870de2a3 100644 --- a/test/unordered/contains_tests.cpp +++ b/test/unordered/contains_tests.cpp @@ -1,4 +1,4 @@ -// 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) diff --git a/test/unordered/emplace_tests.cpp b/test/unordered/emplace_tests.cpp index bf79e2c2..60aceb32 100644 --- a/test/unordered/emplace_tests.cpp +++ b/test/unordered/emplace_tests.cpp @@ -1,5 +1,6 @@ // // 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) diff --git a/test/unordered/equality_tests.cpp b/test/unordered/equality_tests.cpp index 68e021ca..d10d07f2 100644 --- a/test/unordered/equality_tests.cpp +++ b/test/unordered/equality_tests.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/equivalent_keys_tests.cpp b/test/unordered/equivalent_keys_tests.cpp index 0a67b768..b279a8c6 100644 --- a/test/unordered/equivalent_keys_tests.cpp +++ b/test/unordered/equivalent_keys_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) diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp index ff02856a..d030c6aa 100644 --- a/test/unordered/erase_tests.cpp +++ b/test/unordered/erase_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) diff --git a/test/unordered/fwd_map_test.cpp b/test/unordered/fwd_map_test.cpp index 30acf14e..5ce1fb94 100644 --- a/test/unordered/fwd_map_test.cpp +++ b/test/unordered/fwd_map_test.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/fwd_set_test.cpp b/test/unordered/fwd_set_test.cpp index ecaaa3b1..86f4a9d1 100644 --- a/test/unordered/fwd_set_test.cpp +++ b/test/unordered/fwd_set_test.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/hash_is_avalanching_test.cpp b/test/unordered/hash_is_avalanching_test.cpp new file mode 100644 index 00000000..1e25e033 --- /dev/null +++ b/test/unordered/hash_is_avalanching_test.cpp @@ -0,0 +1,48 @@ +// Copyright 2022 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +// an imitation of a third-party header specializing hash_is_avalanching +// (boost/container_hash/hash.hpp is an example doing that) + +#include + +struct X3 +{ +}; + +namespace boost +{ +namespace unordered +{ + + template struct hash_is_avalanching; + template<> struct hash_is_avalanching< ::X3 >: boost::true_type {}; + +} // namespace unordered +} // namespace boost + +// + +#include +#include + +struct X1 +{ +}; + +struct X2 +{ + typedef void is_avalanching; +}; + +int main() +{ + using boost::unordered::hash_is_avalanching; + + BOOST_TEST_TRAIT_FALSE((hash_is_avalanching)); + BOOST_TEST_TRAIT_TRUE((hash_is_avalanching)); + BOOST_TEST_TRAIT_TRUE((hash_is_avalanching)); + + return boost::report_errors(); +} diff --git a/test/unordered/incomplete_test.cpp b/test/unordered/incomplete_test.cpp index 63058f03..3c5b09fa 100644 --- a/test/unordered/incomplete_test.cpp +++ b/test/unordered/incomplete_test.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/init_type_insert_tests.cpp b/test/unordered/init_type_insert_tests.cpp index 94afce4b..e9920d38 100644 --- a/test/unordered/init_type_insert_tests.cpp +++ b/test/unordered/init_type_insert_tests.cpp @@ -1,3 +1,8 @@ + +// 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 @@ -164,9 +169,76 @@ static void test_insert_tracking() 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 17cfd7ff..ebde259c 100644 --- a/test/unordered/insert_hint_tests.cpp +++ b/test/unordered/insert_hint_tests.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/link_test_1.cpp b/test/unordered/link_test_1.cpp index 1d1daae7..d8032e73 100644 --- a/test/unordered/link_test_1.cpp +++ b/test/unordered/link_test_1.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) diff --git a/test/unordered/link_test_2.cpp b/test/unordered/link_test_2.cpp index fdeb529d..74de87c9 100644 --- a/test/unordered/link_test_2.cpp +++ b/test/unordered/link_test_2.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) diff --git a/test/unordered/load_factor_tests.cpp b/test/unordered/load_factor_tests.cpp index 26a12e36..461f883d 100644 --- a/test/unordered/load_factor_tests.cpp +++ b/test/unordered/load_factor_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) diff --git a/test/unordered/max_load_tests.cpp b/test/unordered/max_load_tests.cpp new file mode 100644 index 00000000..dc117177 --- /dev/null +++ b/test/unordered/max_load_tests.cpp @@ -0,0 +1,77 @@ +#if !defined(BOOST_UNORDERED_FOA_TESTS) +#error "max_load_tests is currently only supported by open-addressed containers" +#else + +#include "../helpers/unordered.hpp" + +#include "../helpers/helpers.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/test.hpp" +#include "../helpers/tracker.hpp" +#include "../objects/test.hpp" + +template void max_load_tests(X*, test::random_generator generator) +{ + typedef typename X::size_type size_type; + + test::reset_sequence(); + + X x; + size_type max_load = x.max_load(); + + BOOST_TEST_EQ(max_load, 0u); + + x.reserve(1000); + max_load = x.max_load(); + + size_type bucket_count = x.bucket_count(); + BOOST_TEST_GE(bucket_count, 1000u); + + test::ordered tracker; + { + test::random_values v(max_load, generator); + + x.insert(v.begin(), v.end()); + tracker.insert_range(v.begin(), v.end()); + + BOOST_TEST_EQ(x.bucket_count(), bucket_count); + BOOST_TEST_EQ(x.max_load(), max_load); + BOOST_TEST_EQ(x.size(), max_load); + } + + { + test::random_values v(100, generator); + + x.insert(v.begin(), v.end()); + tracker.insert_range(v.begin(), v.end()); + + BOOST_TEST_GT(x.bucket_count(), bucket_count); + BOOST_TEST_GT(x.max_load(), max_load); + BOOST_TEST_GT(x.size(), max_load); + } + + tracker.compare(x); +} + +using test::default_generator; +using test::generate_collisions; +using test::limited_range; +using test::sequential; + +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(max_load_tests, + ((int_set_ptr)(test_map_ptr)(test_set_tracking)(test_map_tracking))( + (sequential))) +#endif + +RUN_TESTS() diff --git a/test/unordered/merge_tests.cpp b/test/unordered/merge_tests.cpp index 2f1f4302..32f4d685 100644 --- a/test/unordered/merge_tests.cpp +++ b/test/unordered/merge_tests.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/move_tests.cpp b/test/unordered/move_tests.cpp index 9e899c19..53cac2ce 100644 --- a/test/unordered/move_tests.cpp +++ b/test/unordered/move_tests.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/noexcept_tests.cpp b/test/unordered/noexcept_tests.cpp index d4bed53f..0cb4b521 100644 --- a/test/unordered/noexcept_tests.cpp +++ b/test/unordered/noexcept_tests.cpp @@ -1,5 +1,6 @@ // 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) diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp index fb122c03..0c5227a3 100644 --- a/test/unordered/rehash_tests.cpp +++ b/test/unordered/rehash_tests.cpp @@ -1,20 +1,53 @@ // 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 "../helpers/unordered.hpp" -#include "../helpers/test.hpp" -#include "../helpers/random_values.hpp" -#include "../helpers/tracker.hpp" #include "../helpers/metafunctions.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/test.hpp" +#include "../helpers/tracker.hpp" #include "../objects/test.hpp" namespace rehash_tests { test::seed_t initialize_seed(2974); + static int count_allocations; + template struct monotonic_allocator + { + typedef T value_type; + monotonic_allocator() {} + monotonic_allocator(monotonic_allocator const&) {} + + template monotonic_allocator(monotonic_allocator const&) {} + + friend bool operator==( + monotonic_allocator const&, monotonic_allocator const&) + { + return true; + } + + friend bool operator!=( + monotonic_allocator const&, monotonic_allocator const&) + { + return false; + } + + T* allocate(std::size_t n) + { + ++count_allocations; + return static_cast(::operator new(sizeof(T) * n)); + } + + void deallocate(T* p, std::size_t) { ::operator delete(p); } + }; + + void reset_counts() { count_allocations = 0; } + template bool postcondition(X const& x, typename X::size_type n) { return static_cast(x.bucket_count()) >= @@ -320,9 +353,10 @@ namespace rehash_tests { BOOST_TEST_GT(x.bucket_count(), 0u); x.reserve( - 2 * (static_cast( - std::floor(static_cast(x.size()) / x.max_load_factor()) + - std::floor(static_cast(x.size()) * x.max_load_factor())))); + 2 * + (static_cast( + std::floor(static_cast(x.size()) / x.max_load_factor()) + + std::floor(static_cast(x.size()) * x.max_load_factor())))); BOOST_TEST_GT(x.bucket_count(), bucket_count); @@ -375,6 +409,34 @@ namespace rehash_tests { } } + template void rehash_stability(X*, test::random_generator generator) + { + reset_counts(); + + typedef typename X::size_type size_type; + + size_type bucket_count = 100; + X x(bucket_count); + + size_type num_elems = x.bucket_count() - 1; + + test::random_values v(num_elems, generator); + test::ordered tracker; + tracker.insert_range(v.begin(), v.end()); + + typename test::random_values::iterator pos = v.begin(); + for (size_type i = 0; i < num_elems; ++i) { + x.insert(*pos); + ++pos; + } + + int const old_count = count_allocations; + x.rehash(0); + + BOOST_TEST_EQ(count_allocations, old_count); + tracker.compare(x); + } + template void rehash_test1(X*, test::random_generator generator) { test::random_values v(1000, generator); @@ -512,6 +574,13 @@ namespace rehash_tests { test::allocator1 > >* test_map_tracking; + boost::unordered_flat_set >* test_set_monotonic; + boost::unordered_flat_map > >* + test_map_monotonic; + UNORDERED_TEST(rehash_empty_test1, ((int_set_ptr)(test_map_ptr))) UNORDERED_TEST(rehash_empty_test2, ((int_set_ptr)(test_map_ptr))( @@ -533,9 +602,11 @@ namespace rehash_tests { 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))) + UNORDERED_TEST( + rehash_nonempty_tracking, ((test_set_tracking)(test_map_tracking))( + (default_generator)(limited_range))) + UNORDERED_TEST(rehash_stability, ((test_set_monotonic)(test_map_monotonic))( + (default_generator)(limited_range))) #else boost::unordered_set* int_set_ptr; boost::unordered_multiset > >* test_multimap_tracking; + boost::unordered_set >* test_set_monotonic; + boost::unordered_multiset >* test_multiset_monotonic; + boost::unordered_map > >* + test_map_monotonic; + boost::unordered_multimap > >* + test_multimap_monotonic; + UNORDERED_TEST(rehash_empty_test1, ((int_set_ptr)(test_multiset_ptr)(test_map_ptr)(int_multimap_ptr))) UNORDERED_TEST(rehash_empty_test2, @@ -583,7 +666,10 @@ 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))) + UNORDERED_TEST(rehash_stability, + ((test_set_monotonic)(test_multiset_monotonic)(test_map_monotonic)(test_multimap_monotonic))( + (default_generator)(limited_range))) #endif -} +} // namespace rehash_tests RUN_TESTS() diff --git a/test/unordered/simple_tests.cpp b/test/unordered/simple_tests.cpp index e22f6db6..dbbf7a48 100644 --- a/test/unordered/simple_tests.cpp +++ b/test/unordered/simple_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) diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp index cee0a42f..6f41c876 100644 --- a/test/unordered/swap_tests.cpp +++ b/test/unordered/swap_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)