Merge branch 'develop' into feature/foa_rc_docs

This commit is contained in:
Peter Dimov
2022-11-07 21:24:55 +02:00
52 changed files with 1593 additions and 252 deletions

View File

@ -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

View File

@ -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' },
),
]

View File

@ -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

View File

@ -5,6 +5,7 @@
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
@ -294,6 +295,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
@ -366,7 +370,10 @@ template<> struct fnv1a_hash_impl<64>
}
};
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits > {};
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits >
{
using is_avalanching = void;
};
template<class K, class V> using std_unordered_map_fnv1a =
std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
@ -374,6 +381,9 @@ std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map_fnv1a =
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map_fnv1a =
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using multi_index_map_fnv1a = multi_index_container<
pair<K, V>,
indexed_by<
@ -418,10 +428,11 @@ int main()
{
init_indices();
#if 0
#if 1
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
test<multi_index_map>( "multi_index_map" );
#ifdef HAVE_ABSEIL
@ -449,6 +460,7 @@ int main()
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
test<multi_index_map_fnv1a>( "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";
}
}

View File

@ -5,6 +5,7 @@
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
@ -295,6 +296,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
@ -367,7 +371,10 @@ template<> struct fnv1a_hash_impl<64>
}
};
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits > {};
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits >
{
using is_avalanching = void;
};
template<class K, class V> using std_unordered_map_fnv1a =
std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
@ -375,6 +382,9 @@ std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map_fnv1a =
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map_fnv1a =
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using multi_index_map_fnv1a = multi_index_container<
pair<K, V>,
indexed_by<
@ -419,10 +429,11 @@ int main()
{
init_indices();
#if 0
#if 1
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
test<multi_index_map>( "multi_index_map" );
#ifdef HAVE_ABSEIL
@ -450,6 +461,7 @@ int main()
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
test<multi_index_map_fnv1a>( "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";
}
}

View File

@ -5,6 +5,7 @@
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
@ -311,6 +312,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
@ -347,6 +351,7 @@ int main()
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
test<multi_index_map>( "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";
}
}

View File

@ -12,7 +12,6 @@
#include <boost/endian/conversion.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/config.hpp>
#include <fxa_unordered/foa_unordered_rc.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
@ -313,28 +312,8 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template <typename Key, typename Value> struct map_types
{
using key_type = Key;
using value_type = std::pair<const Key, Value>;
static auto& extract(const value_type& x) { return x.first; }
};
template <class Key, class Value>
using boost_unordered_detail_foa_table =
boost::unordered::detail::foa::table<map_types<Key, Value>,
absl::container_internal::hash_default_hash<Key>, std::equal_to<Key>,
allocator_for<Key, Value> >;
template <typename Key, typename Value>
using boost_unordered_flat_map = boost::unordered::unordered_flat_map<Key,
Value, absl::container_internal::hash_default_hash<Key>, std::equal_to<Key>,
allocator_for<Key, Value> >;
template <typename Key, typename Value>
using rc15_flat_map = foa_unordered_rc_map<Key, Value,
absl::container_internal::hash_default_hash<Key>, std::equal_to<Key>,
allocator_for<Key, Value> >;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
@ -372,9 +351,7 @@ int main()
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_detail_foa_table>( "boost_unordered_detail_foa_table" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
test<rc15_flat_map>( "rc15_flat_map" );
test<multi_index_map>( "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";
}
}

402
benchmark/uuid.cpp Normal file
View File

@ -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 <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/config.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
#endif
#include <unordered_map>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <cstring>
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<class Map> 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<class Map> 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<class Map> 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<class Map> 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<class T> struct allocator
{
using value_type = T;
allocator() = default;
template<class U> allocator( allocator<U> const & ) noexcept
{
}
template<class U> bool operator==( allocator<U> const & ) const noexcept
{
return true;
}
template<class U> bool operator!=( allocator<U> const& ) const noexcept
{
return false;
}
T* allocate( std::size_t n ) const
{
s_alloc_bytes += n * sizeof(T);
s_alloc_count++;
return std::allocator<T>().allocate( n );
}
void deallocate( T* p, std::size_t n ) const noexcept
{
s_alloc_bytes -= n * sizeof(T);
s_alloc_count--;
std::allocator<T>().deallocate( p, n );
}
};
//
struct record
{
std::string label_;
long long time_;
std::size_t bytes_;
std::size_t count_;
};
static std::vector<record> times;
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
s_alloc_bytes = 0;
s_alloc_count = 0;
Map<uuid, std::uint64_t> 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<class K, class V> struct pair
{
K first;
mutable V second;
};
using namespace boost::multi_index;
template<class K, class V> using multi_index_map = multi_index_container<
pair<K, V>,
indexed_by<
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
>,
::allocator< pair<K, V> >
>;
// aliases using the counting allocator
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
template<class K, class V> using std_unordered_map =
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map =
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
int main()
{
init_indices();
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
test<multi_index_map>( "multi_index_map" );
#ifdef HAVE_ABSEIL
test<absl_node_hash_map>( "absl::node_hash_map" );
test<absl_flat_hash_map>( "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

View File

@ -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<unsigned char>( overflow() | static_cast<unsigned char>(1<<(hash%8)) );
#else
overflow()|=static_cast<unsigned char>(1<<(hash%8));
#endif
}
static inline bool maybe_caused_overflow(unsigned char* pc)
{
std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
group15 *pg=reinterpret_cast<group15*>(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<N);
return at(pos)==sentinel_;
return pos==N-1&&at(N-1)==sentinel_;
}
inline void reset(std::size_t pos)
@ -337,6 +349,13 @@ struct group15
overflow()|=static_cast<unsigned char>(1<<(hash%8));
}
static inline bool maybe_caused_overflow(unsigned char* pc)
{
std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
group15 *pg=reinterpret_cast<group15*>(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<boost::uint16_t*>(m)[hash%8]|=0x8000u;
}
static inline bool maybe_caused_overflow(unsigned char* pc)
{
std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group15);
group15 *pg=reinterpret_cast<group15*>(pc-pos);
boost::uint64_t x=((pg->m[0])>>pos)&0x000100010001ull;
boost::uint32_t y=static_cast<boost::uint32_t>(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<typename Key>
BOOST_FORCEINLINE
auto erase(Key&& x) -> typename std::enable_if<
!std::is_convertible<Key,iterator>::value&&
!std::is_convertible<Key,const_iterator>::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<unsigned char*>(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<float>(size_+1)/mlf)));
BOOST_TRY{
it=nosize_unchecked_emplace_at(
new_arrays_,position_for(hash,new_arrays_),
hash,std::forward<Args>(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>(args)...),
true
};
}
}
@ -1619,6 +1653,41 @@ private:
return size_policy::size(size_index_for<group_type,size_policy>(n))*N-1;
}
template<typename... Args>
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<float>(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>(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<typename... Args>
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>(args)...);
pg->set(n,hash);
return {pg,n,p};
}
std::pair<std::size_t,std::size_t>
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<typename... Args>
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<typename Predicate>
std::size_t erase_if_impl(Predicate pr)

View File

@ -3411,8 +3411,8 @@ namespace boost {
template <typename Types>
inline void table<Types>::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);

View File

@ -12,7 +12,7 @@
#define BOOST_UNORDERED_HASH_TRAITS_HPP
#include <boost/type_traits/make_void.hpp>
#include <type_traits>
#include <boost/type_traits/integral_constant.hpp>
namespace boost{
namespace unordered{
@ -20,11 +20,12 @@ namespace unordered{
namespace detail{
template<typename Hash,typename=void>
struct hash_is_avalanching_impl:std::false_type{};
struct hash_is_avalanching_impl: boost::false_type{};
template<typename Hash>
struct hash_is_avalanching_impl<Hash,void_t<typename Hash::is_avalanching>>:
std::true_type{};
struct hash_is_avalanching_impl<Hash,
typename boost::make_void<typename Hash::is_avalanching>::type>:
boost::true_type{};
} /* namespace detail */
@ -32,11 +33,11 @@ struct hash_is_avalanching_impl<Hash,void_t<typename Hash::is_avalanching>>:
* 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<Hash>::value is true when the type Hash::is_avalanching
* is present, false otherwise.
*/
template<typename Hash>
struct hash_is_avalanching:detail::hash_is_avalanching_impl<Hash>::type{};
struct hash_is_avalanching: detail::hash_is_avalanching_impl<Hash>::type{};
} /* namespace unordered */
} /* namespace boost */

View File

@ -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 Key, class T, class Hash, class KeyEqual, class Allocator>
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<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
@ -97,6 +106,13 @@ namespace boost {
{
}
template <class InputIterator>
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<value_type> il, allocator_type const& a)
: unordered_flat_map(il, size_type(0), hasher(), key_equal(), a)
{
}
unordered_flat_map(std::initializer_list<value_type> 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 <class Ty>
auto insert(Ty&& value)
BOOST_FORCEINLINE auto insert(Ty&& value)
-> decltype(table_.insert(std::forward<Ty>(value)))
{
return table_.insert(std::forward<Ty>(value));
}
std::pair<iterator, bool> insert(init_type&& value)
BOOST_FORCEINLINE std::pair<iterator, bool> insert(init_type&& value)
{
return table_.insert(std::move(value));
}
iterator insert(const_iterator, value_type const& value)
template <class Ty>
BOOST_FORCEINLINE auto insert(const_iterator, Ty&& value)
-> decltype(table_.insert(std::forward<Ty>(value)).first)
{
return table_.insert(value).first;
return table_.insert(std::forward<Ty>(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 <class InputIterator>
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 <class... Args> std::pair<iterator, bool> emplace(Args&&... args)
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class... Args>
iterator emplace_hint(const_iterator, Args&&... args)
BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
{
return this->emplace(std::forward<Args>(args)...).first;
return table_.emplace(std::forward<Args>(args)...).first;
}
template <class... Args>
std::pair<iterator, bool> try_emplace(key_type const& key, Args&&... args)
BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
key_type const& key, Args&&... args)
{
return table_.try_emplace(key, std::forward<Args>(args)...);
}
template <class... Args>
std::pair<iterator, bool> try_emplace(key_type&& key, Args&&... args)
BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
key_type&& key, Args&&... args)
{
return table_.try_emplace(std::move(key), std::forward<Args>(args)...);
}
template <class... Args>
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>(args)...).first;
}
template <class... Args>
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>(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 <class K>
typename std::enable_if<
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_flat_map>::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

View File

@ -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 Key, class Hash, class KeyEqual, class Allocator>
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<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
@ -79,6 +88,13 @@ namespace boost {
{
}
template <class InputIterator>
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<value_type> il, allocator_type const& a)
: unordered_flat_set(il, size_type(0), hasher(), key_equal(), a)
{
}
unordered_flat_set(std::initializer_list<value_type> 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<iterator, bool> insert(value_type const& value)
BOOST_FORCEINLINE std::pair<iterator, bool> insert(
value_type const& value)
{
return table_.insert(value);
}
std::pair<iterator, bool> insert(value_type&& value)
BOOST_FORCEINLINE std::pair<iterator, bool> 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 <class... Args> std::pair<iterator, bool> emplace(Args&&... args)
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class... Args>
iterator emplace_hint(const_iterator, Args&&... args)
BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
{
return this->emplace(std::forward<Args>(args)...).first;
return table_.emplace(std::forward<Args>(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 <class K>
typename std::enable_if<
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_flat_set>::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

View File

@ -123,6 +123,9 @@ namespace boost {
explicit unordered_map(size_type, const hasher&, const allocator_type&);
template <class InputIterator>
unordered_map(InputIterator, InputIterator, const allocator_type&);
template <class InputIt>
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<value_type>, const allocator_type&);
unordered_map(
std::initializer_list<value_type>, size_type, const allocator_type&);
@ -1076,6 +1081,9 @@ namespace boost {
explicit unordered_multimap(
size_type, const hasher&, const allocator_type&);
template <class InputIterator>
unordered_multimap(InputIterator, InputIterator, const allocator_type&);
template <class InputIt>
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<value_type>, const allocator_type&);
unordered_multimap(
std::initializer_list<value_type>, size_type, const allocator_type&);
@ -1751,6 +1762,17 @@ namespace boost {
{
}
template <class K, class T, class H, class P, class A>
template <class InputIterator>
unordered_map<K, T, H, P, A>::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 <class K, class T, class H, class P, class A>
template <class InputIt>
unordered_map<K, T, H, P, A>::unordered_map(
@ -1773,6 +1795,16 @@ namespace boost {
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
template <class K, class T, class H, class P, class A>
unordered_map<K, T, H, P, A>::unordered_map(
std::initializer_list<value_type> 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 <class K, class T, class H, class P, class A>
unordered_map<K, T, H, P, A>::unordered_map(
std::initializer_list<value_type> list, size_type n,
@ -2234,6 +2266,17 @@ namespace boost {
{
}
template <class K, class T, class H, class P, class A>
template <class InputIterator>
unordered_multimap<K, T, H, P, A>::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 <class K, class T, class H, class P, class A>
template <class InputIt>
unordered_multimap<K, T, H, P, A>::unordered_multimap(
@ -2256,6 +2299,16 @@ namespace boost {
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
template <class K, class T, class H, class P, class A>
unordered_multimap<K, T, H, P, A>::unordered_multimap(
std::initializer_list<value_type> 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 <class K, class T, class H, class P, class A>
unordered_multimap<K, T, H, P, A>::unordered_multimap(
std::initializer_list<value_type> list, size_type n,

View File

@ -121,6 +121,9 @@ namespace boost {
explicit unordered_set(size_type, const hasher&, const allocator_type&);
template <class InputIterator>
unordered_set(InputIterator, InputIterator, const allocator_type&);
template <class InputIt>
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<value_type>, const allocator_type&);
unordered_set(
std::initializer_list<value_type>, size_type, const allocator_type&);
@ -739,6 +744,9 @@ namespace boost {
explicit unordered_multiset(
size_type, const hasher&, const allocator_type&);
template <class InputIterator>
unordered_multiset(InputIterator, InputIterator, const allocator_type&);
template <class InputIt>
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<value_type>, const allocator_type&);
unordered_multiset(
std::initializer_list<value_type>, size_type, const allocator_type&);
@ -1352,6 +1363,17 @@ namespace boost {
{
}
template <class T, class H, class P, class A>
template <class InputIterator>
unordered_set<T, H, P, A>::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 <class T, class H, class P, class A>
template <class InputIt>
unordered_set<T, H, P, A>::unordered_set(
@ -1374,6 +1396,16 @@ namespace boost {
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
template <class T, class H, class P, class A>
unordered_set<T, H, P, A>::unordered_set(
std::initializer_list<value_type> 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 <class T, class H, class P, class A>
unordered_set<T, H, P, A>::unordered_set(
std::initializer_list<value_type> list, size_type n,
@ -1750,6 +1782,17 @@ namespace boost {
{
}
template <class T, class H, class P, class A>
template <class InputIterator>
unordered_multiset<T, H, P, A>::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 <class T, class H, class P, class A>
template <class InputIt>
unordered_multiset<T, H, P, A>::unordered_multiset(
@ -1772,6 +1815,16 @@ namespace boost {
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
template <class T, class H, class P, class A>
unordered_multiset<T, H, P, A>::unordered_multiset(
std::initializer_list<value_type> 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 <class T, class H, class P, class A>
unordered_multiset<T, H, P, A>::unordered_multiset(
std::initializer_list<value_type> list, size_type n,

View File

@ -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 : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_$(name) ;
run unordered/$(name).cpp : : : $(CPP11) <define>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 : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_link_test ;
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : $(CPP11) <define>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 : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <toolset>msvc-14.0:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ;
run unordered/scoped_allocator.cpp : : : $(CPP11) <toolset>msvc-14.0:<build>no <define>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 : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ;
run exception/copy_exception_tests.cpp : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ;
run exception/assign_exception_tests.cpp : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ;
run exception/move_assign_exception_tests.cpp : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_move_assign_exception_tests ;
run exception/insert_exception_tests.cpp : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_insert_exception_tests ;
run exception/erase_exception_tests.cpp : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_erase_exception_tests ;
run exception/rehash_exception_tests.cpp : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ;
run exception/swap_exception_tests.cpp : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ;
run exception/merge_exception_tests.cpp : : : <cxxstd>98:<build>no <cxxstd>03:<build>no <cxxstd>0x:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ;
run unordered/hash_is_avalanching_test.cpp ;
run exception/constructor_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ;
run exception/copy_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ;
run exception/assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ;
run exception/move_assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_move_assign_exception_tests ;
run exception/insert_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_insert_exception_tests ;
run exception/erase_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_erase_exception_tests ;
run exception/rehash_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ;
run exception/swap_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ;
run exception/merge_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ;

View File

@ -28,8 +28,7 @@ type_traits
# Secondary dependencies
detail
integer
describe
static_assert
winapi
)

View File

@ -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)

View File

@ -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"

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -12,7 +12,9 @@
#define BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER
#include "./fwd.hpp"
#include <boost/assert.hpp>
#include <boost/type_traits/add_const.hpp>
#include <climits>
#include <cstdlib>
#include <stdexcept>
#include <string>
@ -34,8 +36,18 @@ namespace test {
return static_cast<std::size_t>(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) {

View File

@ -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)

View File

@ -17,5 +17,6 @@
#include <boost/unordered_map.hpp>
#endif
#include "postfix.hpp"
// clang-format on
#endif

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 <class X, class T> 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<pointer,
typename boost::allocator_pointer<allocator_type>::type>::value));
BOOST_STATIC_ASSERT((boost::is_same<const_pointer,
typename boost::allocator_const_pointer<allocator_type>::type>::value));
// value_type
@ -509,13 +518,11 @@ template <class X> void equality_test(X& r)
template <class X, class T> 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<std::pair<iterator, bool> >::equals(r.insert(t));
test::check_return_type<std::pair<iterator, bool> >::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;

View File

@ -14,6 +14,8 @@
#include "../helpers/input_iterator.hpp"
#include "../helpers/invariants.hpp"
#include <vector>
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<T> v(1000, generator);
T x(v.begin(), v.end(), al);
BOOST_TEST(test::equivalent(x.get_allocator(), al));
test::check_container(x, v);
}
}
template <class T>
@ -317,66 +329,206 @@ namespace constructor_tests {
}
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
std::initializer_list<typename T::value_type> list;
typedef typename T::value_type value_type;
std::initializer_list<value_type> list;
test::random_values<T> v(3, generator);
std::vector<value_type> 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<value_type> 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
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 <boost/type_traits/integral_constant.hpp>
struct X3
{
};
namespace boost
{
namespace unordered
{
template<class T> struct hash_is_avalanching;
template<> struct hash_is_avalanching< ::X3 >: boost::true_type {};
} // namespace unordered
} // namespace boost
//
#include <boost/unordered/hash_traits.hpp>
#include <boost/core/lightweight_test_trait.hpp>
struct X1
{
};
struct X2
{
typedef void is_avalanching;
};
int main()
{
using boost::unordered::hash_is_avalanching;
BOOST_TEST_TRAIT_FALSE((hash_is_avalanching<X1>));
BOOST_TEST_TRAIT_TRUE((hash_is_avalanching<X2>));
BOOST_TEST_TRAIT_TRUE((hash_is_avalanching<X3>));
return boost::report_errors();
}

View File

@ -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)

View File

@ -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<raii_tracker, raii_tracker,
std::hash<raii_tracker> >
map;
{
std::pair<raii_tracker, raii_tracker> 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<raii_tracker, raii_tracker> 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<raii_tracker const, raii_tracker> 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<raii_tracker const, raii_tracker> 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();
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 <class X> 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<X> tracker;
{
test::random_values<X> 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<X> 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>* int_set_ptr;
boost::unordered_flat_map<test::movable, test::movable, test::hash,
test::equal_to, test::allocator2<test::movable> >* test_map_ptr;
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_set_tracking;
boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to,
test::allocator1<std::pair<test::object const, test::object> > >*
test_map_tracking;
UNORDERED_TEST(max_load_tests,
((int_set_ptr)(test_map_ptr)(test_set_tracking)(test_map_tracking))(
(sequential)))
#endif
RUN_TESTS()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 <class T> struct monotonic_allocator
{
typedef T value_type;
monotonic_allocator() {}
monotonic_allocator(monotonic_allocator const&) {}
template <class U> monotonic_allocator(monotonic_allocator<U> 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<T*>(::operator new(sizeof(T) * n));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
};
void reset_counts() { count_allocations = 0; }
template <class X> bool postcondition(X const& x, typename X::size_type n)
{
return static_cast<double>(x.bucket_count()) >=
@ -320,9 +353,10 @@ namespace rehash_tests {
BOOST_TEST_GT(x.bucket_count(), 0u);
x.reserve(
2 * (static_cast<size_type>(
std::floor(static_cast<float>(x.size()) / x.max_load_factor()) +
std::floor(static_cast<float>(x.size()) * x.max_load_factor()))));
2 *
(static_cast<size_type>(
std::floor(static_cast<float>(x.size()) / x.max_load_factor()) +
std::floor(static_cast<float>(x.size()) * x.max_load_factor()))));
BOOST_TEST_GT(x.bucket_count(), bucket_count);
@ -375,6 +409,34 @@ namespace rehash_tests {
}
}
template <class X> 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<X> v(num_elems, generator);
test::ordered<X> tracker;
tracker.insert_range(v.begin(), v.end());
typename test::random_values<X>::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 <class X> void rehash_test1(X*, test::random_generator generator)
{
test::random_values<X> v(1000, generator);
@ -512,6 +574,13 @@ namespace rehash_tests {
test::allocator1<std::pair<test::object const, test::object> > >*
test_map_tracking;
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
monotonic_allocator<test::object> >* test_set_monotonic;
boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to,
monotonic_allocator<std::pair<test::object const, test::object> > >*
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>* int_set_ptr;
boost::unordered_multiset<test::object, test::hash, test::equal_to,
@ -556,6 +627,18 @@ namespace rehash_tests {
test::allocator1<std::pair<test::object const, test::object> > >*
test_multimap_tracking;
boost::unordered_set<test::object, test::hash, test::equal_to,
monotonic_allocator<test::object> >* test_set_monotonic;
boost::unordered_multiset<test::object, test::hash, test::equal_to,
monotonic_allocator<test::object> >* test_multiset_monotonic;
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
monotonic_allocator<std::pair<test::object const, test::object> > >*
test_map_monotonic;
boost::unordered_multimap<test::object, test::object, test::hash,
test::equal_to,
monotonic_allocator<std::pair<test::object const, test::object> > >*
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()

View File

@ -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)

View File

@ -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)