Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8cd7da67a | |||
| d2797abcb7 | |||
| ffb34fdcde | |||
| 38eca0efc0 | |||
| 4f8a037957 | |||
| 61d92b5847 | |||
| 33f4b04f69 | |||
| 8b024986da | |||
| 4948a3a12e | |||
| 35c265f313 | |||
| 15b63cc169 | |||
| 3de21a474c | |||
| 6a6a3084da | |||
| abd69e859e | |||
| ff20d71676 |
@@ -31,6 +31,25 @@ environment:
|
||||
B2_VARIANT: debug,release
|
||||
|
||||
matrix:
|
||||
- FLAVOR: Visual Studio 2008
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-9.0
|
||||
B2_ADDRESS_MODEL: 32 # No 64bit support
|
||||
|
||||
- FLAVOR: Visual Studio 2010
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-10.0
|
||||
B2_ADDRESS_MODEL: 32 # No 64bit support
|
||||
|
||||
- FLAVOR: Visual Studio 2012
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-11.0
|
||||
B2_ADDRESS_MODEL: 32 # No 64bit support
|
||||
|
||||
- FLAVOR: Visual Studio 2013
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-12.0
|
||||
|
||||
- FLAVOR: Visual Studio 2015
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-14.0
|
||||
@@ -50,6 +69,13 @@ environment:
|
||||
B2_CXXSTD: latest
|
||||
B2_TOOLSET: msvc-14.1
|
||||
|
||||
- FLAVOR: cygwin (32-bit, C++03)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
ADDPATH: C:\cygwin\bin;
|
||||
B2_ADDRESS_MODEL: 32
|
||||
B2_CXXSTD: 03
|
||||
B2_TOOLSET: gcc
|
||||
|
||||
- FLAVOR: cygwin (32-bit, C++11)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
ADDPATH: C:\cygwin\bin;
|
||||
@@ -71,6 +97,15 @@ environment:
|
||||
B2_CXXSTD: 1z
|
||||
B2_TOOLSET: gcc
|
||||
|
||||
- FLAVOR: cygwin (64-bit, latest, C++03)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 03
|
||||
B2_TOOLSET: gcc
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
B2_VARIANT: release
|
||||
|
||||
- FLAVOR: cygwin (64-bit, latest, C++11)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
@@ -98,6 +133,13 @@ environment:
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
B2_VARIANT: release
|
||||
|
||||
- FLAVOR: mingw-w64, 32 bit, C++03
|
||||
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
|
||||
B2_TOOLSET: gcc
|
||||
B2_ADDRESS_MODEL: 32
|
||||
|
||||
- FLAVOR: mingw-w64, 32 bit, C++11
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
|
||||
@@ -126,6 +168,13 @@ environment:
|
||||
B2_TOOLSET: gcc
|
||||
B2_ADDRESS_MODEL: 32
|
||||
|
||||
- FLAVOR: mingw-w64, 64 bit, C++03
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
|
||||
B2_CXXSTD: 03
|
||||
B2_TOOLSET: gcc
|
||||
B2_ADDRESS_MODEL: 64
|
||||
|
||||
- FLAVOR: mingw-w64, 64 bit, C++11
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
|
||||
|
||||
@@ -100,16 +100,40 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
};
|
||||
|
||||
[
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.4 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.4', CXXSTD: '98,0x', ADDRMD: '32,64' },
|
||||
"g++-4.4-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.6 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.6', CXXSTD: '98,0x', ADDRMD: '32,64' },
|
||||
"g++-4.6-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.7 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.7', CXXSTD: '98,0x', ADDRMD: '32,64' },
|
||||
"g++-4.7-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.8* 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11', ADDRMD: '32,64' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.9 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '11', ADDRMD: '32,64' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '03,11', ADDRMD: '32,64' },
|
||||
"g++-4.9-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
@@ -117,26 +141,26 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
linux_pipeline(
|
||||
"Linux 16.04 GCC 5* 32/64",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14', ADDRMD: '32,64' },
|
||||
{ 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: '11,14', ADDRMD: '32,64' },
|
||||
{ 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: '11,14,17', ADDRMD: '32,64' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 18.04 GCC 8 32/64 (11)",
|
||||
"Linux 18.04 GCC 8 32/64 (03,11)",
|
||||
"cppalliance/droneubuntu1804:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-8', CXXSTD: '11', ADDRMD: '32,64' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-8', CXXSTD: '03,11', ADDRMD: '32,64' },
|
||||
"g++-8-multilib",
|
||||
),
|
||||
|
||||
@@ -148,9 +172,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* 32/64 (11,14)",
|
||||
"Linux 20.04 GCC 9* 32/64 (03,11,14)",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14', ADDRMD: '32,64' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
@@ -162,14 +186,14 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* ARM64",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14,17,2a' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' },
|
||||
arch="arm64",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* S390x (11,14)",
|
||||
"Linux 20.04 GCC 9* S390x (03,11,14)",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14' },
|
||||
arch="s390x",
|
||||
),
|
||||
|
||||
@@ -181,9 +205,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 10 32/64 (11,14)",
|
||||
"Linux 20.04 GCC 10 32/64 (03,11,14)",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-10', CXXSTD: '11,14', ADDRMD: '32,64' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-10', CXXSTD: '03,11,14', ADDRMD: '32,64' },
|
||||
"g++-10-multilib",
|
||||
),
|
||||
|
||||
@@ -195,9 +219,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 GCC 11* 32/64 (11,14)",
|
||||
"Linux 22.04 GCC 11* 32/64 (03,11,14)",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14', ADDRMD: '32,64' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
@@ -207,9 +231,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 GCC 12 32 ASAN (11,14)",
|
||||
"Linux 22.04 GCC 12 32 ASAN (03,11,14)",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '11', ADDRMD: '32' } + asan,
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11', ADDRMD: '32' } + asan,
|
||||
"g++-12-multilib",
|
||||
),
|
||||
|
||||
@@ -242,9 +266,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 GCC 12 64 ASAN (11,14)",
|
||||
"Linux 22.04 GCC 12 64 ASAN (03,11,14)",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '11,14', ADDRMD: '64' } + asan,
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14', ADDRMD: '64' } + asan,
|
||||
"g++-12-multilib",
|
||||
),
|
||||
|
||||
@@ -277,9 +301,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 23.04 GCC 13 32/64 (11,14)",
|
||||
"Linux 23.04 GCC 13 32/64 (03,11,14)",
|
||||
"cppalliance/droneubuntu2304:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '11,14', ADDRMD: '32,64' },
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '03,11,14', ADDRMD: '32,64' },
|
||||
"g++-13 g++-13-multilib",
|
||||
),
|
||||
|
||||
@@ -293,112 +317,112 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
linux_pipeline(
|
||||
"Linux 16.04 Clang 3.5",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-3.5', CXXSTD: '11' },
|
||||
{ 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: '11,14' },
|
||||
{ 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: '11,14' },
|
||||
{ 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: '11,14' },
|
||||
{ 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: '11,14' },
|
||||
{ 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: '11,14' },
|
||||
{ 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: '11,14,1z' },
|
||||
{ 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: '11,14,17' },
|
||||
{ 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: '11,14,17' },
|
||||
{ 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: '11,14,17' },
|
||||
{ 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: '11,14,17,2a' },
|
||||
{ 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: '11,14,17,2a' },
|
||||
{ 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: '11,14,17,2a' },
|
||||
{ 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: '11,14,17,2a' },
|
||||
{ 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: '11,14,17,20' },
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-13', CXXSTD: '03,11,14,17,20' },
|
||||
"clang-13",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 14 UBSAN (11,14)",
|
||||
"Linux 22.04 Clang 14 UBSAN (03,11,14)",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '11,14' } + ubsan,
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14' } + ubsan,
|
||||
"clang-14",
|
||||
),
|
||||
|
||||
@@ -410,9 +434,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 14 ASAN (11,14)",
|
||||
"Linux 22.04 Clang 14 ASAN (03,11,14)",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '11,14' } + asan,
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14' } + asan,
|
||||
"clang-14",
|
||||
),
|
||||
|
||||
@@ -433,14 +457,14 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 15",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-15', CXXSTD: '11,14,17,20,2b' },
|
||||
{ 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(
|
||||
"MacOS 10.15 Xcode 12.2 UBSAN (11)",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11' } + ubsan,
|
||||
"MacOS 10.15 Xcode 12.2 UBSAN (03,11)",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11' } + ubsan,
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
@@ -455,7 +479,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 12.4 Xcode 13.4.1 ASAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11,14,1z' } + asan,
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + asan,
|
||||
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
|
||||
),
|
||||
|
||||
|
||||
@@ -20,4 +20,4 @@ b2 -d0 headers
|
||||
|
||||
if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
|
||||
if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
|
||||
b2 --abbreviate-paths -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
|
||||
@@ -32,16 +32,6 @@ env:
|
||||
CODECOV_NAME: Github Actions
|
||||
|
||||
jobs:
|
||||
runner-selection:
|
||||
# runs-on: ubuntu-latest
|
||||
runs-on: ${{ github.repository_owner == 'boostorg' && fromJSON('[ "self-hosted", "linux", "x64", "ubuntu-latest-aws" ]') || 'ubuntu-latest' }}
|
||||
outputs:
|
||||
labelmatrix: ${{ steps.aws_hosted_runners.outputs.labelmatrix }}
|
||||
steps:
|
||||
- name: AWS Hosted Runners
|
||||
id: aws_hosted_runners
|
||||
uses: cppalliance/aws-hosted-runners@v1.0.0
|
||||
|
||||
posix:
|
||||
defaults:
|
||||
run:
|
||||
@@ -52,71 +42,52 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
# Linux, gcc
|
||||
- { compiler: gcc-7, cxxstd: '11,14,17', os: 'ubuntu-20.04', install: 'g++-7' }
|
||||
- { compiler: gcc-8, cxxstd: '11,14,17', os: 'ubuntu-20.04', install: 'g++-8' }
|
||||
- { compiler: gcc-9, cxxstd: '11,14,17', os: 'ubuntu-22.04', install: 'g++-9' }
|
||||
- { compiler: gcc-10, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', install: 'g++-10' }
|
||||
- { compiler: gcc-11, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', install: 'g++-11' }
|
||||
- { name: "gcc-12 w/ sanitizers (11)", sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '11', os: 'ubuntu-22.04', ccache_key: "san1" }
|
||||
- { name: "gcc-12 w/ sanitizers (14)", sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '14', os: 'ubuntu-22.04', ccache_key: "san1" }
|
||||
- { name: "gcc-12 w/ sanitizers (17)", sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '17', os: 'ubuntu-22.04', ccache_key: "san2" }
|
||||
- { name: "gcc-12 w/ sanitizers (20)", sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '20', os: 'ubuntu-22.04', ccache_key: "san2" }
|
||||
- { name: "gcc-12 w/ sanitizers (2b)", sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '2b', os: 'ubuntu-22.04', ccache_key: "san2" }
|
||||
- { 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-22.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-12, cxxstd: '20', os: 'ubuntu-22.04', install: 'g++-12-multilib', address-model: '32,64', ccache_key: "cov" }
|
||||
compiler: gcc-12, cxxstd: '03,20', os: ubuntu-22.04, install: 'g++-12-multilib', address-model: '32,64', ccache_key: "cov" }
|
||||
|
||||
- { name: "cfoa tsan (gcc-12)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-22.04', compiler: gcc-12,
|
||||
- { name: "cfoa tsan (gcc-12)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: gcc-12,
|
||||
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes, ccache_key: "tsan" }
|
||||
|
||||
# Linux, clang, libc++
|
||||
- { compiler: clang-7, cxxstd: '11,14,17', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' }
|
||||
- { compiler: clang-10, cxxstd: '11,14,17,20', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' }
|
||||
- { compiler: clang-11, cxxstd: '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 (11,14)", sanitize: yes,
|
||||
compiler: clang-12, cxxstd: '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)", sanitize: yes,
|
||||
compiler: clang-12, cxxstd: '17', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
|
||||
- { name: "clang-12 w/ sanitizers (20)", sanitize: yes,
|
||||
compiler: clang-12, cxxstd: '20', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
|
||||
- { name: "clang-12 w/ sanitizers (2b)", sanitize: yes,
|
||||
compiler: clang-12, cxxstd: '2b', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
|
||||
- { compiler: 'clang-13', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
|
||||
- { compiler: 'clang-13', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
|
||||
- { 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' }
|
||||
|
||||
# not using libc++ because of https://github.com/llvm/llvm-project/issues/52771
|
||||
- { name: "clang-14 w/ sanitizers (11,14)", sanitize: yes,
|
||||
compiler: clang-14, cxxstd: '11,14', os: 'ubuntu-22.04', ccache_key: "san1" }
|
||||
- { name: "clang-14 w/ sanitizers (17)", sanitize: yes,
|
||||
compiler: clang-14, cxxstd: '17', os: 'ubuntu-22.04', ccache_key: "san2" }
|
||||
- { name: "clang-14 w/ sanitizers (20)", sanitize: yes,
|
||||
compiler: clang-14, cxxstd: '20', os: 'ubuntu-22.04', ccache_key: "san2" }
|
||||
- { name: "clang-14 w/ sanitizers (2b)", sanitize: yes,
|
||||
compiler: clang-14, cxxstd: '2b', os: 'ubuntu-22.04', ccache_key: "san2" }
|
||||
- { 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" }
|
||||
|
||||
- { name: "cfoa tsan (clang-14)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-22.04', compiler: clang-14,
|
||||
- { name: "cfoa tsan (clang-14)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: clang-14,
|
||||
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes,
|
||||
stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev', ccache_key: "tsan" }
|
||||
|
||||
- { compiler: 'clang-15', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
|
||||
- { compiler: 'clang-15', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
|
||||
- { compiler: clang-15, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
|
||||
|
||||
# OSX, clang
|
||||
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-11' }
|
||||
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-12', sanitize: yes, ccache_key: "san1" }
|
||||
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-12', thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan" }
|
||||
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-13' }
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11 }
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes, ccache_key: "san1" }
|
||||
- { compiler: clang, cxxstd: '11,14,17,2a', os: macos-12, thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan" }
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,20,2b', os: macos-13 }
|
||||
|
||||
timeout-minutes: 360
|
||||
# posix (gcc-12 w/ sanitizers is taking longer than 210 minutes
|
||||
# timeout-minutes: 210
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
|
||||
timeout-minutes: 210
|
||||
runs-on: ${{matrix.os}}
|
||||
container: ${{matrix.container}}
|
||||
env: {B2_USE_CCACHE: 1}
|
||||
|
||||
@@ -260,16 +231,15 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
|
||||
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
|
||||
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
|
||||
- { toolset: gcc, cxxstd: '11,14,17,2a', addrmd: '64', os: 'windows-2019', variant: 'debug,release' }
|
||||
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: windows-2019, variant: 'debug,release' }
|
||||
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2019, variant: 'debug,release' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: windows-2022, variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
|
||||
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: windows-2022, variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
|
||||
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
|
||||
- { toolset: gcc, cxxstd: '03,11,14,17,2a', addrmd: '64', os: windows-2019, variant: 'debug,release' }
|
||||
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -323,11 +293,10 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { sys: MINGW32, compiler: gcc, cxxstd: '11,17,20' }
|
||||
- { sys: MINGW64, compiler: gcc, cxxstd: '11,17,20' }
|
||||
- { sys: MINGW32, compiler: gcc, cxxstd: '03,11,17,20' }
|
||||
- { sys: MINGW64, compiler: gcc, cxxstd: '03,11,17,20' }
|
||||
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)['windows-latest'] }}
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -381,12 +350,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { os: 'ubuntu-20.04', build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
|
||||
- { os: 'windows-2019', build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
|
||||
- { os: ubuntu-20.04, build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
|
||||
- { os: windows-2019, build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
|
||||
|
||||
timeout-minutes: 120
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 23 KiB |
@@ -447,10 +447,6 @@ operations follow a https://en.wikipedia.org/wiki/Zipf%27s_law#Formal_definition
|
||||
with different _skew_ parameters: the higher the skew, the more concentrated are the keys in the lower values
|
||||
of the covered range.
|
||||
|
||||
`boost::concurrent_flat_map` is exercised using both regular and xref:#concurrent_bulk_visitation[bulk visitation]:
|
||||
in the latter case, lookup keys are buffered in a local array and then processed at
|
||||
once each time the buffer reaches xref:#concurrent_flat_map_constants[`bulk_visit_size`].
|
||||
|
||||
=== GCC 12, x64
|
||||
|
||||
|
||||
|
||||
@@ -6,22 +6,6 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.84.0 - Major update
|
||||
|
||||
* Added `boost::concurrent_flat_set`.
|
||||
* Added `[c]visit_while` operations to concurrent containers,
|
||||
with serial and parallel variants.
|
||||
* Added efficient move construction of `boost::unordered_flat_(map|set)` from
|
||||
`boost::concurrent_flat_(map|set)` and vice versa.
|
||||
* Added bulk visitation to concurrent containers for increased lookup performance.
|
||||
* Added debug-mode mechanisms for detecting illegal reentrancies into
|
||||
a concurrent container from user code.
|
||||
* Added Boost.Serialization support to all containers and their (non-local) iterator types.
|
||||
* Added support for fancy pointers to open-addressing and concurrent containers.
|
||||
This enables scenarios like the use of Boost.Interprocess allocators to construct containers in shared memory.
|
||||
* Starting with this release, `boost::unordered_[multi]set` and `boost::unordered_[multi]map`
|
||||
only work with C++11 onwards.
|
||||
|
||||
== Release 1.83.0 - Major update
|
||||
|
||||
* Added `boost::concurrent_flat_map`, a fast, thread-safe hashmap based on open addressing.
|
||||
|
||||
@@ -7,54 +7,116 @@
|
||||
|
||||
== Closed-addressing Containers
|
||||
|
||||
`boost::unordered_[multi]set` and `boost::unordered_[multi]map` provide a conformant
|
||||
implementation for {cpp}11 (or later) compilers of the latest standard revision of
|
||||
{cpp} unordered associative containers, with very minor deviations as noted.
|
||||
The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
|
||||
and support https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers^].
|
||||
`unordered_[multi]set` and `unordered_[multi]map` are intended to provide a conformant
|
||||
implementation of the {cpp}20 standard that will work with {cpp}98 upwards.
|
||||
This wide compatibility does mean some compromises have to be made.
|
||||
With a compiler and library that fully support {cpp}11, the differences should
|
||||
be minor.
|
||||
|
||||
=== Deduction Guides
|
||||
=== Move Emulation
|
||||
|
||||
Deduction guides for
|
||||
https://en.cppreference.com/w/cpp/language/class_template_argument_deduction[class template argument deduction (CTAD)^]
|
||||
are only available on {cpp}17 (or later) compilers.
|
||||
Support for move semantics is implemented using Boost.Move. If rvalue
|
||||
references are available it will use them, but if not it uses a close,
|
||||
but imperfect emulation. On such compilers:
|
||||
|
||||
=== Piecewise Pair Emplacement
|
||||
* Non-copyable objects can be stored in the containers.
|
||||
They can be constructed in place using `emplace`, or if they support
|
||||
Boost.Move, moved into place.
|
||||
* The containers themselves are not movable.
|
||||
* Argument forwarding is not perfect.
|
||||
|
||||
In accordance with the standard specification,
|
||||
`boost::unordered_[multi]map::emplace` supports piecewise pair construction:
|
||||
=== Use of Allocators
|
||||
|
||||
{cpp}11 introduced a new allocator system. It's backwards compatible due to
|
||||
the lax requirements for allocators in the old standard, but might need
|
||||
some changes for allocators which worked with the old versions of the
|
||||
unordered containers.
|
||||
It uses a traits class, `allocator_traits` to handle the allocator
|
||||
adding extra functionality, and making some methods and types optional.
|
||||
During development a stable release of
|
||||
`allocator_traits` wasn't available so an internal partial implementation
|
||||
is always used in this version. Hopefully a future version will use the
|
||||
standard implementation where available.
|
||||
|
||||
The member functions `construct`, `destroy` and `max_size` are now
|
||||
optional, if they're not available a fallback is used.
|
||||
A full implementation of `allocator_traits` requires sophisticated
|
||||
member function detection so that the fallback is used whenever the
|
||||
member function call is not well formed.
|
||||
This requires support for SFINAE expressions, which are available on
|
||||
GCC from version 4.4 and Clang.
|
||||
|
||||
On other compilers, there's just a test to see if the allocator has
|
||||
a member, but no check that it can be called. So rather than using a
|
||||
fallback there will just be a compile error.
|
||||
|
||||
`propagate_on_container_copy_assignment`,
|
||||
`propagate_on_container_move_assignment`,
|
||||
`propagate_on_container_swap` and
|
||||
`select_on_container_copy_construction` are also supported.
|
||||
Due to imperfect move emulation, some assignments might check
|
||||
`propagate_on_container_copy_assignment` on some compilers and
|
||||
`propagate_on_container_move_assignment` on others.
|
||||
|
||||
=== Construction/Destruction Using Allocators
|
||||
|
||||
The following support is required for full use of {cpp}11 style
|
||||
construction/destruction:
|
||||
|
||||
* Variadic templates.
|
||||
* Piecewise construction of `std::pair`.
|
||||
* Either `std::allocator_traits` or expression SFINAE.
|
||||
|
||||
This is detected using Boost.Config. The macro
|
||||
`BOOST_UNORDERED_CXX11_CONSTRUCTION` will be set to 1 if it is found, or 0
|
||||
otherwise.
|
||||
|
||||
When this is the case `allocator_traits::construct` and
|
||||
`allocator_traits::destroy` will always be used, apart from when piecewise
|
||||
constructing a `std::pair` using `boost::tuple` (see <<compliance_pairs,below>>), but that should be easily avoided.
|
||||
|
||||
When support is not available `allocator_traits::construct` and
|
||||
`allocator_traits::destroy` are never called.
|
||||
|
||||
=== Pointer Traits
|
||||
|
||||
`pointer_traits` aren't used. Instead, pointer types are obtained from
|
||||
rebound allocators, this can cause problems if the allocator can't be
|
||||
used with incomplete types. If `const_pointer` is not defined in the
|
||||
allocator, `boost::pointer_to_other<pointer, const value_type>::type`
|
||||
is used to obtain a const pointer.
|
||||
|
||||
=== Pairs
|
||||
|
||||
Since the containers use `std::pair` they're limited to the version
|
||||
from the current standard library. But since {cpp}11 ``std::pair``'s
|
||||
`piecewise_construct` based constructor is very useful, `emplace`
|
||||
emulates it with a `piecewise_construct` in the `boost::unordered`
|
||||
namespace. So for example, the following will work:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
boost::unordered_multimap<std::string, std::complex> x;
|
||||
|
||||
x.emplace(
|
||||
std::piecewise_construct,
|
||||
std::make_tuple("key"), std::make_tuple(1, 2));
|
||||
----
|
||||
|
||||
Additionally, the same
|
||||
functionality is provided via non-standard `boost::unordered::piecewise_construct`
|
||||
and Boost.Tuple:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
x.emplace(
|
||||
boost::unordered::piecewise_construct,
|
||||
boost::make_tuple("key"), boost::make_tuple(1, 2));
|
||||
----
|
||||
|
||||
This feature has been retained for backwards compatibility with
|
||||
previous versions of Boost.Unordered: users are encouraged to
|
||||
update their code to use `std::piecewise_construct` and
|
||||
``std::tuple``s instead.
|
||||
Older drafts of the standard also supported variadic constructors
|
||||
for `std::pair`, where the first argument would be used for the
|
||||
first part of the pair, and the remaining for the second part.
|
||||
|
||||
=== Swap
|
||||
=== Miscellaneous
|
||||
|
||||
When swapping, `Pred` and `Hash` are not currently swapped by calling
|
||||
`swap`, their copy constructors are used. As a consequence, when swapping
|
||||
`swap`, their copy constructors are used. As a consequence when swapping
|
||||
an exception may be thrown from their copy constructor.
|
||||
|
||||
Variadic constructor arguments for `emplace` are only used when both
|
||||
rvalue references and variadic template parameters are available.
|
||||
Otherwise `emplace` can only take up to 10 constructors arguments.
|
||||
|
||||
== Open-addressing Containers
|
||||
|
||||
The C++ standard does not currently provide any open-addressing container
|
||||
@@ -67,9 +129,7 @@ radically different from that imposed by the standard (closed addressing).
|
||||
Open-addressing containers provided by Boost.Unordered only work with reasonably
|
||||
compliant C++11 (or later) compilers. Language-level features such as move semantics
|
||||
and variadic template parameters are then not emulated.
|
||||
The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
|
||||
and support https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers^].
|
||||
|
||||
The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^].
|
||||
|
||||
The main differences with C++ unordered associative containers are:
|
||||
|
||||
@@ -88,24 +148,22 @@ The main differences with C++ unordered associative containers are:
|
||||
|
||||
== Concurrent Containers
|
||||
|
||||
There is currently no specification in the C++ standard for this or any other type of concurrent
|
||||
data structure. The APIs of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`
|
||||
are modelled after `std::unordered_flat_set` and `std::unordered_flat_map`, respectively,
|
||||
with the crucial difference that iterators are not provided
|
||||
There is currently no specification in the C++ standard for this or any other concurrent
|
||||
data structure. `boost::concurrent_flat_map` takes the same template parameters as `std::unordered_map`
|
||||
and all the maps provided by Boost.Unordered, and its API is modelled after that of
|
||||
`boost::unordered_flat_map` with the crucial difference that iterators are not provided
|
||||
due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking):
|
||||
so, Boost.Unordered concurrent containers are technically not models of
|
||||
so, `boost::concurrent_flat_map` is technically not a
|
||||
https://en.cppreference.com/w/cpp/named_req/Container[Container^], although
|
||||
they meet all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
|
||||
containers (including
|
||||
https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointer^] support)
|
||||
except those implying iterators.
|
||||
it meets all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
|
||||
containers except those implying iterators.
|
||||
|
||||
In a non-concurrent unordered container, iterators serve two main purposes:
|
||||
|
||||
* Access to an element previously located via lookup.
|
||||
* Container traversal.
|
||||
|
||||
In place of iterators, `boost::concurrent_flat_set` and `boost::concurrent_flat_map` use _internal visitation_
|
||||
In place of iterators, `boost::concurrent_flat_map` uses _internal visitation_
|
||||
facilities as a thread-safe substitute. Classical operations returning an iterator to an
|
||||
element already existing in the container, like for instance:
|
||||
|
||||
@@ -133,15 +191,15 @@ template<class F> size_t visit_all(F f);
|
||||
----
|
||||
|
||||
of which there are parallelized versions in C++17 compilers with parallel
|
||||
algorithm support. In general, the interface of concurrent containers
|
||||
is derived from that of their non-concurrent counterparts by a fairly straightforward
|
||||
process of replacing iterators with visitation where applicable. If for
|
||||
regular maps `iterator` and `const_iterator` provide mutable and const access to elements,
|
||||
algorithm support. In general, the interface of `boost::concurrent_flat_map`
|
||||
is derived from that of `boost::unordered_flat_map` by a fairly straightforward
|
||||
process of replacing iterators with visitation where applicable. If
|
||||
`iterator` and `const_iterator` provide mutable and const access to elements,
|
||||
respectively, here visitation is granted mutable or const access depending on
|
||||
the constness of the member function used (there are also `*cvisit` overloads for
|
||||
explicit const visitation); In the case of `boost::concurrent_flat_set`, visitation is always const.
|
||||
explicit const visitation).
|
||||
|
||||
One notable operation not provided by `boost::concurrent_flat_map` is `operator[]`/`at`, which can be
|
||||
The one notable operation not provided is `operator[]`/`at`, which can be
|
||||
replaced, if in a more convoluted manner, by
|
||||
xref:#concurrent_flat_map_try_emplace_or_cvisit[`try_emplace_or_visit`].
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
:idprefix: concurrent_
|
||||
|
||||
Boost.Unordered provides `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
hash tables that allow concurrent write/read access from
|
||||
Boost.Unordered currently provides just one concurrent container named `boost::concurrent_flat_map`.
|
||||
`boost::concurrent_flat_map` is a hash table that allows concurrent write/read access from
|
||||
different threads without having to implement any synchronzation mechanism on the user's side.
|
||||
|
||||
[source,c++]
|
||||
@@ -36,16 +36,16 @@ In the example above, threads access `m` without synchronization, just as we'd d
|
||||
single-threaded scenario. In an ideal setting, if a given workload is distributed among
|
||||
_N_ threads, execution is _N_ times faster than with one thread —this limit is
|
||||
never attained in practice due to synchronization overheads and _contention_ (one thread
|
||||
waiting for another to leave a locked portion of the map), but Boost.Unordered concurrent containers
|
||||
are designed to perform with very little overhead and typically achieve _linear scaling_
|
||||
waiting for another to leave a locked portion of the map), but `boost::concurrent_flat_map`
|
||||
is designed to perform with very little overhead and typically achieves _linear scaling_
|
||||
(that is, performance is proportional to the number of threads up to the number of
|
||||
logical cores in the CPU).
|
||||
|
||||
== Visitation-based API
|
||||
|
||||
The first thing a new user of `boost::concurrent_flat_set` or `boost::concurrent_flat_map`
|
||||
will notice is that these classes _do not provide iterators_ (which makes them technically
|
||||
not https://en.cppreference.com/w/cpp/named_req/Container[Containers^]
|
||||
The first thing a new user of `boost::concurrent_flat_map` will notice is that this
|
||||
class _does not provide iterators_ (which makes it technically
|
||||
not a https://en.cppreference.com/w/cpp/named_req/Container[Container^]
|
||||
in the C++ standard sense). The reason for this is that iterators are inherently
|
||||
thread-unsafe. Consider this hypothetical code:
|
||||
|
||||
@@ -73,7 +73,7 @@ m.visit(k, [](const auto& x) { // x is the element with key k (if it exists)
|
||||
----
|
||||
|
||||
The visitation function passed by the user (in this case, a lambda function)
|
||||
is executed internally by Boost.Unordered in
|
||||
is executed internally by `boost::concurrent_flat_map` in
|
||||
a thread-safe manner, so it can access the element without worrying about other
|
||||
threads interfering in the process.
|
||||
|
||||
@@ -112,7 +112,7 @@ if (found) {
|
||||
}
|
||||
----
|
||||
|
||||
Visitation is prominent in the API provided by `boost::concurrent_flat_set` and `boost::concurrent_flat_map`, and
|
||||
Visitation is prominent in the API provided by `boost::concurrent_flat_map`, and
|
||||
many classical operations have visitation-enabled variations:
|
||||
|
||||
[source,c++]
|
||||
@@ -129,17 +129,13 @@ the element: as a general rule, operations on a `boost::concurrent_flat_map` `m`
|
||||
will grant visitation functions const/non-const access to the element depending on whether
|
||||
`m` is const/non-const. Const access can be always be explicitly requested
|
||||
by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result
|
||||
in higher parallelization. For `boost::concurrent_flat_set`, on the other hand,
|
||||
visitation is always const access.
|
||||
Consult the references of
|
||||
xref:#concurrent_flat_set[`boost::concurrent_flat_set`] and
|
||||
xref:#concurrent_flat_map[`boost::concurrent_flat_map`]
|
||||
for the complete list of visitation-enabled operations.
|
||||
in higher parallelization. Consult the xref:#concurrent_flat_map[reference]
|
||||
for a complete list of available operations.
|
||||
|
||||
== Whole-Table Visitation
|
||||
|
||||
In the absence of iterators, `visit_all` is provided
|
||||
as an alternative way to process all the elements in the container:
|
||||
In the absence of iterators, `boost::concurrent_flat_map` provides `visit_all`
|
||||
as an alternative way to process all the elements in the map:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
@@ -158,28 +154,7 @@ m.visit_all(std::execution::par, [](auto& x) { // run in parallel
|
||||
});
|
||||
----
|
||||
|
||||
Traversal can be interrupted midway:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
// finds the key to a given (unique) value
|
||||
|
||||
int key = 0;
|
||||
int value = ...;
|
||||
bool found = !m.visit_while([&](const auto& x) {
|
||||
if(x.second == value) {
|
||||
key = x.first;
|
||||
return false; // finish
|
||||
}
|
||||
else {
|
||||
return true; // keep on visiting
|
||||
}
|
||||
});
|
||||
|
||||
if(found) { ... }
|
||||
----
|
||||
|
||||
There is one last whole-table visitation operation, `erase_if`:
|
||||
There is another whole-table visitation operation, `erase_if`:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
@@ -188,64 +163,15 @@ m.erase_if([](auto& x) {
|
||||
});
|
||||
----
|
||||
|
||||
`visit_while` and `erase_if` can also be parallelized. Note that, in order to increase efficiency,
|
||||
whole-table visitation operations do not block the table during execution: this implies that elements
|
||||
`erase_if` can also be parallelized. Note that, in order to increase efficiency,
|
||||
these operations do not block the table during execution: this implies that elements
|
||||
may be inserted, modified or erased by other threads during visitation. It is
|
||||
advisable not to assume too much about the exact global state of a concurrent container
|
||||
advisable not to assume too much about the exact global state of a `boost::concurrent_flat_map`
|
||||
at any point in your program.
|
||||
|
||||
== Bulk visitation
|
||||
|
||||
Suppose you have an `std::array` of keys you want to look up for in a concurrent map:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
std::array<int, N> keys;
|
||||
...
|
||||
for(const auto& key: keys) {
|
||||
m.visit(key, [](auto& x) { ++x.second; });
|
||||
}
|
||||
----
|
||||
|
||||
_Bulk visitation_ allows us to pass all the keys in one operation:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
m.visit(keys.begin(), keys.end(), [](auto& x) { ++x.second; });
|
||||
----
|
||||
|
||||
This functionality is not provided for mere syntactic convenience, though: by processing all the
|
||||
keys at once, some internal optimizations can be applied that increase
|
||||
performance over the regular, one-at-a-time case (consult the
|
||||
xref:#benchmarks_boostconcurrent_flat_map[benchmarks]). In fact, it may be beneficial
|
||||
to buffer incoming keys so that they can be bulk visited in chunks:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
static constexpr auto bulk_visit_size = boost::concurrent_flat_map<int,int>::bulk_visit_size;
|
||||
std::array<int, bulk_visit_size> buffer;
|
||||
std::size_t i=0;
|
||||
while(...) { // processing loop
|
||||
...
|
||||
buffer[i++] = k;
|
||||
if(i == bulk_visit_size) {
|
||||
map.visit(buffer.begin(), buffer.end(), [](auto& x) { ++x.second; });
|
||||
i = 0;
|
||||
}
|
||||
...
|
||||
}
|
||||
// flush remaining keys
|
||||
map.visit(buffer.begin(), buffer.begin() + i, [](auto& x) { ++x.second; });
|
||||
----
|
||||
|
||||
There's a latency/throughput tradeoff here: it will take longer for incoming keys to
|
||||
be processed (since they are buffered), but the number of processed keys per second
|
||||
is higher. `bulk_visit_size` is the recommended chunk size —smaller buffers
|
||||
may yield worse performance.
|
||||
|
||||
== Blocking Operations
|
||||
|
||||
``boost::concurrent_flat_set``s and ``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
|
||||
``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
|
||||
Boost.Unordered container. Unlike most other operations, these are _blocking_,
|
||||
that is, all other threads are prevented from accesing the tables involved while a copy, assignment,
|
||||
clear or merge operation is in progress. Blocking is taken care of automatically by the library
|
||||
@@ -254,31 +180,3 @@ and the user need not take any special precaution, but overall performance may b
|
||||
Another blocking operation is _rehashing_, which happens explicitly via `rehash`/`reserve`
|
||||
or during insertion when the table's load hits `max_load()`. As with non-concurrent containers,
|
||||
reserving space in advance of bulk insertions will generally speed up the process.
|
||||
|
||||
== Interoperability with non-concurrent containers
|
||||
|
||||
As open-addressing and concurrent containers are based on the same internal data structure,
|
||||
`boost::unordered_flat_set` and `boost::unordered_flat_map` can
|
||||
be efficiently move-constructed from `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
respectively, and vice versa.
|
||||
This interoperability comes handy in multistage scenarios where parts of the data processing happen
|
||||
in parallel whereas other steps are non-concurrent (or non-modifying). In the following example,
|
||||
we want to construct a histogram from a huge input vector of words:
|
||||
the population phase can be done in parallel with `boost::concurrent_flat_map` and results
|
||||
then transferred to the final container.
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
std::vector<std::string> words = ...;
|
||||
|
||||
// Insert words in parallel
|
||||
boost::concurrent_flat_map<std::string_view, std::size_t> m0;
|
||||
std::for_each(
|
||||
std::execution::par, words.begin(), words.end(),
|
||||
[&](const auto& word) {
|
||||
m0.try_emplace_or_visit(word, 1, [](auto& x) { ++x.second; });
|
||||
});
|
||||
|
||||
// Transfer to a regular unordered_flat_map
|
||||
boost::unordered_flat_map m=std::move(m0);
|
||||
----
|
||||
|
||||
@@ -50,9 +50,6 @@ namespace boost {
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// constants
|
||||
static constexpr size_type xref:#concurrent_flat_map_constants[bulk_visit_size] = _implementation-defined_;
|
||||
|
||||
// construct/copy/destroy
|
||||
xref:#concurrent_flat_map_default_constructor[concurrent_flat_map]();
|
||||
explicit xref:#concurrent_flat_map_bucket_count_constructor[concurrent_flat_map](size_type n,
|
||||
@@ -72,7 +69,6 @@ namespace boost {
|
||||
explicit xref:#concurrent_flat_map_allocator_constructor[concurrent_flat_map](const Allocator& a);
|
||||
xref:#concurrent_flat_map_copy_constructor_with_allocator[concurrent_flat_map](const concurrent_flat_map& other, const Allocator& a);
|
||||
xref:#concurrent_flat_map_move_constructor_with_allocator[concurrent_flat_map](concurrent_flat_map&& other, const Allocator& a);
|
||||
xref:#concurrent_flat_map_move_constructor_from_unordered_flat_map[concurrent_flat_map](unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#concurrent_flat_map_initializer_list_constructor[concurrent_flat_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -93,10 +89,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#concurrent_flat_map_destructor[~concurrent_flat_map]();
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_copy_assignment[operator++=++](const concurrent_flat_map& other);
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#concurrent_flat_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -109,13 +104,6 @@ namespace boost {
|
||||
template<class K, class F> size_t xref:#concurrent_flat_map_cvisit[visit](const K& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_map_cvisit[cvisit](const K& k, F f) const;
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_map_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const;
|
||||
|
||||
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[visit_all](F f);
|
||||
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[visit_all](F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[cvisit_all](F f) const;
|
||||
@@ -126,16 +114,6 @@ namespace boost {
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_map_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f);
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f) const;
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[cvisit_while](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
// capacity
|
||||
++[[nodiscard]]++ bool xref:#concurrent_flat_map_empty[empty]() const noexcept;
|
||||
size_type xref:#concurrent_flat_map_size[size]() const noexcept;
|
||||
@@ -327,7 +305,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the table.
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the table's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -385,25 +364,6 @@ if concurrent outstanding operations on `y` do not access `x` directly or indire
|
||||
|
||||
---
|
||||
|
||||
=== Configuration Macros
|
||||
|
||||
==== `BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK`
|
||||
|
||||
In debug builds (more precisely, when
|
||||
link:../../../assert/doc/html/assert.html#boost_assert_is_void[`BOOST_ASSERT_IS_VOID`^]
|
||||
is not defined), __container reentrancies__ (illegaly invoking an operation on `m` from within
|
||||
a function visiting elements of `m`) are detected and signalled through `BOOST_ASSERT_MSG`.
|
||||
When run-time speed is a concern, the feature can be disabled by globally defining
|
||||
this macro.
|
||||
|
||||
=== Constants
|
||||
|
||||
```cpp
|
||||
static constexpr size_type bulk_visit_size;
|
||||
```
|
||||
|
||||
Chunk size internally used in xref:concurrent_flat_map_bulk_visit[bulk visit] operations.
|
||||
|
||||
=== Constructors
|
||||
|
||||
==== Default Constructor
|
||||
@@ -531,21 +491,6 @@ Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from unordered_flat_map
|
||||
|
||||
```c++
|
||||
concurrent_flat_map(unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#unordered_flat_map[`unordered_flat_map`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; O(`bucket_count()`)
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -690,9 +635,8 @@ Concurrency:;; Blocking on `*this` and `other`.
|
||||
==== Move Assignment
|
||||
```c++
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -739,42 +683,6 @@ Notes:;; The `template<class K, class F>` overloads only participate in overload
|
||||
|
||||
---
|
||||
|
||||
==== Bulk visit
|
||||
|
||||
```c++
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t cvisit(FwdIterator first, FwdIterator last, F f) const;
|
||||
```
|
||||
|
||||
For each element `k` in the range [`first`, `last`),
|
||||
if there is an element `x` in the container with key equivalent to `k`,
|
||||
invokes `f` with a reference to `x`.
|
||||
Such reference is const iff `*this` is const.
|
||||
|
||||
Although functionally equivalent to individually invoking
|
||||
xref:concurrent_flat_map_cvisit[`[c\]visit`] for each key, bulk visitation
|
||||
performs generally faster due to internal streamlining optimizations.
|
||||
It is advisable that `std::distance(first,last)` be at least
|
||||
xref:#concurrent_flat_map_constants[`bulk_visit_size`] to enjoy
|
||||
a performance gain: beyond this size, performance is not expected
|
||||
to increase further.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `FwdIterator` is a https://en.cppreference.com/w/cpp/named_req/ForwardIterator[LegacyForwardIterator^]
|
||||
({cpp}11 to {cpp}17),
|
||||
or satisfies https://en.cppreference.com/w/cpp/iterator/forward_iterator[std::forward_iterator^] ({cpp}20 and later).
|
||||
For `K` = `std::iterator_traits<FwdIterator>::value_type`, either `K` is `key_type` or
|
||||
else `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs.
|
||||
In the latter case, the library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent.
|
||||
This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
Returns:;; The number of elements visited.
|
||||
|
||||
---
|
||||
|
||||
==== [c]visit_all
|
||||
|
||||
```c++
|
||||
@@ -812,50 +720,6 @@ Unsequenced execution policies are not allowed.
|
||||
|
||||
---
|
||||
|
||||
==== [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class F> bool visit_while(F f);
|
||||
template<class F> bool visit_while(F f) const;
|
||||
template<class F> bool cvisit_while(F f) const;
|
||||
```
|
||||
|
||||
Successively invokes `f` with references to each of the elements in the table until `f` returns `false`
|
||||
or all the elements are visited.
|
||||
Such references to the elements are const iff `*this` is const.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; `false` iff `f` ever returns `false`.
|
||||
|
||||
---
|
||||
|
||||
==== Parallel [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
|
||||
```
|
||||
|
||||
Invokes `f` with references to each of the elements in the table until `f` returns `false`
|
||||
or all the elements are visited.
|
||||
Such references to the elements are const iff `*this` is const.
|
||||
Execution is parallelized according to the semantics of the execution policy specified.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; `false` iff `f` ever returns `false`.
|
||||
Throws:;; Depending on the exception handling mechanism of the execution policy used, may call `std::terminate` if an exception is thrown within `f`.
|
||||
Notes:;; Only available in compilers supporting C++17 parallel algorithms. +
|
||||
+
|
||||
These overloads only participate in overload resolution if `std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>` is `true`. +
|
||||
+
|
||||
Unsequenced execution policies are not allowed. +
|
||||
+
|
||||
Parallelization implies that execution does not necessary finish as soon as `f` returns `false`, and as a result
|
||||
`f` may be invoked with further elements for which the return value is also `false`.
|
||||
|
||||
---
|
||||
|
||||
=== Size and Capacity
|
||||
|
||||
==== empty
|
||||
@@ -1098,13 +962,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
unlike xref:#concurrent_flat_map_emplace[emplace], which simply forwards all arguments to ``value_type``'s constructor.
|
||||
@@ -1146,13 +1010,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
|
||||
```c++
|
||||
// first four overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
|
||||
// last two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
Invalidates pointers and references to elements if a rehashing is issued.
|
||||
@@ -1174,19 +1038,19 @@ template<class K, class M> bool insert_or_assign(K&& k, M&& obj);
|
||||
|
||||
Inserts a new element into the table or updates an existing one by assigning to the contained value.
|
||||
|
||||
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
|
||||
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
|
||||
|
||||
If there is no such element, it is added to the table as:
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
@@ -1572,33 +1436,3 @@ Equivalent to
|
||||
-----
|
||||
c.xref:#concurrent_flat_map_erase_if[erase_if](pred);
|
||||
-----
|
||||
|
||||
=== Serialization
|
||||
|
||||
``concurrent_flat_map``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an concurrent_flat_map to an archive
|
||||
|
||||
Saves all the elements of a `concurrent_flat_map` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
Concurrency:;; Blocking on `x`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an concurrent_flat_map from an archive
|
||||
|
||||
Deletes all preexisting elements of a `concurrent_flat_map` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `concurrent_flat_map` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Concurrency:;; Blocking on `x`.
|
||||
|
||||
@@ -44,8 +44,7 @@ boost::unordered_flat_map
|
||||
|
||||
^.^h|*Concurrent*
|
||||
^|
|
||||
^| `boost::concurrent_flat_set` +
|
||||
`boost::concurrent_flat_map`
|
||||
^| `boost::concurrent_flat_map`
|
||||
|
||||
|===
|
||||
|
||||
@@ -57,8 +56,9 @@ in the market within the technical constraints imposed by the required standard
|
||||
interface to accommodate the implementation.
|
||||
There are two variants: **flat** (the fastest) and **node-based**, which
|
||||
provide pointer stability under rehashing at the expense of being slower.
|
||||
* Finally, **concurrent containers** are designed and implemented to be used in high-performance
|
||||
multithreaded scenarios. Their interface is radically different from that of regular C++ containers.
|
||||
* Finally, `boost::concurrent_flat_map` (the only **concurrent container** provided
|
||||
at present) is a hashmap designed and implemented to be used in high-performance
|
||||
multithreaded scenarios. Its interface is radically different from that of regular C++ containers.
|
||||
|
||||
All sets and maps in Boost.Unordered are instantiatied similarly as
|
||||
`std::unordered_set` and `std::unordered_map`, respectively:
|
||||
@@ -73,7 +73,6 @@ namespace boost {
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_set;
|
||||
// same for unordered_multiset, unordered_flat_set, unordered_node_set
|
||||
// and concurrent_flat_set
|
||||
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
|
||||
@@ -121,21 +121,20 @@ for Visual Studio on an x64-mode Intel CPU with SSE2 and for GCC on an IBM s390x
|
||||
== Concurrent Containers
|
||||
|
||||
The same data structure used by Boost.Unordered open-addressing containers has been chosen
|
||||
also as the foundation of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`:
|
||||
also as the foundation of `boost::concurrent_flat_map`:
|
||||
|
||||
* Open-addressing is faster than closed-addressing alternatives, both in non-concurrent and
|
||||
concurrent scenarios.
|
||||
* Open-addressing layouts are eminently suitable for concurrent access and modification
|
||||
with minimal locking. In particular, the metadata array can be used for implementations of
|
||||
lookup that are lock-free up to the last step of actual element comparison.
|
||||
* Layout compatibility with Boost.Unordered flat containers allows for
|
||||
xref:#concurrent_interoperability_with_non_concurrent_containers[fast transfer]
|
||||
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`,
|
||||
and vice versa.
|
||||
* Layout compatibility with Boost.Unordered flat containers allows for fast transfer
|
||||
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`.
|
||||
(This feature has not been implemented yet.)
|
||||
|
||||
=== Hash Function and Platform Interoperability
|
||||
|
||||
Concurrent containers make the same decisions and provide the same guarantees
|
||||
`boost::concurrent_flat_map` makes the same decisions and provides the same guarantees
|
||||
as Boost.Unordered open-addressing containers with regards to
|
||||
xref:#rationale_hash_function[hash function defaults] and
|
||||
xref:#rationale_platform_interoperability[platform interoperability].
|
||||
|
||||
@@ -11,4 +11,3 @@ include::unordered_flat_set.adoc[]
|
||||
include::unordered_node_map.adoc[]
|
||||
include::unordered_node_set.adoc[]
|
||||
include::concurrent_flat_map.adoc[]
|
||||
include::concurrent_flat_set.adoc[]
|
||||
|
||||
@@ -67,8 +67,8 @@ xref:#rationale_closed_addressing_containers[corresponding section].
|
||||
|
||||
== Open-addressing Containers
|
||||
|
||||
The diagram shows the basic internal layout of `boost::unordered_flat_set`/`unordered_node_set` and
|
||||
`boost:unordered_flat_map`/`unordered_node_map`.
|
||||
The diagram shows the basic internal layout of `boost::unordered_flat_map`/`unordered_node_map` and
|
||||
`boost:unordered_flat_set`/`unordered_node_set`.
|
||||
|
||||
|
||||
[#img-foa-layout]
|
||||
@@ -76,7 +76,7 @@ The diagram shows the basic internal layout of `boost::unordered_flat_set`/`unor
|
||||
image::foa.png[align=center]
|
||||
|
||||
As with all open-addressing containers, elements (or pointers to the element nodes in the case of
|
||||
`boost::unordered_node_set` and `boost::unordered_node_map`) are stored directly in the bucket array.
|
||||
`boost::unordered_node_map` and `boost::unordered_node_set`) are stored directly in the bucket array.
|
||||
This array is logically divided into 2^_n_^ _groups_ of 15 elements each.
|
||||
In addition to the bucket array, there is an associated _metadata array_ with 2^_n_^
|
||||
16-byte words.
|
||||
@@ -129,7 +129,7 @@ xref:#rationale_open_addresing_containers[corresponding section].
|
||||
|
||||
== Concurrent Containers
|
||||
|
||||
`boost::concurrent_flat_set` and `boost::concurrent_flat_map` use the basic
|
||||
`boost::concurrent_flat_map` uses the basic
|
||||
xref:#structures_open_addressing_containers[open-addressing layout] described above
|
||||
augmented with synchronization mechanisms.
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ namespace boost {
|
||||
explicit xref:#unordered_flat_map_allocator_constructor[unordered_flat_map](const Allocator& a);
|
||||
xref:#unordered_flat_map_copy_constructor_with_allocator[unordered_flat_map](const unordered_flat_map& other, const Allocator& a);
|
||||
xref:#unordered_flat_map_move_constructor_with_allocator[unordered_flat_map](unordered_flat_map&& other, const Allocator& a);
|
||||
xref:#unordered_flat_map_move_constructor_from_concurrent_flat_map[unordered_flat_map](concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_flat_map_initializer_list_constructor[unordered_flat_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -97,10 +96,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_flat_map_destructor[~unordered_flat_map]();
|
||||
unordered_flat_map& xref:#unordered_flat_map_copy_assignment[operator++=++](const unordered_flat_map& other);
|
||||
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_flat_map& xref:#unordered_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -313,7 +311,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -473,22 +472,6 @@ from `other`, and the allocator is copy-constructed from `a`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_flat_map
|
||||
|
||||
```c++
|
||||
unordered_flat_map(concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_flat_map[`concurrent_flat_map`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -632,9 +615,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_flat_map& operator=(unordered_flat_map&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -909,13 +891,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
unlike xref:#unordered_flat_map_emplace[emplace], which simply forwards all arguments to ``value_type``'s constructor.
|
||||
@@ -954,13 +936,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
unlike xref:#unordered_flat_map_emplace_hint[emplace_hint], which simply forwards all arguments to ``value_type``'s constructor.
|
||||
@@ -985,19 +967,19 @@ template<class K, class M>
|
||||
|
||||
Inserts a new element into the container or updates an existing one by assigning to the contained value.
|
||||
|
||||
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
|
||||
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
|
||||
|
||||
If there is no such element, it is added to the container as:
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
@@ -1023,19 +1005,19 @@ template<class K, class M>
|
||||
|
||||
Inserts a new element into the container or updates an existing one by assigning to the contained value.
|
||||
|
||||
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
|
||||
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
|
||||
|
||||
If there is no such element, it is added to the container as:
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
```
|
||||
|
||||
`hint` is a suggestion to where the element should be inserted. This implementation ignores it.
|
||||
@@ -1326,7 +1308,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
|
||||
Invalidates iterators, pointers and references, and changes the order of elements.
|
||||
|
||||
@@ -1465,54 +1447,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_flat_map``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_flat_map to an archive
|
||||
|
||||
Saves all the elements of an `unordered_flat_map` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_flat_map from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_flat_map` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_flat_map` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_flat_map` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_flat_map` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace boost {
|
||||
xref:#unordered_flat_set_iterator_range_constructor_with_allocator[unordered_flat_set](InputIterator f, InputIterator l, const allocator_type& a);
|
||||
explicit xref:#unordered_flat_set_allocator_constructor[unordered_flat_set](const Allocator& a);
|
||||
xref:#unordered_flat_set_copy_constructor_with_allocator[unordered_flat_set](const unordered_flat_set& other, const Allocator& a);
|
||||
xref:#unordered_flat_set_move_constructor_from_concurrent_flat_set[unordered_flat_set](concurrent_flat_set<Key, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_flat_set_move_constructor_with_allocator[unordered_flat_set](unordered_flat_set&& other, const Allocator& a);
|
||||
xref:#unordered_flat_set_initializer_list_constructor[unordered_flat_set](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -91,10 +91,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_flat_set_destructor[~unordered_flat_set]();
|
||||
unordered_flat_set& xref:#unordered_flat_set_copy_assignment[operator++=++](const unordered_flat_set& other);
|
||||
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_flat_set& xref:#unordered_flat_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -262,7 +261,8 @@ and https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the con
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -422,22 +422,6 @@ from `other`, and the allocator is copy-constructed from `a`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_flat_set
|
||||
|
||||
```c++
|
||||
unordered_flat_set(concurrent_flat_set<Key, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_flat_set[`concurrent_flat_set`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -581,9 +565,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_flat_set& operator=(unordered_flat_set&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -1104,7 +1087,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
|
||||
Invalidates iterators, pointers and references, and changes the order of elements.
|
||||
|
||||
@@ -1218,54 +1201,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_flat_set``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_flat_set to an archive
|
||||
|
||||
Saves all the elements of an `unordered_flat_set` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type`
|
||||
is serializable (XML serializable), and it supports Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_flat_set from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_flat_set` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_flat_set` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_flat_set` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_flat_set` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -26,12 +26,12 @@ namespace boost {
|
||||
using hasher = Hash;
|
||||
using key_equal = Pred;
|
||||
using allocator_type = Allocator;
|
||||
using pointer = typename std::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
|
||||
using pointer = typename boost::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = typename boost::allocator_traits<Allocator>::const_pointer;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = _implementation-defined_;
|
||||
using difference_type = _implementation-defined_;
|
||||
|
||||
using iterator = _implementation-defined_;
|
||||
using const_iterator = _implementation-defined_;
|
||||
@@ -316,7 +316,6 @@ namespace boost {
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@@ -324,15 +323,51 @@ The elements are organized into buckets. Keys with the same hash code are stored
|
||||
|
||||
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
|
||||
|
||||
=== Configuration macros
|
||||
|
||||
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
|
||||
Globally define this macro to support loading of ``unordered_map``s saved to
|
||||
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
|
||||
---
|
||||
|
||||
=== Typedefs
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef typename allocator_type::pointer pointer;
|
||||
----
|
||||
|
||||
`value_type*` if `allocator_type::pointer` is not defined.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef typename allocator_type::const_pointer const_pointer;
|
||||
----
|
||||
|
||||
`boost::pointer_to_other<pointer, value_type>::type` if `allocator_type::const_pointer` is not defined.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ size_type;
|
||||
----
|
||||
|
||||
An unsigned integral type.
|
||||
|
||||
`size_type` can represent any non-negative value of `difference_type`.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ difference_type;
|
||||
----
|
||||
|
||||
A signed integral type.
|
||||
|
||||
Is identical to the difference type of `iterator` and `const_iterator`.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ iterator;
|
||||
@@ -384,8 +419,7 @@ A const_local_iterator object can be used to iterate through a single bucket.
|
||||
typedef _implementation-defined_ node_type;
|
||||
----
|
||||
|
||||
A class for holding extracted container elements, modelling
|
||||
https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle].
|
||||
See node_handle_map for details.
|
||||
|
||||
---
|
||||
|
||||
@@ -394,20 +428,7 @@ https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle].
|
||||
typedef _implementation-defined_ insert_return_type;
|
||||
----
|
||||
|
||||
A specialization of an internal class template:
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
template<class Iterator, class NodeType>
|
||||
struct _insert_return_type_ // name is exposition only
|
||||
{
|
||||
Iterator position;
|
||||
bool inserted;
|
||||
NodeType node;
|
||||
};
|
||||
----
|
||||
|
||||
with `Iterator` = `iterator` and `NodeType` = `node_type`.
|
||||
Structure returned by inserting node_type.
|
||||
|
||||
---
|
||||
|
||||
@@ -487,7 +508,10 @@ The move constructor.
|
||||
|
||||
[horizontal]
|
||||
Notes:;; This is implemented using Boost.Move.
|
||||
Requires:;; `value_type` is move-constructible.
|
||||
Requires:;; `value_type` is move-constructible. +
|
||||
+
|
||||
On compilers without rvalue reference support the emulation does not support moving without calling `boost::move` if `value_type` is not copyable.
|
||||
So, for example, you can't return the container from a function.
|
||||
|
||||
---
|
||||
|
||||
@@ -687,6 +711,7 @@ The move assignment operator.
|
||||
If `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`, the allocator is overwritten, if not the moved elements are created using the existing allocator.
|
||||
|
||||
[horizontal]
|
||||
Notes:;; On compilers without rvalue references, this is emulated using Boost.Move. Note that on some compilers the copy assignment operator may be used in some circumstances.
|
||||
Requires:;; `value_type` is move constructible.
|
||||
|
||||
---
|
||||
@@ -797,7 +822,11 @@ If an insert took place, then the iterator points to the newly inserted element.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. +
|
||||
+
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
|
||||
---
|
||||
|
||||
@@ -818,7 +847,11 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
|
||||
+
|
||||
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to 10 arguments, with no support for rvalue references or move semantics. +
|
||||
+
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
|
||||
---
|
||||
|
||||
@@ -996,13 +1029,13 @@ Notes:;; This function is similiar to xref:#unordered_map_emplace[emplace] excep
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
instead of xref:#unordered_map_emplace[emplace] which simply forwards all arguments to ``value_type``'s constructor.
|
||||
@@ -1012,6 +1045,10 @@ Can invalidate iterators, but only if the insert causes the load factor to be gr
|
||||
Pointers and references to elements are never invalidated.
|
||||
|
||||
The `template<class K, class\... Args>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics.
|
||||
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
--
|
||||
|
||||
---
|
||||
@@ -1041,13 +1078,13 @@ Notes:;; This function is similiar to xref:#unordered_map_emplace_hint[emplace_h
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
instead of xref:#unordered_map_emplace_hint[emplace_hint] which simply forwards all arguments to ``value_type``'s constructor.
|
||||
@@ -1059,6 +1096,10 @@ Can invalidate iterators, but only if the insert causes the load factor to be gr
|
||||
Pointers and references to elements are never invalidated.
|
||||
|
||||
The `template<class K, class\... Args>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics.
|
||||
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
--
|
||||
|
||||
---
|
||||
@@ -1075,19 +1116,19 @@ template<class K, class M>
|
||||
|
||||
Inserts a new element into the container or updates an existing one by assigning to the contained value.
|
||||
|
||||
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
|
||||
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
|
||||
|
||||
If there is no such element, it is added to the container as:
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
@@ -1115,19 +1156,19 @@ template<class K, class M>
|
||||
|
||||
Inserts a new element into the container or updates an existing one by assigning to the contained value.
|
||||
|
||||
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
|
||||
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
|
||||
|
||||
If there is no such element, it is added to the container as:
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
```
|
||||
|
||||
`hint` is a suggestion to where the element should be inserted.
|
||||
@@ -1720,7 +1761,9 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
|
||||
Return `true` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
|
||||
|
||||
[horizontal]
|
||||
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
|
||||
+
|
||||
Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
|
||||
---
|
||||
|
||||
@@ -1734,7 +1777,9 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
|
||||
Return `false` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
|
||||
|
||||
[horizontal]
|
||||
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
|
||||
+
|
||||
Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
|
||||
=== Swap
|
||||
```c++
|
||||
@@ -1780,59 +1825,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_map``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_map to an archive
|
||||
|
||||
Saves all the elements of an `unordered_map` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_map from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_map` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_map` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]
|
||||
from `(std::remove_const<key_type>::type&&, std::remove_const<mapped_type>::type&&)`.
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
|
||||
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_map` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_map` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -26,12 +26,12 @@ namespace boost {
|
||||
using hasher = Hash;
|
||||
using key_equal = Pred;
|
||||
using allocator_type = Allocator;
|
||||
using pointer = typename std::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
|
||||
using pointer = typename boost::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = typename boost::allocator_traits<Allocator>::const_pointer;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = _implementation-defined_;
|
||||
using difference_type = _implementation-defined_;
|
||||
|
||||
using iterator = _implementation-defined_;
|
||||
using const_iterator = _implementation-defined_;
|
||||
@@ -283,7 +283,6 @@ namespace boost {
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@@ -291,15 +290,49 @@ The elements are organized into buckets. Keys with the same hash code are stored
|
||||
|
||||
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
|
||||
|
||||
=== Configuration macros
|
||||
|
||||
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
|
||||
Globally define this macro to support loading of ``unordered_multimap``s saved to
|
||||
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
|
||||
|
||||
=== Typedefs
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef typename allocator_type::pointer pointer;
|
||||
----
|
||||
|
||||
`value_type*` if `allocator_type::pointer` is not defined.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef typename allocator_type::const_pointer const_pointer;
|
||||
----
|
||||
|
||||
`boost::pointer_to_other<pointer, value_type>::type` if `allocator_type::const_pointer` is not defined.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ size_type;
|
||||
----
|
||||
|
||||
An unsigned integral type.
|
||||
|
||||
`size_type` can represent any non-negative value of `difference_type`.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ difference_type;
|
||||
----
|
||||
|
||||
A signed integral type.
|
||||
|
||||
Is identical to the difference type of `iterator` and `const_iterator`.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ iterator;
|
||||
@@ -431,7 +464,10 @@ The move constructor.
|
||||
|
||||
[horizontal]
|
||||
Notes:;; This is implemented using Boost.Move.
|
||||
Requires:;; `value_type` is move-constructible.
|
||||
Requires:;; `value_type` is move-constructible. +
|
||||
+
|
||||
On compilers without rvalue reference support the emulation does not support moving without calling `boost::move` if `value_type` is not copyable.
|
||||
So, for example, you can't return the container from a function.
|
||||
|
||||
---
|
||||
|
||||
@@ -630,6 +666,7 @@ The move assignment operator.
|
||||
If `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`, the allocator is overwritten, if not the moved elements are created using the existing allocator.
|
||||
|
||||
[horizontal]
|
||||
Notes:;; On compilers without rvalue references, this is emulated using Boost.Move. Note that on some compilers the copy assignment operator may be used in some circumstances.
|
||||
Requires:;; `value_type` is move constructible.
|
||||
|
||||
---
|
||||
@@ -738,7 +775,11 @@ Returns:;; An iterator pointing to the inserted element.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. +
|
||||
+
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
|
||||
---
|
||||
|
||||
@@ -759,7 +800,11 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
|
||||
+
|
||||
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to 10 arguments, with no support for rvalue references or move semantics. +
|
||||
+
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
|
||||
---
|
||||
|
||||
@@ -1441,7 +1486,9 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
|
||||
Return `true` if `x.size() == y.size()` and for every equivalent key group in `x`, there is a group in `y` for the same key, which is a permutation (using `operator==` to compare the value types).
|
||||
|
||||
[horizontal]
|
||||
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
|
||||
+
|
||||
Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
|
||||
---
|
||||
|
||||
@@ -1455,7 +1502,9 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
|
||||
Return `false` if `x.size() == y.size()` and for every equivalent key group in `x`, there is a group in `y` for the same key, which is a permutation (using `operator==` to compare the value types).
|
||||
|
||||
[horizontal]
|
||||
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
|
||||
+
|
||||
Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
|
||||
---
|
||||
|
||||
@@ -1503,59 +1552,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_multimap``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_multimap to an archive
|
||||
|
||||
Saves all the elements of an `unordered_multimap` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_multimap from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_multimap` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_multimap` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]
|
||||
from `(std::remove_const<key_type>::type&&, std::remove_const<mapped_type>::type&&)`.
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
|
||||
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_multimap` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_multimap` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -24,12 +24,12 @@ namespace boost {
|
||||
using hasher = Hash;
|
||||
using key_equal = Pred;
|
||||
using allocator_type = Allocator;
|
||||
using pointer = typename std::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
|
||||
using pointer = typename boost::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = typename boost::allocator_traits<Allocator>::const_pointer;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = _implementation-defined_;
|
||||
using difference_type = _implementation-defined_;
|
||||
|
||||
using iterator = _implementation-defined_;
|
||||
using const_iterator = _implementation-defined_;
|
||||
@@ -271,7 +271,6 @@ namespace boost {
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@@ -279,15 +278,50 @@ The elements are organized into buckets. Keys with the same hash code are stored
|
||||
|
||||
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
|
||||
|
||||
=== Configuration macros
|
||||
|
||||
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
|
||||
Globally define this macro to support loading of ``unordered_multiset``s saved to
|
||||
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
|
||||
---
|
||||
|
||||
=== Typedefs
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef typename allocator_type::pointer pointer;
|
||||
----
|
||||
|
||||
`value_type*` if `allocator_type::pointer` is not defined.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef typename allocator_type::const_pointer const_pointer;
|
||||
----
|
||||
|
||||
`boost::pointer_to_other<pointer, value_type>::type` if `allocator_type::const_pointer` is not defined.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ size_type;
|
||||
----
|
||||
|
||||
An unsigned integral type.
|
||||
|
||||
`size_type` can represent any non-negative value of `difference_type`.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ difference_type;
|
||||
----
|
||||
|
||||
A signed integral type.
|
||||
|
||||
Is identical to the difference type of `iterator` and `const_iterator`.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ iterator;
|
||||
@@ -419,7 +453,10 @@ The move constructor.
|
||||
|
||||
[horizontal]
|
||||
Notes:;; This is implemented using Boost.Move.
|
||||
Requires:;; `value_type` is move-constructible.
|
||||
Requires:;; `value_type` is move-constructible. +
|
||||
+
|
||||
On compilers without rvalue reference support the emulation does not support moving without calling `boost::move` if `value_type` is not copyable.
|
||||
So, for example, you can't return the container from a function.
|
||||
|
||||
---
|
||||
|
||||
@@ -619,6 +656,7 @@ The move assignment operator.
|
||||
If `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`, the allocator is overwritten, if not the moved elements are created using the existing allocator.
|
||||
|
||||
[horizontal]
|
||||
Notes:;; On compilers without rvalue references, this is emulated using Boost.Move. Note that on some compilers the copy assignment operator may be used in some circumstances.
|
||||
Requires:;; `value_type` is move constructible.
|
||||
|
||||
---
|
||||
@@ -729,7 +767,11 @@ Returns:;; An iterator pointing to the inserted element.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. +
|
||||
+
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
|
||||
---
|
||||
|
||||
@@ -750,7 +792,11 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
|
||||
+
|
||||
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to 10 arguments, with no support for rvalue references or move semantics. +
|
||||
+
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
|
||||
---
|
||||
|
||||
@@ -1373,7 +1419,9 @@ template<class Key, class Hash, class Pred, class Alloc>
|
||||
Return `true` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
|
||||
|
||||
[horizontal]
|
||||
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
|
||||
+
|
||||
Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
|
||||
---
|
||||
|
||||
@@ -1387,7 +1435,9 @@ template<class Key, class Hash, class Pred, class Alloc>
|
||||
Return `false` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
|
||||
|
||||
[horizontal]
|
||||
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
|
||||
+
|
||||
Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
|
||||
---
|
||||
|
||||
@@ -1435,58 +1485,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_multiset``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_multiset to an archive
|
||||
|
||||
Saves all the elements of an `unordered_multiset` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type`
|
||||
is serializable (XML serializable), and it supports Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_multiset from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_multiset` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_multiset` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
|
||||
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_multiset` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_multiset` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -95,10 +95,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_node_map_destructor[~unordered_node_map]();
|
||||
unordered_node_map& xref:#unordered_node_map_copy_assignment[operator++=++](const unordered_node_map& other);
|
||||
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_node_map& xref:#unordered_node_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_node_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -315,7 +314,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -649,9 +649,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_node_map& operator=(unordered_node_map&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -962,13 +961,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
unlike xref:#unordered_node_map_emplace[emplace], which simply forwards all arguments to ``value_type``'s constructor.
|
||||
@@ -1007,13 +1006,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
unlike xref:#unordered_node_map_emplace_hint[emplace_hint], which simply forwards all arguments to ``value_type``'s constructor.
|
||||
@@ -1038,19 +1037,19 @@ template<class K, class M>
|
||||
|
||||
Inserts a new element into the container or updates an existing one by assigning to the contained value.
|
||||
|
||||
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
|
||||
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
|
||||
|
||||
If there is no such element, it is added to the container as:
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
@@ -1076,19 +1075,19 @@ template<class K, class M>
|
||||
|
||||
Inserts a new element into the container or updates an existing one by assigning to the contained value.
|
||||
|
||||
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
|
||||
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
|
||||
|
||||
If there is no such element, it is added to the container as:
|
||||
```c++
|
||||
// first two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<Key>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<Key>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
|
||||
// third overload
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<M>(obj)))
|
||||
std::forward_as_tuple(boost::forward<K>(k)),
|
||||
std::forward_as_tuple(boost::forward<M>(obj)))
|
||||
```
|
||||
|
||||
`hint` is a suggestion to where the element should be inserted. This implementation ignores it.
|
||||
@@ -1407,7 +1406,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
|
||||
Invalidates iterators and changes the order of elements.
|
||||
|
||||
@@ -1546,57 +1545,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_node_map``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_node_map to an archive
|
||||
|
||||
Saves all the elements of an `unordered_node_map` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
|
||||
are serializable (XML serializable), and they do support Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_node_map from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_node_map` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_node_map` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `key_type` and `mapped_type` are constructible from
|
||||
`std::remove_const<key_type>::type&&` and `std::remove_const<mapped_type>::type&&`,
|
||||
respectively.
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_node_map` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_node_map` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -90,10 +90,9 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_node_set_destructor[~unordered_node_set]();
|
||||
unordered_node_set& xref:#unordered_node_set_copy_assignment[operator++=++](const unordered_node_set& other);
|
||||
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_node_set& xref:#unordered_node_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_node_set_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@@ -265,7 +264,8 @@ namespace boost {
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
|
||||
|===
|
||||
|
||||
@@ -602,9 +602,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_node_set& operator=(unordered_node_set&& other)
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@@ -1189,7 +1188,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
|
||||
Invalidates iterators and changes the order of elements.
|
||||
|
||||
@@ -1303,55 +1302,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_node_set``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_node_set to an archive
|
||||
|
||||
Saves all the elements of an `unordered_node_set` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type`
|
||||
is serializable (XML serializable), and it supports Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_node_set from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_node_set` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_node_set` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_node_set` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_node_set` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -24,12 +24,12 @@ namespace boost {
|
||||
using hasher = Hash;
|
||||
using key_equal = Pred;
|
||||
using allocator_type = Allocator;
|
||||
using pointer = typename std::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
|
||||
using pointer = typename boost::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = typename boost::allocator_traits<Allocator>::const_pointer;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = _implementation-defined_;
|
||||
using difference_type = _implementation-defined_;
|
||||
|
||||
using iterator = _implementation-defined_;
|
||||
using const_iterator = _implementation-defined_;
|
||||
@@ -272,7 +272,6 @@ namespace boost {
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@@ -280,15 +279,51 @@ The elements are organized into buckets. Keys with the same hash code are stored
|
||||
|
||||
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
|
||||
|
||||
=== Configuration macros
|
||||
|
||||
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
|
||||
Globally define this macro to support loading of ``unordered_set``s saved to
|
||||
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
|
||||
---
|
||||
|
||||
=== Typedefs
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef typename allocator_type::pointer pointer;
|
||||
----
|
||||
|
||||
`value_type*` if `allocator_type::pointer` is not defined.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef typename allocator_type::const_pointer const_pointer;
|
||||
----
|
||||
|
||||
`boost::pointer_to_other<pointer, value_type>::type` if `allocator_type::const_pointer` is not defined.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ size_type;
|
||||
----
|
||||
|
||||
An unsigned integral type.
|
||||
|
||||
`size_type` can represent any non-negative value of `difference_type`.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ difference_type;
|
||||
----
|
||||
|
||||
A signed integral type.
|
||||
|
||||
Is identical to the difference type of `iterator` and `const_iterator`.
|
||||
|
||||
---
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
typedef _implementation-defined_ iterator;
|
||||
@@ -340,8 +375,7 @@ A const_local_iterator object can be used to iterate through a single bucket.
|
||||
typedef _implementation-defined_ node_type;
|
||||
----
|
||||
|
||||
A class for holding extracted container elements, modelling
|
||||
https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle].
|
||||
See node_handle_set for details.
|
||||
|
||||
---
|
||||
|
||||
@@ -350,20 +384,7 @@ https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle].
|
||||
typedef _implementation-defined_ insert_return_type;
|
||||
----
|
||||
|
||||
A specialization of an internal class template:
|
||||
|
||||
[source,c++,subs=+quotes]
|
||||
----
|
||||
template<class Iterator, class NodeType>
|
||||
struct _insert_return_type_ // name is exposition only
|
||||
{
|
||||
Iterator position;
|
||||
bool inserted;
|
||||
NodeType node;
|
||||
};
|
||||
----
|
||||
|
||||
with `Iterator` = `iterator` and `NodeType` = `node_type`.
|
||||
Structure returned by inserting node_type.
|
||||
|
||||
---
|
||||
|
||||
@@ -443,7 +464,10 @@ The move constructor.
|
||||
|
||||
[horizontal]
|
||||
Notes:;; This is implemented using Boost.Move.
|
||||
Requires:;; `value_type` is move-constructible.
|
||||
Requires:;; `value_type` is move-constructible. +
|
||||
+
|
||||
On compilers without rvalue reference support the emulation does not support moving without calling `boost::move` if `value_type` is not copyable.
|
||||
So, for example, you can't return the container from a function.
|
||||
|
||||
---
|
||||
|
||||
@@ -643,6 +667,7 @@ The move assignment operator.
|
||||
If `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`, the allocator is overwritten, if not the moved elements are created using the existing allocator.
|
||||
|
||||
[horizontal]
|
||||
Notes:;; On compilers without rvalue references, this is emulated using Boost.Move. Note that on some compilers the copy assignment operator may be used in some circumstances.
|
||||
Requires:;; `value_type` is move constructible.
|
||||
|
||||
---
|
||||
@@ -755,7 +780,11 @@ If an insert took place, then the iterator points to the newly inserted element.
|
||||
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
|
||||
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. +
|
||||
+
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
|
||||
---
|
||||
|
||||
@@ -776,7 +805,11 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
|
||||
+
|
||||
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
|
||||
+
|
||||
Pointers and references to elements are never invalidated.
|
||||
Pointers and references to elements are never invalidated. +
|
||||
+
|
||||
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to 10 arguments, with no support for rvalue references or move semantics. +
|
||||
+
|
||||
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
|
||||
|
||||
---
|
||||
|
||||
@@ -1451,7 +1484,9 @@ template<class Key, class Hash, class Pred, class Alloc>
|
||||
Return `true` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
|
||||
|
||||
[horizontal]
|
||||
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
|
||||
+
|
||||
Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
|
||||
---
|
||||
|
||||
@@ -1465,7 +1500,9 @@ template<class Key, class Hash, class Pred, class Alloc>
|
||||
Return `false` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
|
||||
|
||||
[horizontal]
|
||||
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
|
||||
+
|
||||
Behavior is undefined if the two containers don't have equivalent equality predicates.
|
||||
|
||||
---
|
||||
|
||||
@@ -1513,58 +1550,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
=== Serialization
|
||||
|
||||
``unordered_set``s can be archived/retrieved by means of
|
||||
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
|
||||
by this library. Both regular and XML archives are supported.
|
||||
|
||||
==== Saving an unordered_set to an archive
|
||||
|
||||
Saves all the elements of an `unordered_set` `x` to an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type`
|
||||
is serializable (XML serializable), and it supports Boost.Serialization
|
||||
`save_construct_data`/`load_construct_data` protocol (automatically suported by
|
||||
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
|
||||
types).
|
||||
|
||||
---
|
||||
|
||||
==== Loading an unordered_set from an archive
|
||||
|
||||
Deletes all preexisting elements of an `unordered_set` `x` and inserts
|
||||
from an archive (XML archive) `ar` restored copies of the elements of the
|
||||
original `unordered_set` `other` saved to the storage read by `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
|
||||
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
|
||||
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
|
||||
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
|
||||
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
|
||||
|
||||
---
|
||||
|
||||
==== Saving an iterator/const_iterator to an archive
|
||||
|
||||
Saves the positional information of an `iterator` (`const_iterator`) `it`
|
||||
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; The `unordered_set` `x` pointed to by `it` has been previously saved to `ar`,
|
||||
and no modifying operations have been issued on `x` between saving of `x` and
|
||||
saving of `it`.
|
||||
|
||||
---
|
||||
|
||||
==== Loading an iterator/const_iterator from an archive
|
||||
|
||||
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
|
||||
the original `iterator` (`const_iterator`) saved to the storage read by
|
||||
an archive (XML archive) `ar`.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; If `x` is the `unordered_set` `it` points to, no modifying operations
|
||||
have been issued on `x` between loading of `x` and loading of `it`.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#define BOOST_HASH_EXAMPLES_CASE_INSENSITIVE_HEADER
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
namespace hash_examples
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* Fast open-addressing concurrent hashmap.
|
||||
/* Fast open-addressing concurrent hash table.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -13,20 +12,71 @@
|
||||
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
|
||||
#include <boost/unordered/detail/foa/concurrent_table.hpp>
|
||||
#include <boost/unordered/detail/foa/flat_map_types.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_map_fwd.hpp>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/type_traits/type_identity.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
|
||||
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
|
||||
"The provided Callable must be invocable with value_type&");
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \
|
||||
static_assert( \
|
||||
boost::unordered::detail::is_invocable<F, value_type const&>::value, \
|
||||
"The provided Callable must be invocable with value_type const&");
|
||||
|
||||
#if BOOST_CXX_VERSION >= 202002L
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
|
||||
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
|
||||
ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced."); \
|
||||
static_assert( \
|
||||
!std::is_base_of<std::execution::unsequenced_policy, ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced.");
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
|
||||
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
|
||||
ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced.");
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_COMMA ,
|
||||
|
||||
#define BOOST_UNORDERED_LAST_ARG(Arg, Args) \
|
||||
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_COMMA Args> >
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(BOOST_UNORDERED_LAST_ARG(Arg, Args))
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
|
||||
BOOST_UNORDERED_LAST_ARG(Arg, Args))
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
template <class F, class... Args>
|
||||
struct is_invocable
|
||||
: std::is_constructible<std::function<void(Args...)>,
|
||||
std::reference_wrapper<typename std::remove_reference<F>::type> >
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class T, class Hash, class Pred, class Allocator>
|
||||
class concurrent_flat_map
|
||||
{
|
||||
@@ -34,16 +84,10 @@ namespace boost {
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class concurrent_flat_map;
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class unordered_flat_map;
|
||||
|
||||
using type_policy = detail::foa::flat_map_types<Key, T>;
|
||||
|
||||
using table_type =
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
|
||||
|
||||
table_type table_;
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
|
||||
|
||||
template <class K, class V, class H, class KE, class A>
|
||||
bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs,
|
||||
@@ -53,11 +97,6 @@ namespace boost {
|
||||
friend typename concurrent_flat_map<K, V, H, KE, A>::size_type erase_if(
|
||||
concurrent_flat_map<K, V, H, KE, A>& set, Predicate pred);
|
||||
|
||||
template<class Archive, class K, class V, class H, class KE, class A>
|
||||
friend void serialize(
|
||||
Archive& ar, concurrent_flat_map<K, V, H, KE, A>& c,
|
||||
unsigned int version);
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
@@ -65,15 +104,14 @@ namespace boost {
|
||||
using init_type = typename type_policy::init_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
|
||||
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
|
||||
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
|
||||
using hasher = typename boost::type_identity<Hash>::type;
|
||||
using key_equal = typename boost::type_identity<Pred>::type;
|
||||
using allocator_type = typename boost::type_identity<Allocator>::type;
|
||||
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;
|
||||
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
|
||||
|
||||
concurrent_flat_map()
|
||||
: concurrent_flat_map(detail::foa::default_bucket_count)
|
||||
@@ -185,13 +223,6 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
concurrent_flat_map(
|
||||
unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~concurrent_flat_map() = default;
|
||||
|
||||
concurrent_flat_map& operator=(concurrent_flat_map const& rhs)
|
||||
@@ -200,8 +231,10 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& rhs) noexcept(
|
||||
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& rhs)
|
||||
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
Allocator>::type::value)
|
||||
{
|
||||
table_ = std::move(rhs.table_);
|
||||
return *this;
|
||||
@@ -272,33 +305,6 @@ namespace boost {
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
@@ -349,56 +355,6 @@ namespace boost {
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> bool visit_while(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool visit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool cvisit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_while(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
cvisit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.cvisit_while(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Modifiers
|
||||
///
|
||||
|
||||
@@ -455,7 +411,6 @@ namespace boost {
|
||||
BOOST_FORCEINLINE auto insert_or_visit(Ty&& value, F f)
|
||||
-> decltype(table_.insert_or_visit(std::forward<Ty>(value), f))
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.insert_or_visit(std::forward<Ty>(value), f);
|
||||
}
|
||||
|
||||
@@ -510,7 +465,7 @@ namespace boost {
|
||||
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
@@ -756,13 +711,6 @@ namespace boost {
|
||||
return c.table_.erase_if(pred);
|
||||
}
|
||||
|
||||
template<class Archive, class K, class V, class H, class KE, class A>
|
||||
void serialize(
|
||||
Archive& ar, concurrent_flat_map<K, V, H, KE, A>& c, unsigned int)
|
||||
{
|
||||
ar & core::make_nvp("table",c.table_);
|
||||
}
|
||||
|
||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
|
||||
template <class InputIterator,
|
||||
@@ -772,10 +720,10 @@ namespace boost {
|
||||
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
class Allocator = std::allocator<
|
||||
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_map(InputIterator, InputIterator,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
@@ -785,21 +733,21 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class Key, class T,
|
||||
class Hash = boost::hash<std::remove_const_t<Key> >,
|
||||
class Pred = std::equal_to<std::remove_const_t<Key> >,
|
||||
class Hash = boost::hash<boost::remove_const_t<Key> >,
|
||||
class Pred = std::equal_to<boost::remove_const_t<Key> >,
|
||||
class Allocator = std::allocator<std::pair<const Key, T> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_flat_map<std::remove_const_t<Key>, T, Hash, Pred,
|
||||
-> concurrent_flat_map<boost::remove_const_t<Key>, T, Hash, Pred,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_map(InputIterator, InputIterator, std::size_t, Allocator)
|
||||
-> concurrent_flat_map<
|
||||
boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
@@ -809,8 +757,8 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_map(InputIterator, InputIterator, Allocator)
|
||||
-> concurrent_flat_map<
|
||||
boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
@@ -820,9 +768,9 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_map(
|
||||
InputIterator, InputIterator, std::size_t, Hash, Allocator)
|
||||
-> concurrent_flat_map<
|
||||
@@ -832,25 +780,25 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class Key, class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
|
||||
Allocator) -> concurrent_flat_map<std::remove_const_t<Key>, T,
|
||||
boost::hash<std::remove_const_t<Key> >,
|
||||
std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
Allocator) -> concurrent_flat_map<boost::remove_const_t<Key>, T,
|
||||
boost::hash<boost::remove_const_t<Key> >,
|
||||
std::equal_to<boost::remove_const_t<Key> >, Allocator>;
|
||||
|
||||
template <class Key, class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, Allocator)
|
||||
-> concurrent_flat_map<std::remove_const_t<Key>, T,
|
||||
boost::hash<std::remove_const_t<Key> >,
|
||||
std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
-> concurrent_flat_map<boost::remove_const_t<Key>, T,
|
||||
boost::hash<boost::remove_const_t<Key> >,
|
||||
std::equal_to<boost::remove_const_t<Key> >, Allocator>;
|
||||
|
||||
template <class Key, class T, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
|
||||
Hash, Allocator) -> concurrent_flat_map<std::remove_const_t<Key>, T,
|
||||
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
Hash, Allocator) -> concurrent_flat_map<boost::remove_const_t<Key>, T,
|
||||
Hash, std::equal_to<boost::remove_const_t<Key> >, Allocator>;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -859,4 +807,12 @@ namespace boost {
|
||||
using unordered::concurrent_flat_map;
|
||||
} // namespace boost
|
||||
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY
|
||||
#undef BOOST_UNORDERED_COMMA
|
||||
#undef BOOST_UNORDERED_LAST_ARG
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Fast open-addressing concurrent hashmap.
|
||||
/* Fast open-addressing concurrent hash table.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
|
||||
@@ -1,718 +0,0 @@
|
||||
/* Fast open-addressing concurrent hashset.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
|
||||
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
|
||||
#include <boost/unordered/detail/foa/concurrent_table.hpp>
|
||||
#include <boost/unordered/detail/foa/flat_set_types.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_set_fwd.hpp>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
template <class Key, class Hash, class Pred, class Allocator>
|
||||
class concurrent_flat_set
|
||||
{
|
||||
private:
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class concurrent_flat_set;
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class unordered_flat_set;
|
||||
|
||||
using type_policy = detail::foa::flat_set_types<Key>;
|
||||
|
||||
using table_type =
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
|
||||
|
||||
table_type table_;
|
||||
|
||||
template <class K, class H, class KE, class A>
|
||||
bool friend operator==(concurrent_flat_set<K, H, KE, A> const& lhs,
|
||||
concurrent_flat_set<K, H, KE, A> const& rhs);
|
||||
|
||||
template <class K, class H, class KE, class A, class Predicate>
|
||||
friend typename concurrent_flat_set<K, H, KE, A>::size_type erase_if(
|
||||
concurrent_flat_set<K, H, KE, A>& set, Predicate pred);
|
||||
|
||||
template<class Archive, class K, class H, class KE, class A>
|
||||
friend void serialize(
|
||||
Archive& ar, concurrent_flat_set<K, H, KE, A>& c,
|
||||
unsigned int version);
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using value_type = typename type_policy::value_type;
|
||||
using init_type = typename type_policy::init_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
|
||||
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
|
||||
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
|
||||
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;
|
||||
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
|
||||
|
||||
concurrent_flat_set()
|
||||
: concurrent_flat_set(detail::foa::default_bucket_count)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_flat_set(size_type n, const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_flat_set(InputIterator f, InputIterator l,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
this->insert(f, l);
|
||||
}
|
||||
|
||||
concurrent_flat_set(concurrent_flat_set const& rhs)
|
||||
: table_(rhs.table_,
|
||||
boost::allocator_select_on_container_copy_construction(
|
||||
rhs.get_allocator()))
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(concurrent_flat_set&& rhs)
|
||||
: table_(std::move(rhs.table_))
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_flat_set(
|
||||
InputIterator f, InputIterator l, allocator_type const& a)
|
||||
: concurrent_flat_set(f, l, 0, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_flat_set(allocator_type const& a)
|
||||
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(
|
||||
concurrent_flat_set const& rhs, allocator_type const& a)
|
||||
: table_(rhs.table_, a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(concurrent_flat_set&& rhs, allocator_type const& a)
|
||||
: table_(std::move(rhs.table_), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(std::initializer_list<value_type> il,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: concurrent_flat_set(n, hf, eql, a)
|
||||
{
|
||||
this->insert(il.begin(), il.end());
|
||||
}
|
||||
|
||||
concurrent_flat_set(size_type n, const allocator_type& a)
|
||||
: concurrent_flat_set(n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(
|
||||
size_type n, const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_set(n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_flat_set(
|
||||
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
|
||||
: concurrent_flat_set(f, l, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_flat_set(InputIterator f, InputIterator l, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_set(f, l, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(
|
||||
std::initializer_list<value_type> il, const allocator_type& a)
|
||||
: concurrent_flat_set(
|
||||
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(std::initializer_list<value_type> il, size_type n,
|
||||
const allocator_type& a)
|
||||
: concurrent_flat_set(il, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_flat_set(std::initializer_list<value_type> il, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_flat_set(il, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
concurrent_flat_set(
|
||||
unordered_flat_set<Key, Hash, Pred, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~concurrent_flat_set() = default;
|
||||
|
||||
concurrent_flat_set& operator=(concurrent_flat_set const& rhs)
|
||||
{
|
||||
table_ = rhs.table_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_flat_set& operator=(concurrent_flat_set&& rhs)
|
||||
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
Allocator>::type::value)
|
||||
{
|
||||
table_ = std::move(rhs.table_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_flat_set& operator=(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
table_ = ilist;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Capacity
|
||||
///
|
||||
|
||||
size_type size() const noexcept { return table_.size(); }
|
||||
size_type max_size() const noexcept { return table_.max_size(); }
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
visit(K&& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
cvisit(K&& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> size_type cvisit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_all(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
visit_all(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
cvisit_all(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.cvisit_all(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> bool visit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool cvisit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_while(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
cvisit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.cvisit_while(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Modifiers
|
||||
///
|
||||
|
||||
BOOST_FORCEINLINE bool insert(value_type const& obj)
|
||||
{
|
||||
return table_.insert(obj);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool insert(value_type&& obj)
|
||||
{
|
||||
return table_.insert(std::move(obj));
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert(K&& k)
|
||||
{
|
||||
return table_.try_emplace(std::forward<K>(k));
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator begin, InputIterator end)
|
||||
{
|
||||
for (auto pos = begin; pos != end; ++pos) {
|
||||
table_.emplace(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
this->insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert_or_visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_visit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(value_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert_or_cvisit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type erase(key_type const& k)
|
||||
{
|
||||
return table_.erase(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
erase(K&& k)
|
||||
{
|
||||
return table_.erase(std::forward<K>(k));
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
|
||||
{
|
||||
return table_.erase_if(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value &&
|
||||
!detail::is_execution_policy<K>::value,
|
||||
size_type>::type
|
||||
erase_if(K&& k, F f)
|
||||
{
|
||||
return table_.erase_if(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
erase_if(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.erase_if(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
|
||||
|
||||
void swap(concurrent_flat_set& other) noexcept(
|
||||
boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
|
||||
{
|
||||
return table_.swap(other.table_);
|
||||
}
|
||||
|
||||
void clear() noexcept { table_.clear(); }
|
||||
|
||||
template <typename H2, typename P2>
|
||||
size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>& x)
|
||||
{
|
||||
BOOST_ASSERT(get_allocator() == x.get_allocator());
|
||||
return table_.merge(x.table_);
|
||||
}
|
||||
|
||||
template <typename H2, typename P2>
|
||||
size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>&& x)
|
||||
{
|
||||
return merge(x);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type count(key_type const& k) const
|
||||
{
|
||||
return table_.count(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
count(K const& k)
|
||||
{
|
||||
return table_.count(k);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool contains(key_type const& k) const
|
||||
{
|
||||
return table_.contains(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
|
||||
contains(K const& k) const
|
||||
{
|
||||
return table_.contains(k);
|
||||
}
|
||||
|
||||
/// Hash Policy
|
||||
///
|
||||
size_type bucket_count() const noexcept { return table_.capacity(); }
|
||||
|
||||
float load_factor() const noexcept { return table_.load_factor(); }
|
||||
float max_load_factor() const noexcept
|
||||
{
|
||||
return table_.max_load_factor();
|
||||
}
|
||||
void max_load_factor(float) {}
|
||||
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); }
|
||||
|
||||
/// Observers
|
||||
///
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
}
|
||||
|
||||
hasher hash_function() const { return table_.hash_function(); }
|
||||
key_equal key_eq() const { return table_.key_eq(); }
|
||||
};
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return lhs.table_ == rhs.table_;
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
|
||||
concurrent_flat_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)))
|
||||
{
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename concurrent_flat_set<K, H, P, A>::size_type erase_if(
|
||||
concurrent_flat_set<K, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return c.table_.erase_if(pred);
|
||||
}
|
||||
|
||||
template<class Archive, class K, class H, class KE, class A>
|
||||
void serialize(
|
||||
Archive& ar, concurrent_flat_set<K, H, KE, A>& c, unsigned int)
|
||||
{
|
||||
ar & core::make_nvp("table",c.table_);
|
||||
}
|
||||
|
||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
|
||||
template <class InputIterator,
|
||||
class Hash =
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class Pred =
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class Allocator = std::allocator<
|
||||
typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(InputIterator, InputIterator,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Hash = boost::hash<T>,
|
||||
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(std::initializer_list<T>,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_flat_set< T, Hash, Pred, Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(InputIterator, InputIterator, std::size_t, Allocator)
|
||||
-> concurrent_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(InputIterator, InputIterator, Allocator)
|
||||
-> concurrent_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(
|
||||
InputIterator, InputIterator, std::size_t, Hash, Allocator)
|
||||
-> concurrent_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type, Hash,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(std::initializer_list<T>, std::size_t, Allocator)
|
||||
-> concurrent_flat_set<T, boost::hash<T>,std::equal_to<T>, Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(std::initializer_list<T >, Allocator)
|
||||
-> concurrent_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
|
||||
|
||||
template <class T, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_flat_set(std::initializer_list<T >, std::size_t,Hash, Allocator)
|
||||
-> concurrent_flat_set<T, Hash, std::equal_to<T>, Allocator>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using unordered::concurrent_flat_set;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
|
||||
@@ -1,55 +0,0 @@
|
||||
/* Fast open-addressing concurrent hashset.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
|
||||
template <class Key, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key> >
|
||||
class concurrent_flat_set;
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
|
||||
concurrent_flat_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename concurrent_flat_set<K, H, P, A>::size_type erase_if(
|
||||
concurrent_flat_set<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::concurrent_flat_set;
|
||||
using boost::unordered::swap;
|
||||
using boost::unordered::operator==;
|
||||
using boost::unordered::operator!=;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
|
||||
@@ -1,71 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_ARCHIVE_CONSTRUCTED_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_ARCHIVE_CONSTRUCTED_HPP
|
||||
|
||||
#include <boost/unordered/detail/opt_storage.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
/* constructs a stack-based object from a serialization archive */
|
||||
|
||||
template<typename T>
|
||||
struct archive_constructed:private noncopyable
|
||||
{
|
||||
template<class Archive>
|
||||
archive_constructed(const char* name,Archive& ar,unsigned int version)
|
||||
{
|
||||
core::load_construct_data_adl(ar,std::addressof(get()),version);
|
||||
BOOST_TRY{
|
||||
ar>>core::make_nvp(name,get());
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
get().~T();
|
||||
BOOST_RETHROW;
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
|
||||
~archive_constructed()
|
||||
{
|
||||
get().~T();
|
||||
}
|
||||
|
||||
#if defined(BOOST_GCC)&&(BOOST_GCC>=4*10000+6*100)
|
||||
#define BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
|
||||
T& get(){return *space.address();}
|
||||
|
||||
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
|
||||
#pragma GCC diagnostic pop
|
||||
#undef BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
|
||||
#endif
|
||||
|
||||
private:
|
||||
opt_storage<T> space;
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_BAD_ARCHIVE_EXCEPTION_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_BAD_ARCHIVE_EXCEPTION_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
struct bad_archive_exception:std::runtime_error
|
||||
{
|
||||
bad_archive_exception():std::runtime_error("Invalid or corrupted archive"){}
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,105 +0,0 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
|
||||
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
|
||||
"The provided Callable must be invocable with value_type&");
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \
|
||||
static_assert( \
|
||||
boost::unordered::detail::is_invocable<F, value_type const&>::value, \
|
||||
"The provided Callable must be invocable with value_type const&");
|
||||
|
||||
#if BOOST_CXX_VERSION >= 202002L
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
|
||||
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
|
||||
ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced."); \
|
||||
static_assert( \
|
||||
!std::is_base_of<std::execution::unsequenced_policy, ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced.");
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
|
||||
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
|
||||
ExecPolicy>::value, \
|
||||
"ExecPolicy must be sequenced.");
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_DETAIL_COMMA ,
|
||||
|
||||
#define BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args) \
|
||||
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_DETAIL_COMMA Args> >
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE( \
|
||||
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
|
||||
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
template <class F, class... Args>
|
||||
struct is_invocable
|
||||
: std::is_constructible<std::function<void(Args...)>,
|
||||
std::reference_wrapper<typename std::remove_reference<F>::type> >
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#if defined(BOOST_NO_CXX20_HDR_CONCEPTS)
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
|
||||
static_assert( \
|
||||
std::is_base_of< \
|
||||
std::forward_iterator_tag, \
|
||||
typename std::iterator_traits<Iterator>::iterator_category>::value, \
|
||||
"The provided iterator must be at least forward");
|
||||
#else
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
|
||||
static_assert(std::forward_iterator<Iterator>, \
|
||||
"The provided iterator must be at least forward");
|
||||
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_KEY_COMPATIBLE_ITERATOR(Iterator) \
|
||||
static_assert( \
|
||||
std::is_same< \
|
||||
typename std::iterator_traits<Iterator>::value_type, \
|
||||
key_type>::value || \
|
||||
detail::are_transparent< \
|
||||
typename std::iterator_traits<Iterator>::value_type, \
|
||||
hasher, key_equal>::value, \
|
||||
"The provided iterator must dereference to a compatible key value");
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(Iterator) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_KEY_COMPATIBLE_ITERATOR(Iterator)
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
@@ -114,17 +114,18 @@ to normal separate chaining implementations.
|
||||
*/
|
||||
|
||||
#include <boost/unordered/detail/prime_fmod.hpp>
|
||||
#include <boost/unordered/detail/serialize_tracked_address.hpp>
|
||||
#include <boost/unordered/detail/opt_storage.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/addressof.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/bit.hpp>
|
||||
#include <boost/core/empty_value.hpp>
|
||||
#include <boost/core/invoke_swap.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/move/core.hpp>
|
||||
#include <boost/move/utility_core.hpp>
|
||||
#include <boost/swap.hpp>
|
||||
#include <boost/type_traits/aligned_storage.hpp>
|
||||
#include <boost/type_traits/alignment_of.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
@@ -141,18 +142,19 @@ namespace boost {
|
||||
node>::type node_pointer;
|
||||
|
||||
node_pointer next;
|
||||
opt_storage<value_type> buf;
|
||||
typename boost::aligned_storage<sizeof(value_type),
|
||||
boost::alignment_of<value_type>::value>::type buf;
|
||||
|
||||
node() noexcept : next(), buf() {}
|
||||
node() BOOST_NOEXCEPT : next(), buf() {}
|
||||
|
||||
value_type* value_ptr() noexcept
|
||||
value_type* value_ptr() BOOST_NOEXCEPT
|
||||
{
|
||||
return buf.address();
|
||||
return reinterpret_cast<value_type*>(buf.address());
|
||||
}
|
||||
|
||||
value_type& value() noexcept
|
||||
value_type& value() BOOST_NOEXCEPT
|
||||
{
|
||||
return *buf.address();
|
||||
return *reinterpret_cast<value_type*>(buf.address());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,7 +168,7 @@ namespace boost {
|
||||
|
||||
node_pointer next;
|
||||
|
||||
bucket() noexcept : next() {}
|
||||
bucket() BOOST_NOEXCEPT : next() {}
|
||||
};
|
||||
|
||||
template <class Bucket> struct bucket_group
|
||||
@@ -182,7 +184,7 @@ namespace boost {
|
||||
std::size_t bitmask;
|
||||
bucket_group_pointer next, prev;
|
||||
|
||||
bucket_group() noexcept : buckets(), bitmask(0), next(), prev() {}
|
||||
bucket_group() BOOST_NOEXCEPT : buckets(), bitmask(0), next(), prev() {}
|
||||
~bucket_group() {}
|
||||
};
|
||||
|
||||
@@ -220,28 +222,33 @@ namespace boost {
|
||||
public:
|
||||
grouped_bucket_iterator() : p(), pbg() {}
|
||||
|
||||
reference operator*() const noexcept { return dereference(); }
|
||||
pointer operator->() const noexcept { return boost::to_address(p); }
|
||||
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
|
||||
pointer operator->() const BOOST_NOEXCEPT
|
||||
{
|
||||
return boost::to_address(p);
|
||||
}
|
||||
|
||||
grouped_bucket_iterator& operator++() noexcept
|
||||
grouped_bucket_iterator& operator++() BOOST_NOEXCEPT
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
grouped_bucket_iterator operator++(int) noexcept
|
||||
grouped_bucket_iterator operator++(int) BOOST_NOEXCEPT
|
||||
{
|
||||
grouped_bucket_iterator old = *this;
|
||||
increment();
|
||||
return old;
|
||||
}
|
||||
|
||||
bool operator==(grouped_bucket_iterator const& other) const noexcept
|
||||
bool operator==(
|
||||
grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return equal(other);
|
||||
}
|
||||
|
||||
bool operator!=(grouped_bucket_iterator const& other) const noexcept
|
||||
bool operator!=(
|
||||
grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return !equal(other);
|
||||
}
|
||||
@@ -257,14 +264,14 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
Bucket& dereference() const noexcept { return *p; }
|
||||
Bucket& dereference() const BOOST_NOEXCEPT { return *p; }
|
||||
|
||||
bool equal(const grouped_bucket_iterator& x) const noexcept
|
||||
bool equal(const grouped_bucket_iterator& x) const BOOST_NOEXCEPT
|
||||
{
|
||||
return p == x.p;
|
||||
}
|
||||
|
||||
void increment() noexcept
|
||||
void increment() BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t const offset = static_cast<std::size_t>(p - pbg->buckets);
|
||||
|
||||
@@ -280,24 +287,6 @@ namespace boost {
|
||||
p = pbg->buckets + x;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
friend void serialization_track(
|
||||
Archive& ar, grouped_bucket_iterator const& x)
|
||||
{
|
||||
// requires: not at end() position
|
||||
track_address(ar, x.p);
|
||||
track_address(ar, x.pbg);
|
||||
}
|
||||
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template <typename Archive> void serialize(Archive& ar, unsigned int)
|
||||
{
|
||||
// requires: not at end() position
|
||||
serialize_tracked_address(ar, p);
|
||||
serialize_tracked_address(ar, pbg);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Node> struct const_grouped_local_bucket_iterator;
|
||||
@@ -316,17 +305,20 @@ namespace boost {
|
||||
|
||||
grouped_local_bucket_iterator() : p() {}
|
||||
|
||||
reference operator*() const noexcept { return dereference(); }
|
||||
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
|
||||
|
||||
pointer operator->() const noexcept { return boost::to_address(p); }
|
||||
pointer operator->() const BOOST_NOEXCEPT
|
||||
{
|
||||
return boost::to_address(p);
|
||||
}
|
||||
|
||||
grouped_local_bucket_iterator& operator++() noexcept
|
||||
grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
grouped_local_bucket_iterator operator++(int) noexcept
|
||||
grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
|
||||
{
|
||||
grouped_local_bucket_iterator old = *this;
|
||||
increment();
|
||||
@@ -334,13 +326,13 @@ namespace boost {
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
grouped_local_bucket_iterator const& other) const noexcept
|
||||
grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return equal(other);
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
grouped_local_bucket_iterator const& other) const noexcept
|
||||
grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return !equal(other);
|
||||
}
|
||||
@@ -353,14 +345,14 @@ namespace boost {
|
||||
|
||||
grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
|
||||
|
||||
value_type& dereference() const noexcept { return p->value(); }
|
||||
value_type& dereference() const BOOST_NOEXCEPT { return p->value(); }
|
||||
|
||||
bool equal(const grouped_local_bucket_iterator& x) const noexcept
|
||||
bool equal(const grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
|
||||
{
|
||||
return p == x.p;
|
||||
}
|
||||
|
||||
void increment() noexcept { p = p->next; }
|
||||
void increment() BOOST_NOEXCEPT { p = p->next; }
|
||||
|
||||
node_pointer p;
|
||||
};
|
||||
@@ -384,17 +376,20 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
reference operator*() const noexcept { return dereference(); }
|
||||
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
|
||||
|
||||
pointer operator->() const noexcept { return boost::to_address(p); }
|
||||
pointer operator->() const BOOST_NOEXCEPT
|
||||
{
|
||||
return boost::to_address(p);
|
||||
}
|
||||
|
||||
const_grouped_local_bucket_iterator& operator++() noexcept
|
||||
const_grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_grouped_local_bucket_iterator operator++(int) noexcept
|
||||
const_grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
|
||||
{
|
||||
const_grouped_local_bucket_iterator old = *this;
|
||||
increment();
|
||||
@@ -402,13 +397,13 @@ namespace boost {
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const_grouped_local_bucket_iterator const& other) const noexcept
|
||||
const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return equal(other);
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const_grouped_local_bucket_iterator const& other) const noexcept
|
||||
const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return !equal(other);
|
||||
}
|
||||
@@ -419,22 +414,23 @@ namespace boost {
|
||||
|
||||
const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
|
||||
|
||||
value_type& dereference() const noexcept { return p->value(); }
|
||||
value_type& dereference() const BOOST_NOEXCEPT { return p->value(); }
|
||||
|
||||
bool equal(const const_grouped_local_bucket_iterator& x) const noexcept
|
||||
bool equal(
|
||||
const const_grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
|
||||
{
|
||||
return p == x.p;
|
||||
}
|
||||
|
||||
void increment() noexcept { p = p->next; }
|
||||
void increment() BOOST_NOEXCEPT { p = p->next; }
|
||||
|
||||
node_pointer p;
|
||||
};
|
||||
|
||||
template <class T> struct span
|
||||
{
|
||||
T* begin() const noexcept { return data; }
|
||||
T* end() const noexcept { return data + size; }
|
||||
T* begin() const BOOST_NOEXCEPT { return data; }
|
||||
T* end() const BOOST_NOEXCEPT { return data + size; }
|
||||
|
||||
T* data;
|
||||
std::size_t size;
|
||||
@@ -449,6 +445,8 @@ namespace boost {
|
||||
typename boost::allocator_void_pointer<Allocator>::type> >::
|
||||
type>
|
||||
{
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(grouped_bucket_array)
|
||||
|
||||
typedef typename boost::allocator_value_type<Allocator>::type
|
||||
allocator_value_type;
|
||||
typedef
|
||||
@@ -513,7 +511,8 @@ namespace boost {
|
||||
|
||||
grouped_bucket_array(size_type n, const Allocator& al)
|
||||
: empty_value<node_allocator_type>(empty_init_t(), al),
|
||||
size_index_(0), size_(0), buckets(), groups()
|
||||
size_index_(0),
|
||||
size_(0), buckets(), groups()
|
||||
{
|
||||
if (n == 0) {
|
||||
return;
|
||||
@@ -562,10 +561,8 @@ namespace boost {
|
||||
|
||||
~grouped_bucket_array() { this->deallocate(); }
|
||||
|
||||
grouped_bucket_array(grouped_bucket_array const&) = delete;
|
||||
grouped_bucket_array& operator=(grouped_bucket_array const&) = delete;
|
||||
|
||||
grouped_bucket_array(grouped_bucket_array&& other) noexcept
|
||||
grouped_bucket_array(
|
||||
BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
|
||||
: empty_value<node_allocator_type>(
|
||||
empty_init_t(), other.get_node_allocator()),
|
||||
size_index_(other.size_index_),
|
||||
@@ -579,12 +576,13 @@ namespace boost {
|
||||
other.groups = group_pointer();
|
||||
}
|
||||
|
||||
grouped_bucket_array& operator=(grouped_bucket_array&& other) noexcept
|
||||
grouped_bucket_array& operator=(
|
||||
BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
this->get_node_allocator() == other.get_node_allocator());
|
||||
|
||||
if (this == std::addressof(other)) {
|
||||
if (this == boost::addressof(other)) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -603,48 +601,25 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter (dtor calls)
|
||||
#endif
|
||||
|
||||
void deallocate() noexcept
|
||||
void deallocate() BOOST_NOEXCEPT
|
||||
{
|
||||
if (buckets) {
|
||||
size_type const num_buckets = buckets_len();
|
||||
bucket_type* pb = boost::to_address(buckets);
|
||||
(void)pb; // VS complains when dtor is trivial
|
||||
|
||||
for (size_type i = 0; i < num_buckets; ++i) {
|
||||
(pb + i)->~bucket_type();
|
||||
}
|
||||
|
||||
bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
|
||||
boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
|
||||
boost::allocator_deallocate(
|
||||
bucket_alloc, buckets, this->buckets_len());
|
||||
|
||||
buckets = bucket_pointer();
|
||||
}
|
||||
|
||||
if (groups) {
|
||||
size_type const num_groups = groups_len();
|
||||
group* pg = boost::to_address(groups);
|
||||
(void)pg; // VS complains when dtor is trivial
|
||||
|
||||
for (size_type i = 0; i < num_groups; ++i) {
|
||||
(pg + i)->~group();
|
||||
}
|
||||
|
||||
group_allocator_type group_alloc = this->get_group_allocator();
|
||||
boost::allocator_deallocate(group_alloc, groups, num_groups);
|
||||
boost::allocator_deallocate(
|
||||
group_alloc, groups, this->groups_len());
|
||||
|
||||
groups = group_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
void swap(grouped_bucket_array& other)
|
||||
{
|
||||
std::swap(size_index_, other.size_index_);
|
||||
@@ -655,8 +630,7 @@ namespace boost {
|
||||
bool b = boost::allocator_propagate_on_container_swap<
|
||||
allocator_type>::type::value;
|
||||
if (b) {
|
||||
boost::core::invoke_swap(
|
||||
get_node_allocator(), other.get_node_allocator());
|
||||
boost::swap(get_node_allocator(), other.get_node_allocator());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,9 +654,12 @@ namespace boost {
|
||||
return this->get_node_allocator();
|
||||
}
|
||||
|
||||
size_type buckets_len() const noexcept { return size_ + 1; }
|
||||
size_type buckets_len() const BOOST_NOEXCEPT { return size_ + 1; }
|
||||
|
||||
size_type groups_len() const noexcept { return size_ / group::N + 1; }
|
||||
size_type groups_len() const BOOST_NOEXCEPT
|
||||
{
|
||||
return size_ / group::N + 1;
|
||||
}
|
||||
|
||||
void reset_allocator(Allocator const& allocator_)
|
||||
{
|
||||
@@ -715,7 +692,7 @@ namespace boost {
|
||||
|
||||
local_iterator end(size_type) const { return local_iterator(); }
|
||||
|
||||
size_type capacity() const noexcept { return size_; }
|
||||
size_type capacity() const BOOST_NOEXCEPT { return size_; }
|
||||
|
||||
iterator at(size_type n) const
|
||||
{
|
||||
@@ -749,7 +726,7 @@ namespace boost {
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
void append_bucket_group(iterator itb) noexcept
|
||||
void append_bucket_group(iterator itb) BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t const N = group::N;
|
||||
|
||||
@@ -779,7 +756,7 @@ namespace boost {
|
||||
}
|
||||
}
|
||||
|
||||
void insert_node(iterator itb, node_pointer p) noexcept
|
||||
void insert_node(iterator itb, node_pointer p) BOOST_NOEXCEPT
|
||||
{
|
||||
this->append_bucket_group(itb);
|
||||
|
||||
@@ -788,7 +765,7 @@ namespace boost {
|
||||
}
|
||||
|
||||
void insert_node_hint(
|
||||
iterator itb, node_pointer p, node_pointer hint) noexcept
|
||||
iterator itb, node_pointer p, node_pointer hint) BOOST_NOEXCEPT
|
||||
{
|
||||
this->append_bucket_group(itb);
|
||||
|
||||
@@ -801,24 +778,24 @@ namespace boost {
|
||||
}
|
||||
}
|
||||
|
||||
void extract_node(iterator itb, node_pointer p) noexcept
|
||||
void extract_node(iterator itb, node_pointer p) BOOST_NOEXCEPT
|
||||
{
|
||||
node_pointer* pp = std::addressof(itb->next);
|
||||
node_pointer* pp = boost::addressof(itb->next);
|
||||
while ((*pp) != p)
|
||||
pp = std::addressof((*pp)->next);
|
||||
pp = boost::addressof((*pp)->next);
|
||||
*pp = p->next;
|
||||
if (!itb->next)
|
||||
unlink_bucket(itb);
|
||||
}
|
||||
|
||||
void extract_node_after(iterator itb, node_pointer* pp) noexcept
|
||||
void extract_node_after(iterator itb, node_pointer* pp) BOOST_NOEXCEPT
|
||||
{
|
||||
*pp = (*pp)->next;
|
||||
if (!itb->next)
|
||||
unlink_bucket(itb);
|
||||
}
|
||||
|
||||
void unlink_empty_buckets() noexcept
|
||||
void unlink_empty_buckets() BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t const N = group::N;
|
||||
|
||||
@@ -866,7 +843,7 @@ namespace boost {
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FCA_HPP
|
||||
|
||||
@@ -16,22 +16,18 @@
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/unordered/detail/archive_constructed.hpp>
|
||||
#include <boost/unordered/detail/bad_archive_exception.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/unordered/detail/foa/core.hpp>
|
||||
#include <boost/unordered/detail/foa/reentrancy_check.hpp>
|
||||
#include <boost/unordered/detail/foa/rw_spinlock.hpp>
|
||||
#include <boost/unordered/detail/foa/takes_arg_as_const_reference.hpp>
|
||||
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
|
||||
#include <boost/unordered/detail/serialization_version.hpp>
|
||||
#include <boost/unordered/detail/static_assert.hpp>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
@@ -89,7 +85,7 @@ private:
|
||||
static constexpr std::size_t element_offset=
|
||||
(sizeof(T)+cacheline_size-1)/cacheline_size*cacheline_size;
|
||||
|
||||
BOOST_UNORDERED_STATIC_ASSERT(alignof(T)<=cacheline_size);
|
||||
BOOST_STATIC_ASSERT(alignof(T)<=cacheline_size);
|
||||
|
||||
T* data(std::size_t pos)noexcept
|
||||
{
|
||||
@@ -127,7 +123,7 @@ template<typename Mutex>
|
||||
class shared_lock
|
||||
{
|
||||
public:
|
||||
shared_lock(Mutex& m_)noexcept:m(m_){m.lock_shared();}
|
||||
shared_lock(Mutex& m_)noexcept:m{m_}{m.lock_shared();}
|
||||
~shared_lock()noexcept{if(owns)m.unlock_shared();}
|
||||
|
||||
/* not used but VS in pre-C++17 mode needs to see it for RVO */
|
||||
@@ -149,7 +145,7 @@ template<typename Mutex>
|
||||
class lock_guard
|
||||
{
|
||||
public:
|
||||
lock_guard(Mutex& m_)noexcept:m(m_){m.lock();}
|
||||
lock_guard(Mutex& m_)noexcept:m{m_}{m.lock();}
|
||||
~lock_guard()noexcept{m.unlock();}
|
||||
|
||||
/* not used but VS in pre-C++17 mode needs to see it for RVO */
|
||||
@@ -248,105 +244,73 @@ group_access* dummy_group_accesses()
|
||||
|
||||
/* subclasses table_arrays to add an additional group_access array */
|
||||
|
||||
template<typename Value,typename Group,typename SizePolicy,typename Allocator>
|
||||
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy,Allocator>
|
||||
template<typename Value,typename Group,typename SizePolicy>
|
||||
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy>
|
||||
{
|
||||
using group_access_allocator_type=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using group_access_pointer=
|
||||
typename boost::allocator_pointer<group_access_allocator_type>::type;
|
||||
using super=table_arrays<Value,Group,SizePolicy>;
|
||||
|
||||
using super=table_arrays<Value,Group,SizePolicy,Allocator>;
|
||||
concurrent_table_arrays(const super& arrays,group_access *pga):
|
||||
super{arrays},group_accesses{pga}{}
|
||||
|
||||
concurrent_table_arrays(const super& arrays,group_access_pointer pga):
|
||||
super{arrays},group_accesses_{pga}{}
|
||||
|
||||
group_access* group_accesses()const noexcept{
|
||||
return boost::to_address(group_accesses_);
|
||||
}
|
||||
|
||||
static concurrent_table_arrays new_(
|
||||
group_access_allocator_type al,std::size_t n)
|
||||
template<typename Allocator>
|
||||
static concurrent_table_arrays new_(Allocator& al,std::size_t n)
|
||||
{
|
||||
super x{super::new_(al,n)};
|
||||
BOOST_TRY{
|
||||
return new_group_access(al,x);
|
||||
concurrent_table_arrays arrays{super::new_(al,n),nullptr};
|
||||
if(!arrays.elements){
|
||||
arrays.group_accesses=dummy_group_accesses<SizePolicy::min_size()>();
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
super::delete_(al,x);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
else{
|
||||
using access_alloc=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using access_traits=boost::allocator_traits<access_alloc>;
|
||||
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,concurrent_table_arrays& arrays)
|
||||
{
|
||||
set_group_access(
|
||||
al,arrays,std::is_same<group_access*,group_access_pointer>{});
|
||||
}
|
||||
BOOST_TRY{
|
||||
auto aal=access_alloc(al);
|
||||
arrays.group_accesses=boost::to_address(
|
||||
access_traits::allocate(aal,arrays.groups_size_mask+1));
|
||||
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,
|
||||
concurrent_table_arrays& arrays,
|
||||
std::false_type /* fancy pointers */)
|
||||
{
|
||||
arrays.group_accesses_=
|
||||
boost::allocator_allocate(al,arrays.groups_size_mask+1);
|
||||
|
||||
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
|
||||
::new (arrays.group_accesses()+i) group_access();
|
||||
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
|
||||
::new (arrays.group_accesses+i) group_access();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,
|
||||
concurrent_table_arrays& arrays,
|
||||
std::true_type /* optimize when elements() is null */)
|
||||
{
|
||||
if(!arrays.elements()){
|
||||
arrays.group_accesses_=
|
||||
dummy_group_accesses<SizePolicy::min_size()>();
|
||||
} else {
|
||||
set_group_access(al,arrays,std::false_type{});
|
||||
BOOST_CATCH(...){
|
||||
super::delete_(al,arrays);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
}
|
||||
|
||||
static concurrent_table_arrays new_group_access(
|
||||
group_access_allocator_type al,const super& x)
|
||||
{
|
||||
concurrent_table_arrays arrays{x,nullptr};
|
||||
set_group_access(al,arrays);
|
||||
return arrays;
|
||||
}
|
||||
|
||||
static void delete_(
|
||||
group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
|
||||
template<typename Allocator>
|
||||
static void delete_(Allocator& al,concurrent_table_arrays& arrays)noexcept
|
||||
{
|
||||
delete_group_access(al,arrays);
|
||||
if(arrays.elements){
|
||||
using access_alloc=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using access_traits=boost::allocator_traits<access_alloc>;
|
||||
using pointer=typename access_traits::pointer;
|
||||
using pointer_traits=boost::pointer_traits<pointer>;
|
||||
|
||||
auto aal=access_alloc(al);
|
||||
access_traits::deallocate(
|
||||
aal,pointer_traits::pointer_to(*arrays.group_accesses),
|
||||
arrays.groups_size_mask+1);
|
||||
}
|
||||
super::delete_(al,arrays);
|
||||
}
|
||||
|
||||
static void delete_group_access(
|
||||
group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
|
||||
{
|
||||
if(arrays.elements()){
|
||||
boost::allocator_deallocate(
|
||||
al,arrays.group_accesses_,arrays.groups_size_mask+1);
|
||||
}
|
||||
}
|
||||
|
||||
group_access_pointer group_accesses_;
|
||||
group_access *group_accesses;
|
||||
};
|
||||
|
||||
struct atomic_size_control
|
||||
{
|
||||
static constexpr auto atomic_size_t_size=sizeof(std::atomic<std::size_t>);
|
||||
BOOST_UNORDERED_STATIC_ASSERT(atomic_size_t_size<cacheline_size);
|
||||
BOOST_STATIC_ASSERT(atomic_size_t_size<cacheline_size);
|
||||
|
||||
atomic_size_control(std::size_t ml_,std::size_t size_):
|
||||
pad0_{},ml{ml_},pad1_{},size{size_}{}
|
||||
atomic_size_control(const atomic_size_control& x):
|
||||
atomic_size_control(atomic_size_control& x):
|
||||
pad0_{},ml{x.ml.load()},pad1_{},size{x.size.load()}{}
|
||||
|
||||
/* padding to avoid false sharing internally and with sorrounding data */
|
||||
@@ -374,7 +338,9 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
|
||||
}
|
||||
|
||||
/* foa::concurrent_table serves as the foundation for end-user concurrent
|
||||
* hash containers.
|
||||
* hash containers. The TypePolicy parameter can specify flat/node-based
|
||||
* map-like and set-like containers, though currently we're only providing
|
||||
* boost::concurrent_flat_map.
|
||||
*
|
||||
* The exposed interface (completed by the wrapping containers) is not that
|
||||
* of a regular container (in fact, it does not model Container as understood
|
||||
@@ -396,7 +362,7 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
|
||||
* - Parallel versions of [c]visit_all(f) and erase_if(f) are provided based
|
||||
* on C++17 stdlib parallel algorithms.
|
||||
*
|
||||
* Consult boost::concurrent_flat_(map|set) docs for the full API reference.
|
||||
* Consult boost::unordered_flat_map docs for the full API reference.
|
||||
* Heterogeneous lookup is suported by default, that is, without checking for
|
||||
* any ::is_transparent typedefs --this checking is done by the wrapping
|
||||
* containers.
|
||||
@@ -424,13 +390,10 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
|
||||
* reduced hash value is set) and the insertion counter is atomically
|
||||
* incremented: if no other thread has incremented the counter during the
|
||||
* whole operation (which is checked by comparing with c0), then we're
|
||||
* good to go and complete the insertion, otherwise we roll back and
|
||||
* start over.
|
||||
* good to go and complete the insertion, otherwise we roll back and start
|
||||
* over.
|
||||
*/
|
||||
|
||||
template<typename,typename,typename,typename>
|
||||
class table; /* concurrent/non-concurrent interop */
|
||||
|
||||
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
|
||||
using concurrent_table_core_impl=table_core<
|
||||
TypePolicy,group15<atomic_integral>,concurrent_table_arrays,
|
||||
@@ -452,10 +415,10 @@ class concurrent_table:
|
||||
using group_type=typename super::group_type;
|
||||
using super::N;
|
||||
using prober=typename super::prober;
|
||||
using arrays_type=typename super::arrays_type;
|
||||
using size_ctrl_type=typename super::size_ctrl_type;
|
||||
using compatible_nonconcurrent_table=table<TypePolicy,Hash,Pred,Allocator>;
|
||||
friend compatible_nonconcurrent_table;
|
||||
|
||||
template<
|
||||
typename TypePolicy2,typename Hash2,typename Pred2,typename Allocator2>
|
||||
friend class concurrent_table;
|
||||
|
||||
public:
|
||||
using key_type=typename super::key_type;
|
||||
@@ -466,7 +429,6 @@ public:
|
||||
using key_equal=typename super::key_equal;
|
||||
using allocator_type=typename super::allocator_type;
|
||||
using size_type=typename super::size_type;
|
||||
static constexpr std::size_t bulk_visit_size=16;
|
||||
|
||||
private:
|
||||
template<typename Value,typename T>
|
||||
@@ -491,30 +453,6 @@ public:
|
||||
concurrent_table(x,al_,x.exclusive_access()){}
|
||||
concurrent_table(concurrent_table&& x,const Allocator& al_):
|
||||
concurrent_table(std::move(x),al_,x.exclusive_access()){}
|
||||
|
||||
template<typename ArraysType>
|
||||
concurrent_table(
|
||||
compatible_nonconcurrent_table&& x,
|
||||
arrays_holder<ArraysType,Allocator>&& ah):
|
||||
super{
|
||||
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
|
||||
[&x]{return arrays_type::new_group_access(
|
||||
x.al(),typename arrays_type::super{
|
||||
x.arrays.groups_size_index,x.arrays.groups_size_mask,
|
||||
to_pointer<typename arrays_type::group_type_pointer>(
|
||||
reinterpret_cast<group_type*>(x.arrays.groups())),
|
||||
x.arrays.elements_});},
|
||||
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
|
||||
{
|
||||
x.arrays=ah.release();
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
}
|
||||
|
||||
concurrent_table(compatible_nonconcurrent_table&& x):
|
||||
concurrent_table(std::move(x),x.make_empty_arrays())
|
||||
{}
|
||||
|
||||
~concurrent_table()=default;
|
||||
|
||||
concurrent_table& operator=(const concurrent_table& x)
|
||||
@@ -524,8 +462,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_table& operator=(concurrent_table&& x)noexcept(
|
||||
noexcept(std::declval<super&>() = std::declval<super&&>()))
|
||||
concurrent_table& operator=(concurrent_table&& x)
|
||||
{
|
||||
auto lck=exclusive_access(*this,x);
|
||||
super::operator=(std::move(x));
|
||||
@@ -551,7 +488,7 @@ public:
|
||||
template<typename Key,typename F>
|
||||
BOOST_FORCEINLINE std::size_t visit(const Key& x,F&& f)
|
||||
{
|
||||
return visit_impl(group_exclusive{},x,std::forward<F>(f));
|
||||
return visit_impl(group_access_for<F>{},x,std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename Key,typename F>
|
||||
@@ -566,30 +503,9 @@ public:
|
||||
return visit(x,std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE
|
||||
std::size_t visit(FwdIterator first,FwdIterator last,F&& f)
|
||||
{
|
||||
return bulk_visit_impl(group_exclusive{},first,last,std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE
|
||||
std::size_t visit(FwdIterator first,FwdIterator last,F&& f)const
|
||||
{
|
||||
return bulk_visit_impl(group_shared{},first,last,std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE
|
||||
std::size_t cvisit(FwdIterator first,FwdIterator last,F&& f)const
|
||||
{
|
||||
return visit(first,last,std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F> std::size_t visit_all(F&& f)
|
||||
{
|
||||
return visit_all_impl(group_exclusive{},std::forward<F>(f));
|
||||
return visit_all_impl(group_access_for<F>{},std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F> std::size_t visit_all(F&& f)const
|
||||
@@ -607,7 +523,7 @@ public:
|
||||
void visit_all(ExecutionPolicy&& policy,F&& f)
|
||||
{
|
||||
visit_all_impl(
|
||||
group_exclusive{},
|
||||
group_access_for<F>{},
|
||||
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
|
||||
}
|
||||
|
||||
@@ -626,46 +542,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename F> bool visit_while(F&& f)
|
||||
{
|
||||
return visit_while_impl(group_exclusive{},std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F> bool visit_while(F&& f)const
|
||||
{
|
||||
return visit_while_impl(group_shared{},std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F> bool cvisit_while(F&& f)const
|
||||
{
|
||||
return visit_while(std::forward<F>(f));
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template<typename ExecutionPolicy,typename F>
|
||||
bool visit_while(ExecutionPolicy&& policy,F&& f)
|
||||
{
|
||||
return visit_while_impl(
|
||||
group_exclusive{},
|
||||
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename ExecutionPolicy,typename F>
|
||||
bool visit_while(ExecutionPolicy&& policy,F&& f)const
|
||||
{
|
||||
return visit_while_impl(
|
||||
group_shared{},
|
||||
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename ExecutionPolicy,typename F>
|
||||
bool cvisit_while(ExecutionPolicy&& policy,F&& f)const
|
||||
{
|
||||
return visit_while(
|
||||
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool empty()const noexcept{return size()==0;}
|
||||
|
||||
std::size_t size()const noexcept
|
||||
@@ -709,7 +585,7 @@ public:
|
||||
BOOST_FORCEINLINE bool try_emplace_or_visit(Key&& x,Args&&... args)
|
||||
{
|
||||
return emplace_or_visit_flast(
|
||||
group_exclusive{},
|
||||
group_access_for_last_of<Args...>{},
|
||||
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -725,7 +601,7 @@ public:
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Args&&... args)
|
||||
{
|
||||
return construct_and_emplace_or_visit_flast(
|
||||
group_exclusive{},std::forward<Args>(args)...);
|
||||
group_access_for_last_of<Args...>{},std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
@@ -738,7 +614,7 @@ public:
|
||||
template<typename F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(const init_type& x,F&& f)
|
||||
{
|
||||
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x);
|
||||
return emplace_or_visit_impl(group_access_for<F>{},std::forward<F>(f),x);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
@@ -751,7 +627,7 @@ public:
|
||||
BOOST_FORCEINLINE bool insert_or_visit(init_type&& x,F&& f)
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_exclusive{},std::forward<F>(f),std::move(x));
|
||||
group_access_for<F>{},std::forward<F>(f),std::move(x));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
@@ -767,7 +643,7 @@ public:
|
||||
BOOST_FORCEINLINE auto insert_or_visit(const Value& x,F&& f)
|
||||
->enable_if_is_value_type<Value,bool>
|
||||
{
|
||||
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x);
|
||||
return emplace_or_visit_impl(group_access_for<F>{},std::forward<F>(f),x);
|
||||
}
|
||||
|
||||
template<typename Value,typename F>
|
||||
@@ -782,7 +658,7 @@ public:
|
||||
->enable_if_is_value_type<Value,bool>
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_exclusive{},std::forward<F>(f),std::move(x));
|
||||
group_access_for<F>{},std::forward<F>(f),std::move(x));
|
||||
}
|
||||
|
||||
template<typename Value,typename F>
|
||||
@@ -810,7 +686,7 @@ public:
|
||||
group_exclusive{},x,this->position_for(hash),hash,
|
||||
[&,this](group_type* pg,unsigned int n,element_type* p)
|
||||
{
|
||||
if(f(cast_for(group_exclusive{},type_policy::value_from(*p)))){
|
||||
if(f(cast_for(group_access_for<F>{},type_policy::value_from(*p)))){
|
||||
super::erase(pg,n,p);
|
||||
res=1;
|
||||
}
|
||||
@@ -826,7 +702,7 @@ public:
|
||||
for_all_elements(
|
||||
group_exclusive{},
|
||||
[&,this](group_type* pg,unsigned int n,element_type* p){
|
||||
if(f(cast_for(group_exclusive{},type_policy::value_from(*p)))){
|
||||
if(f(cast_for(group_access_for<F>{},type_policy::value_from(*p)))){
|
||||
super::erase(pg,n,p);
|
||||
++res;
|
||||
}
|
||||
@@ -843,7 +719,7 @@ public:
|
||||
for_all_elements(
|
||||
group_exclusive{},std::forward<ExecutionPolicy>(policy),
|
||||
[&,this](group_type* pg,unsigned int n,element_type* p){
|
||||
if(f(cast_for(group_exclusive{},type_policy::value_from(*p)))){
|
||||
if(f(cast_for(group_access_for<F>{},type_policy::value_from(*p)))){
|
||||
super::erase(pg,n,p);
|
||||
}
|
||||
});
|
||||
@@ -962,14 +838,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename,typename,typename,typename> friend class concurrent_table;
|
||||
|
||||
using mutex_type=rw_spinlock;
|
||||
using multimutex_type=multimutex<mutex_type,128>; // TODO: adapt 128 to the machine
|
||||
using shared_lock_guard=reentrancy_checked<shared_lock<mutex_type>>;
|
||||
using exclusive_lock_guard=reentrancy_checked<lock_guard<multimutex_type>>;
|
||||
using exclusive_bilock_guard=
|
||||
reentrancy_bichecked<scoped_bilock<multimutex_type>>;
|
||||
using shared_lock_guard=shared_lock<mutex_type>;
|
||||
using exclusive_lock_guard=lock_guard<multimutex_type>;
|
||||
using exclusive_bilock_guard=scoped_bilock<multimutex_type>;
|
||||
using group_shared_lock_guard=typename group_access::shared_lock_guard;
|
||||
using group_exclusive_lock_guard=typename group_access::exclusive_lock_guard;
|
||||
using group_insert_counter_type=typename group_access::insert_counter_type;
|
||||
@@ -989,18 +862,18 @@ private:
|
||||
{
|
||||
thread_local auto id=(++thread_counter)%mutexes.size();
|
||||
|
||||
return shared_lock_guard{this,mutexes[id]};
|
||||
return shared_lock_guard{mutexes[id]};
|
||||
}
|
||||
|
||||
inline exclusive_lock_guard exclusive_access()const
|
||||
{
|
||||
return exclusive_lock_guard{this,mutexes};
|
||||
return exclusive_lock_guard{mutexes};
|
||||
}
|
||||
|
||||
static inline exclusive_bilock_guard exclusive_access(
|
||||
const concurrent_table& x,const concurrent_table& y)
|
||||
{
|
||||
return {&x,&y,x.mutexes,y.mutexes};
|
||||
return {x.mutexes,y.mutexes};
|
||||
}
|
||||
|
||||
template<typename Hash2,typename Pred2>
|
||||
@@ -1008,7 +881,7 @@ private:
|
||||
const concurrent_table& x,
|
||||
const concurrent_table<TypePolicy,Hash2,Pred2,Allocator>& y)
|
||||
{
|
||||
return {&x,&y,x.mutexes,y.mutexes};
|
||||
return {x.mutexes,y.mutexes};
|
||||
}
|
||||
|
||||
/* Tag-dispatched shared/exclusive group access */
|
||||
@@ -1016,20 +889,31 @@ private:
|
||||
using group_shared=std::false_type;
|
||||
using group_exclusive=std::true_type;
|
||||
|
||||
template<typename F>
|
||||
using group_access_for=typename std::conditional<
|
||||
takes_arg_as_const_reference<F,value_type>::value,
|
||||
group_shared,
|
||||
group_exclusive
|
||||
>::type;
|
||||
|
||||
template<typename... Args>
|
||||
using group_access_for_last_of=
|
||||
group_access_for<mp11::mp_back<mp11::mp_list<Args...>>>;
|
||||
|
||||
inline group_shared_lock_guard access(group_shared,std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses()[pos].shared_access();
|
||||
return this->arrays.group_accesses[pos].shared_access();
|
||||
}
|
||||
|
||||
inline group_exclusive_lock_guard access(
|
||||
group_exclusive,std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses()[pos].exclusive_access();
|
||||
return this->arrays.group_accesses[pos].exclusive_access();
|
||||
}
|
||||
|
||||
inline group_insert_counter_type& insert_counter(std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses()[pos].insert_counter();
|
||||
return this->arrays.group_accesses[pos].insert_counter();
|
||||
}
|
||||
|
||||
/* Const casts value_type& according to the level of group access for
|
||||
@@ -1052,7 +936,7 @@ private:
|
||||
erase_on_exit(
|
||||
concurrent_table& x_,
|
||||
group_type* pg_,unsigned int pos_,element_type* p_):
|
||||
x(x_),pg(pg_),pos(pos_),p(p_){}
|
||||
x{x_},pg{pg_},pos{pos_},p{p_}{}
|
||||
~erase_on_exit(){if(!rollback_)x.super::erase(pg,pos,p);}
|
||||
|
||||
void rollback(){rollback_=true;}
|
||||
@@ -1074,26 +958,6 @@ private:
|
||||
access_mode,x,this->position_for(hash),hash,std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE
|
||||
std::size_t bulk_visit_impl(
|
||||
GroupAccessMode access_mode,FwdIterator first,FwdIterator last,F&& f)const
|
||||
{
|
||||
auto lck=shared_access();
|
||||
std::size_t res=0;
|
||||
auto n=static_cast<std::size_t>(std::distance(first,last));
|
||||
while(n){
|
||||
auto m=n<2*bulk_visit_size?n:bulk_visit_size;
|
||||
res+=unprotected_bulk_visit(access_mode,first,m,std::forward<F>(f));
|
||||
n-=m;
|
||||
std::advance(
|
||||
first,
|
||||
static_cast<
|
||||
typename std::iterator_traits<FwdIterator>::difference_type>(m));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F>
|
||||
std::size_t visit_all_impl(GroupAccessMode access_mode,F&& f)const
|
||||
{
|
||||
@@ -1120,29 +984,6 @@ private:
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename GroupAccessMode,typename F>
|
||||
bool visit_while_impl(GroupAccessMode access_mode,F&& f)const
|
||||
{
|
||||
auto lck=shared_access();
|
||||
return for_all_elements_while(access_mode,[&](element_type* p){
|
||||
return f(cast_for(access_mode,type_policy::value_from(*p)));
|
||||
});
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template<typename GroupAccessMode,typename ExecutionPolicy,typename F>
|
||||
bool visit_while_impl(
|
||||
GroupAccessMode access_mode,ExecutionPolicy&& policy,F&& f)const
|
||||
{
|
||||
auto lck=shared_access();
|
||||
return for_all_elements_while(
|
||||
access_mode,std::forward<ExecutionPolicy>(policy),
|
||||
[&](element_type* p){
|
||||
return f(cast_for(access_mode,type_policy::value_from(*p)));
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename GroupAccessMode,typename Key,typename F>
|
||||
BOOST_FORCEINLINE std::size_t unprotected_visit(
|
||||
GroupAccessMode access_mode,
|
||||
@@ -1168,10 +1009,10 @@ private:
|
||||
prober pb(pos0);
|
||||
do{
|
||||
auto pos=pb.get();
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto pg=this->arrays.groups+pos;
|
||||
auto mask=pg->match(hash);
|
||||
if(mask){
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
auto p=this->arrays.elements+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
auto lck=access(access_mode,pos);
|
||||
do{
|
||||
@@ -1192,77 +1033,6 @@ private:
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename FwdIterator,typename F>
|
||||
BOOST_FORCEINLINE std::size_t unprotected_bulk_visit(
|
||||
GroupAccessMode access_mode,FwdIterator first,std::size_t m,F&& f)const
|
||||
{
|
||||
BOOST_ASSERT(m<2*bulk_visit_size);
|
||||
|
||||
std::size_t res=0,
|
||||
hashes[2*bulk_visit_size-1],
|
||||
positions[2*bulk_visit_size-1];
|
||||
int masks[2*bulk_visit_size-1];
|
||||
auto it=first;
|
||||
|
||||
for(auto i=m;i--;++it){
|
||||
auto hash=hashes[i]=this->hash_for(*it);
|
||||
auto pos=positions[i]=this->position_for(hash);
|
||||
BOOST_UNORDERED_PREFETCH(this->arrays.groups()+pos);
|
||||
}
|
||||
|
||||
for(auto i=m;i--;){
|
||||
auto hash=hashes[i];
|
||||
auto pos=positions[i];
|
||||
auto mask=masks[i]=(this->arrays.groups()+pos)->match(hash);
|
||||
if(mask){
|
||||
BOOST_UNORDERED_PREFETCH(this->arrays.group_accesses()+pos);
|
||||
//BOOST_UNORDERED_PREFETCH_ELEMENTS(this->arrays.elements()+pos*N,N);
|
||||
BOOST_UNORDERED_PREFETCH(this->arrays.elements()+pos*N+unchecked_countr_zero(mask));
|
||||
}
|
||||
}
|
||||
|
||||
it=first;
|
||||
for(auto i=m;i--;++it){
|
||||
auto pos=positions[i];
|
||||
prober pb(pos);
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto mask=masks[i];
|
||||
element_type *p;
|
||||
if(!mask)goto post_mask;
|
||||
p=this->arrays.elements()+pos*N;
|
||||
for(;;){
|
||||
{
|
||||
auto lck=access(access_mode,pos);
|
||||
do{
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_LIKELY(
|
||||
pg->is_occupied(n)&&
|
||||
bool(this->pred()(*it,this->key_from(p[n]))))){
|
||||
f(cast_for(access_mode,type_policy::value_from(p[n])));
|
||||
++res;
|
||||
goto next_key;
|
||||
}
|
||||
mask&=mask-1;
|
||||
}while(mask);
|
||||
}
|
||||
post_mask:
|
||||
do{
|
||||
if(BOOST_LIKELY(pg->is_not_overflowed(hashes[i]))||
|
||||
BOOST_UNLIKELY(!pb.next(this->arrays.groups_size_mask))){
|
||||
goto next_key;
|
||||
}
|
||||
pos=pb.get();
|
||||
pg=this->arrays.groups()+pos;
|
||||
mask=pg->match(hashes[i]);
|
||||
}while(!mask);
|
||||
p=this->arrays.elements()+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
}
|
||||
next_key:;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) /* C4800 */
|
||||
#endif
|
||||
@@ -1397,7 +1167,7 @@ private:
|
||||
|
||||
struct reserve_size
|
||||
{
|
||||
reserve_size(concurrent_table& x_):x(x_)
|
||||
reserve_size(concurrent_table& x_):x{x_}
|
||||
{
|
||||
size_=++x.size_ctrl.size;
|
||||
}
|
||||
@@ -1455,7 +1225,7 @@ private:
|
||||
if(BOOST_LIKELY(rsize.succeeded())){
|
||||
for(prober pb(pos0);;pb.next(this->arrays.groups_size_mask)){
|
||||
auto pos=pb.get();
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto pg=this->arrays.groups+pos;
|
||||
auto lck=access(group_exclusive{},pos);
|
||||
auto mask=pg->match_available();
|
||||
if(BOOST_LIKELY(mask!=0)){
|
||||
@@ -1465,7 +1235,7 @@ private:
|
||||
/* other thread inserted from pos0, need to start over */
|
||||
goto startover;
|
||||
}
|
||||
auto p=this->arrays.elements()+pos*N+n;
|
||||
auto p=this->arrays.elements+pos*N+n;
|
||||
this->construct_element(p,std::forward<Args>(args)...);
|
||||
rslot.commit();
|
||||
rsize.commit();
|
||||
@@ -1498,37 +1268,18 @@ private:
|
||||
auto for_all_elements(GroupAccessMode access_mode,F f)const
|
||||
->decltype(f(nullptr,0,nullptr),void())
|
||||
{
|
||||
for_all_elements_while(
|
||||
access_mode,[&](group_type* pg,unsigned int n,element_type* p)
|
||||
{f(pg,n,p);return true;});
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F>
|
||||
auto for_all_elements_while(GroupAccessMode access_mode,F f)const
|
||||
->decltype(f(nullptr),bool())
|
||||
{
|
||||
return for_all_elements_while(
|
||||
access_mode,[&](group_type*,unsigned int,element_type* p){return f(p);});
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F>
|
||||
auto for_all_elements_while(GroupAccessMode access_mode,F f)const
|
||||
->decltype(f(nullptr,0,nullptr),bool())
|
||||
{
|
||||
auto p=this->arrays.elements();
|
||||
if(p){
|
||||
for(auto pg=this->arrays.groups(),last=pg+this->arrays.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups()));
|
||||
auto mask=this->match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(!f(pg,n,p+n))return false;
|
||||
mask&=mask-1;
|
||||
}
|
||||
auto p=this->arrays.elements;
|
||||
if(!p)return;
|
||||
for(auto pg=this->arrays.groups,last=pg+this->arrays.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups));
|
||||
auto mask=this->match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
f(pg,n,p+n);
|
||||
mask&=mask-1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
@@ -1547,15 +1298,15 @@ private:
|
||||
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
|
||||
->decltype(f(nullptr,0,nullptr),void())
|
||||
{
|
||||
if(!this->arrays.elements())return;
|
||||
auto first=this->arrays.groups(),
|
||||
if(!this->arrays.elements)return;
|
||||
auto first=this->arrays.groups,
|
||||
last=first+this->arrays.groups_size_mask+1;
|
||||
std::for_each(std::forward<ExecutionPolicy>(policy),first,last,
|
||||
[&,this](group_type& g){
|
||||
auto pos=static_cast<std::size_t>(&g-first);
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
auto lck=access(access_mode,pos);
|
||||
auto mask=this->match_really_occupied(&g,last);
|
||||
std::size_t pos=static_cast<std::size_t>(&g-first);
|
||||
auto p=this->arrays.elements+pos*N;
|
||||
auto lck=access(access_mode,pos);
|
||||
auto mask=this->match_really_occupied(&g,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
f(&g,n,p+n);
|
||||
@@ -1564,164 +1315,8 @@ private:
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename ExecutionPolicy,typename F>
|
||||
bool for_all_elements_while(
|
||||
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
|
||||
{
|
||||
if(!this->arrays.elements())return true;
|
||||
auto first=this->arrays.groups(),
|
||||
last=first+this->arrays.groups_size_mask+1;
|
||||
return std::all_of(std::forward<ExecutionPolicy>(policy),first,last,
|
||||
[&,this](group_type& g){
|
||||
auto pos=static_cast<std::size_t>(&g-first);
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
auto lck=access(access_mode,pos);
|
||||
auto mask=this->match_really_occupied(&g,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(!f(p+n))return false;
|
||||
mask&=mask-1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar,unsigned int version)
|
||||
{
|
||||
core::split_member(ar,*this,version);
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void save(Archive& ar,unsigned int version)const
|
||||
{
|
||||
save(
|
||||
ar,version,
|
||||
std::integral_constant<bool,std::is_same<key_type,value_type>::value>{});
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void save(Archive& ar,unsigned int,std::true_type /* set */)const
|
||||
{
|
||||
auto lck=exclusive_access();
|
||||
const std::size_t s=super::size();
|
||||
const serialization_version<value_type> value_version;
|
||||
|
||||
ar<<core::make_nvp("count",s);
|
||||
ar<<core::make_nvp("value_version",value_version);
|
||||
|
||||
super::for_all_elements([&,this](element_type* p){
|
||||
auto& x=type_policy::value_from(*p);
|
||||
core::save_construct_data_adl(ar,std::addressof(x),value_version);
|
||||
ar<<serialization::make_nvp("item",x);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void save(Archive& ar,unsigned int,std::false_type /* map */)const
|
||||
{
|
||||
using raw_key_type=typename std::remove_const<key_type>::type;
|
||||
using raw_mapped_type=typename std::remove_const<
|
||||
typename TypePolicy::mapped_type>::type;
|
||||
|
||||
auto lck=exclusive_access();
|
||||
const std::size_t s=super::size();
|
||||
const serialization_version<raw_key_type> key_version;
|
||||
const serialization_version<raw_mapped_type> mapped_version;
|
||||
|
||||
ar<<core::make_nvp("count",s);
|
||||
ar<<core::make_nvp("key_version",key_version);
|
||||
ar<<core::make_nvp("mapped_version",mapped_version);
|
||||
|
||||
super::for_all_elements([&,this](element_type* p){
|
||||
/* To remain lib-independent from Boost.Serialization and not rely on
|
||||
* the user having included the serialization code for std::pair
|
||||
* (boost/serialization/utility.hpp), we serialize the key and the
|
||||
* mapped value separately.
|
||||
*/
|
||||
|
||||
auto& x=type_policy::value_from(*p);
|
||||
core::save_construct_data_adl(
|
||||
ar,std::addressof(x.first),key_version);
|
||||
ar<<serialization::make_nvp("key",x.first);
|
||||
core::save_construct_data_adl(
|
||||
ar,std::addressof(x.second),mapped_version);
|
||||
ar<<serialization::make_nvp("mapped",x.second);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar,unsigned int version)
|
||||
{
|
||||
load(
|
||||
ar,version,
|
||||
std::integral_constant<bool,std::is_same<key_type,value_type>::value>{});
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar,unsigned int,std::true_type /* set */)
|
||||
{
|
||||
auto lck=exclusive_access();
|
||||
std::size_t s;
|
||||
serialization_version<value_type> value_version;
|
||||
|
||||
ar>>core::make_nvp("count",s);
|
||||
ar>>core::make_nvp("value_version",value_version);
|
||||
|
||||
super::clear();
|
||||
super::reserve(s);
|
||||
|
||||
for(std::size_t n=0;n<s;++n){
|
||||
archive_constructed<value_type> value("item",ar,value_version);
|
||||
auto& x=value.get();
|
||||
auto hash=this->hash_for(x);
|
||||
auto pos0=this->position_for(hash);
|
||||
|
||||
if(this->find(x,pos0,hash))throw_exception(bad_archive_exception());
|
||||
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(x));
|
||||
ar.reset_object_address(std::addressof(*loc.p),std::addressof(x));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void load(Archive& ar,unsigned int,std::false_type /* map */)
|
||||
{
|
||||
using raw_key_type=typename std::remove_const<key_type>::type;
|
||||
using raw_mapped_type=typename std::remove_const<
|
||||
typename TypePolicy::mapped_type>::type;
|
||||
|
||||
auto lck=exclusive_access();
|
||||
std::size_t s;
|
||||
serialization_version<raw_key_type> key_version;
|
||||
serialization_version<raw_mapped_type> mapped_version;
|
||||
|
||||
ar>>core::make_nvp("count",s);
|
||||
ar>>core::make_nvp("key_version",key_version);
|
||||
ar>>core::make_nvp("mapped_version",mapped_version);
|
||||
|
||||
super::clear();
|
||||
super::reserve(s);
|
||||
|
||||
for(std::size_t n=0;n<s;++n){
|
||||
archive_constructed<raw_key_type> key("key",ar,key_version);
|
||||
archive_constructed<raw_mapped_type> mapped("mapped",ar,mapped_version);
|
||||
auto& k=key.get();
|
||||
auto& m=mapped.get();
|
||||
auto hash=this->hash_for(k);
|
||||
auto pos0=this->position_for(hash);
|
||||
|
||||
if(this->find(k,pos0,hash))throw_exception(bad_archive_exception());
|
||||
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(k),std::move(m));
|
||||
ar.reset_object_address(std::addressof(loc.p->first),std::addressof(k));
|
||||
ar.reset_object_address(std::addressof(loc.p->second),std::addressof(m));
|
||||
}
|
||||
}
|
||||
|
||||
static std::atomic<std::size_t> thread_counter;
|
||||
mutable multimutex_type mutexes;
|
||||
};
|
||||
|
||||
@@ -22,10 +22,13 @@
|
||||
#include <boost/core/pointer_traits.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/predef.h>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/type_traits/has_trivial_constructor.hpp>
|
||||
#include <boost/type_traits/has_trivial_copy.hpp>
|
||||
#include <boost/type_traits/has_trivial_assign.hpp>
|
||||
#include <boost/type_traits/is_nothrow_swappable.hpp>
|
||||
#include <boost/unordered/detail/narrow_cast.hpp>
|
||||
#include <boost/unordered/detail/mulx.hpp>
|
||||
#include <boost/unordered/detail/static_assert.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/hash_traits.hpp>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
@@ -92,7 +95,7 @@
|
||||
#elif defined(BOOST_UNORDERED_SSE2)
|
||||
#define BOOST_UNORDERED_PREFETCH(p) _mm_prefetch((const char*)(p),_MM_HINT_T0)
|
||||
#else
|
||||
#define BOOST_UNORDERED_PREFETCH(p) ((void)(p))
|
||||
#define BOOST_UNORDERED_PREFETCH(p) ((void)0)
|
||||
#endif
|
||||
|
||||
/* We have experimentally confirmed that ARM architectures get a higher
|
||||
@@ -131,9 +134,9 @@
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) \
|
||||
static_assert(boost::unordered::detail::is_nothrow_swappable<Hash>::value, \
|
||||
static_assert(boost::is_nothrow_swappable<Hash>::value, \
|
||||
"Template parameter Hash is required to be nothrow Swappable."); \
|
||||
static_assert(boost::unordered::detail::is_nothrow_swappable<Pred>::value, \
|
||||
static_assert(boost::is_nothrow_swappable<Pred>::value, \
|
||||
"Template parameter Pred is required to be nothrow Swappable");
|
||||
|
||||
namespace boost{
|
||||
@@ -145,7 +148,7 @@ static constexpr std::size_t default_bucket_count=0;
|
||||
|
||||
/* foa::table_core is the common base of foa::table and foa::concurrent_table,
|
||||
* which in their turn serve as the foundational core of
|
||||
* boost::unordered_(flat|node)_(map|set) and boost::concurrent_flat_(map|set),
|
||||
* boost::unordered_(flat|node)_(map|set) and boost::concurrent_flat_map,
|
||||
* respectively. Its main internal design aspects are:
|
||||
*
|
||||
* - Element slots are logically split into groups of size N=15. The number
|
||||
@@ -308,7 +311,7 @@ struct group15
|
||||
|
||||
private:
|
||||
using slot_type=IntegralWrapper<unsigned char>;
|
||||
BOOST_UNORDERED_STATIC_ASSERT(sizeof(slot_type)==1);
|
||||
BOOST_STATIC_ASSERT(sizeof(slot_type)==1);
|
||||
|
||||
static constexpr unsigned char available_=0,
|
||||
sentinel_=1;
|
||||
@@ -334,49 +337,38 @@ private:
|
||||
{
|
||||
static constexpr boost::uint32_t word[]=
|
||||
{
|
||||
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,0x20202020u,0x21212121u,0x22222222u,0x23232323u,
|
||||
0x24242424u,0x25252525u,0x26262626u,0x27272727u,0x28282828u,0x29292929u,
|
||||
0x2A2A2A2Au,0x2B2B2B2Bu,0x2C2C2C2Cu,0x2D2D2D2Du,0x2E2E2E2Eu,0x2F2F2F2Fu,
|
||||
0x30303030u,0x31313131u,0x32323232u,0x33333333u,0x34343434u,0x35353535u,
|
||||
0x36363636u,0x37373737u,0x38383838u,0x39393939u,0x3A3A3A3Au,0x3B3B3B3Bu,
|
||||
0x3C3C3C3Cu,0x3D3D3D3Du,0x3E3E3E3Eu,0x3F3F3F3Fu,0x40404040u,0x41414141u,
|
||||
0x42424242u,0x43434343u,0x44444444u,0x45454545u,0x46464646u,0x47474747u,
|
||||
0x48484848u,0x49494949u,0x4A4A4A4Au,0x4B4B4B4Bu,0x4C4C4C4Cu,0x4D4D4D4Du,
|
||||
0x4E4E4E4Eu,0x4F4F4F4Fu,0x50505050u,0x51515151u,0x52525252u,0x53535353u,
|
||||
0x54545454u,0x55555555u,0x56565656u,0x57575757u,0x58585858u,0x59595959u,
|
||||
0x5A5A5A5Au,0x5B5B5B5Bu,0x5C5C5C5Cu,0x5D5D5D5Du,0x5E5E5E5Eu,0x5F5F5F5Fu,
|
||||
0x60606060u,0x61616161u,0x62626262u,0x63636363u,0x64646464u,0x65656565u,
|
||||
0x66666666u,0x67676767u,0x68686868u,0x69696969u,0x6A6A6A6Au,0x6B6B6B6Bu,
|
||||
0x6C6C6C6Cu,0x6D6D6D6Du,0x6E6E6E6Eu,0x6F6F6F6Fu,0x70707070u,0x71717171u,
|
||||
0x72727272u,0x73737373u,0x74747474u,0x75757575u,0x76767676u,0x77777777u,
|
||||
0x78787878u,0x79797979u,0x7A7A7A7Au,0x7B7B7B7Bu,0x7C7C7C7Cu,0x7D7D7D7Du,
|
||||
0x7E7E7E7Eu,0x7F7F7F7Fu,0x80808080u,0x81818181u,0x82828282u,0x83838383u,
|
||||
0x84848484u,0x85858585u,0x86868686u,0x87878787u,0x88888888u,0x89898989u,
|
||||
0x8A8A8A8Au,0x8B8B8B8Bu,0x8C8C8C8Cu,0x8D8D8D8Du,0x8E8E8E8Eu,0x8F8F8F8Fu,
|
||||
0x90909090u,0x91919191u,0x92929292u,0x93939393u,0x94949494u,0x95959595u,
|
||||
0x96969696u,0x97979797u,0x98989898u,0x99999999u,0x9A9A9A9Au,0x9B9B9B9Bu,
|
||||
0x9C9C9C9Cu,0x9D9D9D9Du,0x9E9E9E9Eu,0x9F9F9F9Fu,0xA0A0A0A0u,0xA1A1A1A1u,
|
||||
0xA2A2A2A2u,0xA3A3A3A3u,0xA4A4A4A4u,0xA5A5A5A5u,0xA6A6A6A6u,0xA7A7A7A7u,
|
||||
0xA8A8A8A8u,0xA9A9A9A9u,0xAAAAAAAAu,0xABABABABu,0xACACACACu,0xADADADADu,
|
||||
0xAEAEAEAEu,0xAFAFAFAFu,0xB0B0B0B0u,0xB1B1B1B1u,0xB2B2B2B2u,0xB3B3B3B3u,
|
||||
0xB4B4B4B4u,0xB5B5B5B5u,0xB6B6B6B6u,0xB7B7B7B7u,0xB8B8B8B8u,0xB9B9B9B9u,
|
||||
0xBABABABAu,0xBBBBBBBBu,0xBCBCBCBCu,0xBDBDBDBDu,0xBEBEBEBEu,0xBFBFBFBFu,
|
||||
0xC0C0C0C0u,0xC1C1C1C1u,0xC2C2C2C2u,0xC3C3C3C3u,0xC4C4C4C4u,0xC5C5C5C5u,
|
||||
0xC6C6C6C6u,0xC7C7C7C7u,0xC8C8C8C8u,0xC9C9C9C9u,0xCACACACAu,0xCBCBCBCBu,
|
||||
0xCCCCCCCCu,0xCDCDCDCDu,0xCECECECEu,0xCFCFCFCFu,0xD0D0D0D0u,0xD1D1D1D1u,
|
||||
0xD2D2D2D2u,0xD3D3D3D3u,0xD4D4D4D4u,0xD5D5D5D5u,0xD6D6D6D6u,0xD7D7D7D7u,
|
||||
0xD8D8D8D8u,0xD9D9D9D9u,0xDADADADAu,0xDBDBDBDBu,0xDCDCDCDCu,0xDDDDDDDDu,
|
||||
0xDEDEDEDEu,0xDFDFDFDFu,0xE0E0E0E0u,0xE1E1E1E1u,0xE2E2E2E2u,0xE3E3E3E3u,
|
||||
0xE4E4E4E4u,0xE5E5E5E5u,0xE6E6E6E6u,0xE7E7E7E7u,0xE8E8E8E8u,0xE9E9E9E9u,
|
||||
0xEAEAEAEAu,0xEBEBEBEBu,0xECECECECu,0xEDEDEDEDu,0xEEEEEEEEu,0xEFEFEFEFu,
|
||||
0xF0F0F0F0u,0xF1F1F1F1u,0xF2F2F2F2u,0xF3F3F3F3u,0xF4F4F4F4u,0xF5F5F5F5u,
|
||||
0xF6F6F6F6u,0xF7F7F7F7u,0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,
|
||||
0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu,
|
||||
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,
|
||||
0x20202020u,0x21212121u,0x22222222u,0x23232323u,0x24242424u,0x25252525u,0x26262626u,0x27272727u,
|
||||
0x28282828u,0x29292929u,0x2A2A2A2Au,0x2B2B2B2Bu,0x2C2C2C2Cu,0x2D2D2D2Du,0x2E2E2E2Eu,0x2F2F2F2Fu,
|
||||
0x30303030u,0x31313131u,0x32323232u,0x33333333u,0x34343434u,0x35353535u,0x36363636u,0x37373737u,
|
||||
0x38383838u,0x39393939u,0x3A3A3A3Au,0x3B3B3B3Bu,0x3C3C3C3Cu,0x3D3D3D3Du,0x3E3E3E3Eu,0x3F3F3F3Fu,
|
||||
0x40404040u,0x41414141u,0x42424242u,0x43434343u,0x44444444u,0x45454545u,0x46464646u,0x47474747u,
|
||||
0x48484848u,0x49494949u,0x4A4A4A4Au,0x4B4B4B4Bu,0x4C4C4C4Cu,0x4D4D4D4Du,0x4E4E4E4Eu,0x4F4F4F4Fu,
|
||||
0x50505050u,0x51515151u,0x52525252u,0x53535353u,0x54545454u,0x55555555u,0x56565656u,0x57575757u,
|
||||
0x58585858u,0x59595959u,0x5A5A5A5Au,0x5B5B5B5Bu,0x5C5C5C5Cu,0x5D5D5D5Du,0x5E5E5E5Eu,0x5F5F5F5Fu,
|
||||
0x60606060u,0x61616161u,0x62626262u,0x63636363u,0x64646464u,0x65656565u,0x66666666u,0x67676767u,
|
||||
0x68686868u,0x69696969u,0x6A6A6A6Au,0x6B6B6B6Bu,0x6C6C6C6Cu,0x6D6D6D6Du,0x6E6E6E6Eu,0x6F6F6F6Fu,
|
||||
0x70707070u,0x71717171u,0x72727272u,0x73737373u,0x74747474u,0x75757575u,0x76767676u,0x77777777u,
|
||||
0x78787878u,0x79797979u,0x7A7A7A7Au,0x7B7B7B7Bu,0x7C7C7C7Cu,0x7D7D7D7Du,0x7E7E7E7Eu,0x7F7F7F7Fu,
|
||||
0x80808080u,0x81818181u,0x82828282u,0x83838383u,0x84848484u,0x85858585u,0x86868686u,0x87878787u,
|
||||
0x88888888u,0x89898989u,0x8A8A8A8Au,0x8B8B8B8Bu,0x8C8C8C8Cu,0x8D8D8D8Du,0x8E8E8E8Eu,0x8F8F8F8Fu,
|
||||
0x90909090u,0x91919191u,0x92929292u,0x93939393u,0x94949494u,0x95959595u,0x96969696u,0x97979797u,
|
||||
0x98989898u,0x99999999u,0x9A9A9A9Au,0x9B9B9B9Bu,0x9C9C9C9Cu,0x9D9D9D9Du,0x9E9E9E9Eu,0x9F9F9F9Fu,
|
||||
0xA0A0A0A0u,0xA1A1A1A1u,0xA2A2A2A2u,0xA3A3A3A3u,0xA4A4A4A4u,0xA5A5A5A5u,0xA6A6A6A6u,0xA7A7A7A7u,
|
||||
0xA8A8A8A8u,0xA9A9A9A9u,0xAAAAAAAAu,0xABABABABu,0xACACACACu,0xADADADADu,0xAEAEAEAEu,0xAFAFAFAFu,
|
||||
0xB0B0B0B0u,0xB1B1B1B1u,0xB2B2B2B2u,0xB3B3B3B3u,0xB4B4B4B4u,0xB5B5B5B5u,0xB6B6B6B6u,0xB7B7B7B7u,
|
||||
0xB8B8B8B8u,0xB9B9B9B9u,0xBABABABAu,0xBBBBBBBBu,0xBCBCBCBCu,0xBDBDBDBDu,0xBEBEBEBEu,0xBFBFBFBFu,
|
||||
0xC0C0C0C0u,0xC1C1C1C1u,0xC2C2C2C2u,0xC3C3C3C3u,0xC4C4C4C4u,0xC5C5C5C5u,0xC6C6C6C6u,0xC7C7C7C7u,
|
||||
0xC8C8C8C8u,0xC9C9C9C9u,0xCACACACAu,0xCBCBCBCBu,0xCCCCCCCCu,0xCDCDCDCDu,0xCECECECEu,0xCFCFCFCFu,
|
||||
0xD0D0D0D0u,0xD1D1D1D1u,0xD2D2D2D2u,0xD3D3D3D3u,0xD4D4D4D4u,0xD5D5D5D5u,0xD6D6D6D6u,0xD7D7D7D7u,
|
||||
0xD8D8D8D8u,0xD9D9D9D9u,0xDADADADAu,0xDBDBDBDBu,0xDCDCDCDCu,0xDDDDDDDDu,0xDEDEDEDEu,0xDFDFDFDFu,
|
||||
0xE0E0E0E0u,0xE1E1E1E1u,0xE2E2E2E2u,0xE3E3E3E3u,0xE4E4E4E4u,0xE5E5E5E5u,0xE6E6E6E6u,0xE7E7E7E7u,
|
||||
0xE8E8E8E8u,0xE9E9E9E9u,0xEAEAEAEAu,0xEBEBEBEBu,0xECECECECu,0xEDEDEDEDu,0xEEEEEEEEu,0xEFEFEFEFu,
|
||||
0xF0F0F0F0u,0xF1F1F1F1u,0xF2F2F2F2u,0xF3F3F3F3u,0xF4F4F4F4u,0xF5F5F5F5u,0xF6F6F6F6u,0xF7F7F7F7u,
|
||||
0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu,
|
||||
};
|
||||
|
||||
return (int)word[narrow_cast<unsigned char>(hash)];
|
||||
@@ -511,7 +503,7 @@ struct group15
|
||||
|
||||
private:
|
||||
using slot_type=IntegralWrapper<unsigned char>;
|
||||
BOOST_UNORDERED_STATIC_ASSERT(sizeof(slot_type)==1);
|
||||
BOOST_STATIC_ASSERT(sizeof(slot_type)==1);
|
||||
|
||||
static constexpr unsigned char available_=0,
|
||||
sentinel_=1;
|
||||
@@ -557,8 +549,7 @@ private:
|
||||
}
|
||||
|
||||
/* Copied from
|
||||
* https://github.com/simd-everywhere/simde/blob/master/simde/x86/
|
||||
* sse2.h#L3763
|
||||
* https://github.com/simd-everywhere/simde/blob/master/simde/x86/sse2.h#L3763
|
||||
*/
|
||||
|
||||
static inline int simde_mm_movemask_epi8(uint8x16_t a)
|
||||
@@ -637,8 +628,7 @@ struct group15
|
||||
BOOST_ASSERT(pos<N);
|
||||
return
|
||||
pos==N-1&&
|
||||
(m[0] & boost::uint64_t(0x4000400040004000ull))==
|
||||
boost::uint64_t(0x4000ull)&&
|
||||
(m[0] & boost::uint64_t(0x4000400040004000ull))==boost::uint64_t(0x4000ull)&&
|
||||
(m[1] & boost::uint64_t(0x4000400040004000ull))==0;
|
||||
}
|
||||
|
||||
@@ -704,7 +694,7 @@ struct group15
|
||||
|
||||
private:
|
||||
using word_type=IntegralWrapper<uint64_t>;
|
||||
BOOST_UNORDERED_STATIC_ASSERT(sizeof(word_type)==8);
|
||||
BOOST_STATIC_ASSERT(sizeof(word_type)==8);
|
||||
|
||||
static constexpr unsigned char available_=0,
|
||||
sentinel_=1;
|
||||
@@ -797,8 +787,8 @@ private:
|
||||
*
|
||||
* - size_index(n) returns an unspecified "index" number used in other policy
|
||||
* operations.
|
||||
* - size(size_index_) returns the number of groups for the given index. It
|
||||
* is guaranteed that size(size_index(n)) >= n.
|
||||
* - size(size_index_) returns the number of groups for the given index. It is
|
||||
* guaranteed that size(size_index(n)) >= n.
|
||||
* - min_size() is the minimum number of groups permissible, i.e.
|
||||
* size(size_index(0)).
|
||||
* - position(hash,size_index_) maps hash to a position in the range
|
||||
@@ -948,134 +938,74 @@ Group* dummy_groups()
|
||||
const_cast<typename Group::dummy_group_type*>(storage));
|
||||
}
|
||||
|
||||
template<
|
||||
typename Ptr,typename Ptr2,
|
||||
typename std::enable_if<!std::is_same<Ptr,Ptr2>::value>::type* = nullptr
|
||||
>
|
||||
Ptr to_pointer(Ptr2 p)
|
||||
{
|
||||
if(!p){return nullptr;}
|
||||
return boost::pointer_traits<Ptr>::pointer_to(*p);
|
||||
}
|
||||
|
||||
template<typename Ptr>
|
||||
Ptr to_pointer(Ptr p)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
template<typename Arrays,typename Allocator>
|
||||
struct arrays_holder
|
||||
{
|
||||
arrays_holder(const Arrays& arrays,const Allocator& al):
|
||||
arrays_{arrays},al_{al}
|
||||
{}
|
||||
|
||||
/* not defined but VS in pre-C++17 mode needs to see it for RVO */
|
||||
arrays_holder(arrays_holder const&);
|
||||
arrays_holder& operator=(arrays_holder const&)=delete;
|
||||
|
||||
~arrays_holder(){if(!released_)arrays_.delete_(al_,arrays_);}
|
||||
|
||||
const Arrays& release()
|
||||
{
|
||||
released_=true;
|
||||
return arrays_;
|
||||
}
|
||||
|
||||
private:
|
||||
Arrays arrays_;
|
||||
Allocator al_;
|
||||
bool released_=false;
|
||||
};
|
||||
|
||||
template<typename Value,typename Group,typename SizePolicy,typename Allocator>
|
||||
template<typename Value,typename Group,typename SizePolicy>
|
||||
struct table_arrays
|
||||
{
|
||||
using allocator_type=typename boost::allocator_rebind<Allocator,Value>::type;
|
||||
|
||||
using value_type=Value;
|
||||
using group_type=Group;
|
||||
static constexpr auto N=group_type::N;
|
||||
using size_policy=SizePolicy;
|
||||
using value_type_pointer=
|
||||
typename boost::allocator_pointer<allocator_type>::type;
|
||||
using group_type_pointer=
|
||||
typename boost::pointer_traits<value_type_pointer>::template
|
||||
rebind<group_type>;
|
||||
using group_type_pointer_traits=boost::pointer_traits<group_type_pointer>;
|
||||
|
||||
table_arrays(
|
||||
std::size_t gsi,std::size_t gsm,
|
||||
group_type_pointer pg,value_type_pointer pe):
|
||||
groups_size_index{gsi},groups_size_mask{gsm},groups_{pg},elements_{pe}{}
|
||||
table_arrays(std::size_t gsi,std::size_t gsm,group_type *pg,value_type *pe):
|
||||
groups_size_index{gsi},groups_size_mask{gsm},groups{pg},elements{pe}{}
|
||||
|
||||
value_type* elements()const noexcept{return boost::to_address(elements_);}
|
||||
group_type* groups()const noexcept{return boost::to_address(groups_);}
|
||||
|
||||
static void set_arrays(table_arrays& arrays,allocator_type al,std::size_t n)
|
||||
template<typename Allocator>
|
||||
static table_arrays new_(Allocator& al,std::size_t n)
|
||||
{
|
||||
return set_arrays(
|
||||
arrays,al,n,std::is_same<group_type*,group_type_pointer>{});
|
||||
}
|
||||
using storage_allocator=
|
||||
typename boost::allocator_rebind<Allocator, Value>::type;
|
||||
using storage_traits=boost::allocator_traits<storage_allocator>;
|
||||
|
||||
static void set_arrays(
|
||||
table_arrays& arrays,allocator_type al,std::size_t,
|
||||
std::false_type /* always allocate */)
|
||||
{
|
||||
using storage_traits=boost::allocator_traits<allocator_type>;
|
||||
auto groups_size_index=arrays.groups_size_index;
|
||||
auto groups_size=size_policy::size(groups_size_index);
|
||||
|
||||
auto sal=allocator_type(al);
|
||||
arrays.elements_=storage_traits::allocate(sal,buffer_size(groups_size));
|
||||
|
||||
/* Align arrays.groups to sizeof(group_type). table_iterator critically
|
||||
* depends on such alignment for its increment operation.
|
||||
*/
|
||||
|
||||
auto p=reinterpret_cast<unsigned char*>(arrays.elements()+groups_size*N-1);
|
||||
p+=(uintptr_t(sizeof(group_type))-
|
||||
reinterpret_cast<uintptr_t>(p))%sizeof(group_type);
|
||||
arrays.groups_=
|
||||
group_type_pointer_traits::pointer_to(*reinterpret_cast<group_type*>(p));
|
||||
|
||||
initialize_groups(
|
||||
arrays.groups(),groups_size,
|
||||
is_trivially_default_constructible<group_type>{});
|
||||
arrays.groups()[groups_size-1].set_sentinel();
|
||||
}
|
||||
|
||||
static void set_arrays(
|
||||
table_arrays& arrays,allocator_type al,std::size_t n,
|
||||
std::true_type /* optimize for n==0*/)
|
||||
{
|
||||
if(!n){
|
||||
arrays.groups_=dummy_groups<group_type,size_policy::min_size()>();
|
||||
}
|
||||
else{
|
||||
set_arrays(arrays,al,n,std::false_type{});
|
||||
}
|
||||
}
|
||||
|
||||
static table_arrays new_(allocator_type al,std::size_t n)
|
||||
{
|
||||
auto groups_size_index=size_index_for<group_type,size_policy>(n);
|
||||
auto groups_size=size_policy::size(groups_size_index);
|
||||
table_arrays arrays{groups_size_index,groups_size-1,nullptr,nullptr};
|
||||
|
||||
set_arrays(arrays,al,n);
|
||||
if(!n){
|
||||
arrays.groups=dummy_groups<group_type,size_policy::min_size()>();
|
||||
}
|
||||
else{
|
||||
auto sal=storage_allocator(al);
|
||||
arrays.elements=boost::to_address(
|
||||
storage_traits::allocate(sal,buffer_size(groups_size)));
|
||||
|
||||
/* Align arrays.groups to sizeof(group_type). table_iterator critically
|
||||
* depends on such alignment for its increment operation.
|
||||
*/
|
||||
|
||||
auto p=reinterpret_cast<unsigned char*>(arrays.elements+groups_size*N-1);
|
||||
p+=(uintptr_t(sizeof(group_type))-
|
||||
reinterpret_cast<uintptr_t>(p))%sizeof(group_type);
|
||||
arrays.groups=reinterpret_cast<group_type*>(p);
|
||||
|
||||
initialize_groups(
|
||||
arrays.groups,groups_size,
|
||||
std::integral_constant<
|
||||
bool,
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
|
||||
/* std::is_trivially_constructible not provided */
|
||||
boost::has_trivial_constructor<group_type>::value
|
||||
#else
|
||||
std::is_trivially_constructible<group_type>::value
|
||||
#endif
|
||||
>{});
|
||||
arrays.groups[groups_size-1].set_sentinel();
|
||||
}
|
||||
return arrays;
|
||||
}
|
||||
|
||||
static void delete_(allocator_type al,table_arrays& arrays)noexcept
|
||||
template<typename Allocator>
|
||||
static void delete_(Allocator& al,table_arrays& arrays)noexcept
|
||||
{
|
||||
using storage_traits=boost::allocator_traits<allocator_type>;
|
||||
using storage_alloc=typename boost::allocator_rebind<Allocator,Value>::type;
|
||||
using storage_traits=boost::allocator_traits<storage_alloc>;
|
||||
using pointer=typename storage_traits::pointer;
|
||||
using pointer_traits=boost::pointer_traits<pointer>;
|
||||
|
||||
auto sal=allocator_type(al);
|
||||
if(arrays.elements()){
|
||||
auto sal=storage_alloc(al);
|
||||
if(arrays.elements){
|
||||
storage_traits::deallocate(
|
||||
sal,arrays.elements_,buffer_size(arrays.groups_size_mask+1));
|
||||
sal,pointer_traits::pointer_to(*arrays.elements),
|
||||
buffer_size(arrays.groups_size_mask+1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1094,7 +1024,7 @@ struct table_arrays
|
||||
}
|
||||
|
||||
static void initialize_groups(
|
||||
group_type* pg,std::size_t size,std::true_type /* memset */)
|
||||
group_type* groups_,std::size_t size,std::true_type /* memset */)
|
||||
{
|
||||
/* memset faster/not slower than manual, assumes all zeros is group_type's
|
||||
* default layout.
|
||||
@@ -1103,19 +1033,19 @@ struct table_arrays
|
||||
*/
|
||||
|
||||
std::memset(
|
||||
reinterpret_cast<unsigned char*>(pg),0,sizeof(group_type)*size);
|
||||
reinterpret_cast<unsigned char*>(groups_),0,sizeof(group_type)*size);
|
||||
}
|
||||
|
||||
static void initialize_groups(
|
||||
group_type* pg,std::size_t size,std::false_type /* manual */)
|
||||
group_type* groups_,std::size_t size,std::false_type /* manual */)
|
||||
{
|
||||
while(size--!=0)::new (pg++) group_type();
|
||||
while(size--!=0)::new (groups_++) group_type();
|
||||
}
|
||||
|
||||
std::size_t groups_size_index;
|
||||
std::size_t groups_size_mask;
|
||||
group_type_pointer groups_;
|
||||
value_type_pointer elements_;
|
||||
std::size_t groups_size_index;
|
||||
std::size_t groups_size_mask;
|
||||
group_type *groups;
|
||||
value_type *elements;
|
||||
};
|
||||
|
||||
struct if_constexpr_void_else{void operator()()const{}};
|
||||
@@ -1250,8 +1180,7 @@ alloc_make_insert_type(const Allocator& al,Args&&... args)
|
||||
* init_type and element_type:
|
||||
*
|
||||
* - TypePolicy::key_type and TypePolicy::value_type have the obvious
|
||||
* meaning. TypePolicy::mapped_type is expected to be provided as well
|
||||
* when key_type and value_type are not the same.
|
||||
* meaning.
|
||||
*
|
||||
* - TypePolicy::init_type is the type implicitly converted to when
|
||||
* writing x.insert({...}). For maps, this is std::pair<Key,T> rather
|
||||
@@ -1261,8 +1190,8 @@ alloc_make_insert_type(const Allocator& al,Args&&... args)
|
||||
* both init_type and value_type references.
|
||||
*
|
||||
* - TypePolicy::construct and TypePolicy::destroy are used for the
|
||||
* construction and destruction of the internal types: value_type,
|
||||
* init_type and element_type.
|
||||
* construction and destruction of the internal types: value_type, init_type
|
||||
* and element_type.
|
||||
*
|
||||
* - TypePolicy::move is used to provide move semantics for the internal
|
||||
* types used by the container during rehashing and emplace. These types
|
||||
@@ -1328,12 +1257,8 @@ public:
|
||||
>::type;
|
||||
using alloc_traits=boost::allocator_traits<Allocator>;
|
||||
using element_type=typename type_policy::element_type;
|
||||
using arrays_type=Arrays<element_type,group_type,size_policy,Allocator>;
|
||||
using arrays_type=Arrays<element_type,group_type,size_policy>;
|
||||
using size_ctrl_type=SizeControl;
|
||||
static constexpr auto uses_fancy_pointers=!std::is_same<
|
||||
typename alloc_traits::pointer,
|
||||
typename alloc_traits::value_type*
|
||||
>::value;
|
||||
|
||||
using key_type=typename type_policy::key_type;
|
||||
using init_type=typename type_policy::init_type;
|
||||
@@ -1348,7 +1273,6 @@ public:
|
||||
using size_type=std::size_t;
|
||||
using difference_type=std::ptrdiff_t;
|
||||
using locator=table_locator<group_type,element_type>;
|
||||
using arrays_holder_type=arrays_holder<arrays_type,Allocator>;
|
||||
|
||||
table_core(
|
||||
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
|
||||
@@ -1358,43 +1282,23 @@ public:
|
||||
size_ctrl{initial_max_load(),0}
|
||||
{}
|
||||
|
||||
/* genericize on an ArraysFn so that we can do things like delay an
|
||||
* allocation for the group_access data required by cfoa after the move
|
||||
* constructors of Hash, Pred have been invoked
|
||||
*/
|
||||
template<typename ArraysFn>
|
||||
table_core(
|
||||
Hash&& h_,Pred&& pred_,Allocator&& al_,
|
||||
ArraysFn arrays_fn,const size_ctrl_type& size_ctrl_):
|
||||
hash_base{empty_init,std::move(h_)},
|
||||
pred_base{empty_init,std::move(pred_)},
|
||||
allocator_base{empty_init,std::move(al_)},
|
||||
arrays(arrays_fn()),size_ctrl(size_ctrl_)
|
||||
{}
|
||||
|
||||
table_core(const table_core& x):
|
||||
table_core{x,alloc_traits::select_on_container_copy_construction(x.al())}{}
|
||||
|
||||
template<typename ArraysFn>
|
||||
table_core(table_core&& x,arrays_holder_type&& ah,ArraysFn arrays_fn):
|
||||
table_core(
|
||||
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
|
||||
arrays_fn,x.size_ctrl)
|
||||
{
|
||||
x.arrays=ah.release();
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
}
|
||||
|
||||
table_core(table_core&& x)
|
||||
noexcept(
|
||||
std::is_nothrow_move_constructible<Hash>::value&&
|
||||
std::is_nothrow_move_constructible<Pred>::value&&
|
||||
std::is_nothrow_move_constructible<Allocator>::value&&
|
||||
!uses_fancy_pointers):
|
||||
table_core{
|
||||
std::move(x),x.make_empty_arrays(),[&x]{return x.arrays;}}
|
||||
{}
|
||||
std::is_nothrow_move_constructible<Allocator>::value):
|
||||
hash_base{empty_init,std::move(x.h())},
|
||||
pred_base{empty_init,std::move(x.pred())},
|
||||
allocator_base{empty_init,std::move(x.al())},
|
||||
arrays(x.arrays),size_ctrl(x.size_ctrl)
|
||||
{
|
||||
x.arrays=x.new_arrays(0);
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
}
|
||||
|
||||
table_core(const table_core& x,const Allocator& al_):
|
||||
table_core{std::size_t(std::ceil(float(x.size())/mlf)),x.h(),x.pred(),al_}
|
||||
@@ -1432,24 +1336,6 @@ public:
|
||||
delete_arrays(arrays);
|
||||
}
|
||||
|
||||
std::size_t initial_max_load()const
|
||||
{
|
||||
static constexpr std::size_t small_capacity=2*N-1;
|
||||
|
||||
auto capacity_=capacity();
|
||||
if(capacity_<=small_capacity){
|
||||
return capacity_; /* we allow 100% usage */
|
||||
}
|
||||
else{
|
||||
return (std::size_t)(mlf*(float)(capacity_));
|
||||
}
|
||||
}
|
||||
|
||||
arrays_holder_type make_empty_arrays()const
|
||||
{
|
||||
return make_arrays(0);
|
||||
}
|
||||
|
||||
table_core& operator=(const table_core& x)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||
@@ -1464,6 +1350,9 @@ public:
|
||||
hasher tmp_h=x.h();
|
||||
key_equal tmp_p=x.pred();
|
||||
|
||||
/* already noexcept, clear() before we swap the Hash, Pred just in case
|
||||
* the clear() impl relies on them at some point in the future.
|
||||
*/
|
||||
clear();
|
||||
|
||||
/* Because we've asserted at compile-time that Hash and Pred are nothrow
|
||||
@@ -1475,12 +1364,7 @@ public:
|
||||
swap(pred(),tmp_p);
|
||||
|
||||
if_constexpr<pocca>([&,this]{
|
||||
if(al()!=x.al()){
|
||||
auto ah=x.make_arrays(std::size_t(std::ceil(float(x.size())/mlf)));
|
||||
delete_arrays(arrays);
|
||||
arrays=ah.release();
|
||||
size_ctrl.ml=initial_max_load();
|
||||
}
|
||||
if(al()!=x.al())reserve(0);
|
||||
copy_assign_if<pocca>(al(),x.al());
|
||||
});
|
||||
/* noshrink: favor memory reuse over tightness */
|
||||
@@ -1497,8 +1381,8 @@ public:
|
||||
|
||||
table_core& operator=(table_core&& x)
|
||||
noexcept(
|
||||
(alloc_traits::propagate_on_container_move_assignment::value||
|
||||
alloc_traits::is_always_equal::value)&&!uses_fancy_pointers)
|
||||
alloc_traits::propagate_on_container_move_assignment::value||
|
||||
alloc_traits::is_always_equal::value)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||
|
||||
@@ -1519,24 +1403,16 @@ public:
|
||||
using std::swap;
|
||||
|
||||
clear();
|
||||
swap(h(),x.h());
|
||||
swap(pred(),x.pred());
|
||||
|
||||
if(pocma||al()==x.al()){
|
||||
auto ah=x.make_empty_arrays();
|
||||
swap(h(),x.h());
|
||||
swap(pred(),x.pred());
|
||||
delete_arrays(arrays);
|
||||
reserve(0);
|
||||
move_assign_if<pocma>(al(),x.al());
|
||||
arrays=x.arrays;
|
||||
size_ctrl.ml=std::size_t(x.size_ctrl.ml);
|
||||
size_ctrl.size=std::size_t(x.size_ctrl.size);
|
||||
x.arrays=ah.release();
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
swap(arrays,x.arrays);
|
||||
swap(size_ctrl,x.size_ctrl);
|
||||
}
|
||||
else{
|
||||
swap(h(),x.h());
|
||||
swap(pred(),x.pred());
|
||||
|
||||
/* noshrink: favor memory reuse over tightness */
|
||||
noshrink_reserve(x.size());
|
||||
clear_on_exit c{x};
|
||||
@@ -1597,12 +1473,11 @@ public:
|
||||
prober pb(pos0);
|
||||
do{
|
||||
auto pos=pb.get();
|
||||
auto pg=arrays.groups()+pos;
|
||||
auto pg=arrays.groups+pos;
|
||||
auto mask=pg->match(hash);
|
||||
if(mask){
|
||||
auto elements=arrays.elements();
|
||||
BOOST_UNORDERED_ASSUME(elements!=nullptr);
|
||||
auto p=elements+pos*N;
|
||||
BOOST_UNORDERED_ASSUME(arrays.elements!=nullptr);
|
||||
auto p=arrays.elements+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
do{
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
@@ -1651,9 +1526,9 @@ public:
|
||||
|
||||
void clear()noexcept
|
||||
{
|
||||
auto p=arrays.elements();
|
||||
auto p=arrays.elements;
|
||||
if(p){
|
||||
for(auto pg=arrays.groups(),last=pg+arrays.groups_size_mask+1;
|
||||
for(auto pg=arrays.groups,last=pg+arrays.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto mask=match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
@@ -1663,7 +1538,7 @@ public:
|
||||
/* we wipe the entire metadata to reset the overflow byte as well */
|
||||
pg->initialize();
|
||||
}
|
||||
arrays.groups()[arrays.groups_size_mask].set_sentinel();
|
||||
arrays.groups[arrays.groups_size_mask].set_sentinel();
|
||||
size_ctrl.ml=initial_max_load();
|
||||
size_ctrl.size=0;
|
||||
}
|
||||
@@ -1674,7 +1549,7 @@ public:
|
||||
|
||||
std::size_t capacity()const noexcept
|
||||
{
|
||||
return arrays.elements()?(arrays.groups_size_mask+1)*N-1:0;
|
||||
return arrays.elements?(arrays.groups_size_mask+1)*N-1:0;
|
||||
}
|
||||
|
||||
float load_factor()const noexcept
|
||||
@@ -1893,9 +1768,9 @@ public:
|
||||
static auto for_all_elements_while(const arrays_type& arrays_,F f)
|
||||
->decltype(f(nullptr,0,nullptr),bool())
|
||||
{
|
||||
auto p=arrays_.elements();
|
||||
auto p=arrays_.elements;
|
||||
if(p){
|
||||
for(auto pg=arrays_.groups(),last=pg+arrays_.groups_size_mask+1;
|
||||
for(auto pg=arrays_.groups,last=pg+arrays_.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto mask=match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
@@ -1929,15 +1804,14 @@ private:
|
||||
pred_base{empty_init,std::move(pred_)},
|
||||
allocator_base{empty_init,al_},arrays(new_arrays(0)),
|
||||
size_ctrl{initial_max_load(),0}
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
arrays_type new_arrays(std::size_t n)const
|
||||
arrays_type new_arrays(std::size_t n)
|
||||
{
|
||||
return arrays_type::new_(al(),n);
|
||||
}
|
||||
|
||||
arrays_type new_arrays_for_growth()const
|
||||
arrays_type new_arrays_for_growth()
|
||||
{
|
||||
/* Due to the anti-drift mechanism (see recover_slot), the new arrays may
|
||||
* be of the same size as the old arrays; in the limit, erasing one
|
||||
@@ -1958,11 +1832,6 @@ private:
|
||||
arrays_type::delete_(al(),arrays_);
|
||||
}
|
||||
|
||||
arrays_holder_type make_arrays(std::size_t n)const
|
||||
{
|
||||
return {new_arrays(n),al()};
|
||||
}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
void construct_element_from_try_emplace_args(
|
||||
element_type* p,std::false_type,Key&& x,Args&&... args)
|
||||
@@ -2001,10 +1870,9 @@ private:
|
||||
|
||||
void fast_copy_elements_from(const table_core& x)
|
||||
{
|
||||
if(arrays.elements()&&x.arrays.elements()){
|
||||
if(arrays.elements){
|
||||
copy_elements_array_from(x);
|
||||
copy_groups_array_from(x);
|
||||
size_ctrl.ml=std::size_t(x.size_ctrl.ml);
|
||||
size_ctrl.size=std::size_t(x.size_ctrl.size);
|
||||
}
|
||||
}
|
||||
@@ -2015,7 +1883,13 @@ private:
|
||||
x,
|
||||
std::integral_constant<
|
||||
bool,
|
||||
is_trivially_copy_constructible<element_type>::value&&(
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
|
||||
/* std::is_trivially_copy_constructible not provided */
|
||||
boost::has_trivial_copy<element_type>::value
|
||||
#else
|
||||
std::is_trivially_copy_constructible<element_type>::value
|
||||
#endif
|
||||
&&(
|
||||
is_std_allocator<Allocator>::value||
|
||||
!alloc_has_construct<Allocator,value_type*,const value_type&>::value)
|
||||
>{}
|
||||
@@ -2029,8 +1903,8 @@ private:
|
||||
* copy-assignable when we're relying on trivial copy constructibility.
|
||||
*/
|
||||
std::memcpy(
|
||||
reinterpret_cast<unsigned char*>(arrays.elements()),
|
||||
reinterpret_cast<unsigned char*>(x.arrays.elements()),
|
||||
reinterpret_cast<unsigned char*>(arrays.elements),
|
||||
reinterpret_cast<unsigned char*>(x.arrays.elements),
|
||||
x.capacity()*sizeof(value_type));
|
||||
}
|
||||
|
||||
@@ -2040,14 +1914,14 @@ private:
|
||||
std::size_t num_constructed=0;
|
||||
BOOST_TRY{
|
||||
x.for_all_elements([&,this](const element_type* p){
|
||||
construct_element(arrays.elements()+(p-x.arrays.elements()),*p);
|
||||
construct_element(arrays.elements+(p-x.arrays.elements),*p);
|
||||
++num_constructed;
|
||||
});
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
if(num_constructed){
|
||||
x.for_all_elements_while([&,this](const element_type* p){
|
||||
destroy_element(arrays.elements()+(p-x.arrays.elements()));
|
||||
destroy_element(arrays.elements+(p-x.arrays.elements));
|
||||
return --num_constructed!=0;
|
||||
});
|
||||
}
|
||||
@@ -2057,31 +1931,37 @@ private:
|
||||
}
|
||||
|
||||
void copy_groups_array_from(const table_core& x) {
|
||||
copy_groups_array_from(x,is_trivially_copy_assignable<group_type>{});
|
||||
copy_groups_array_from(x, std::integral_constant<bool,
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
|
||||
/* std::is_trivially_copy_assignable not provided */
|
||||
boost::has_trivial_assign<group_type>::value
|
||||
#else
|
||||
std::is_trivially_copy_assignable<group_type>::value
|
||||
#endif
|
||||
>{}
|
||||
);
|
||||
}
|
||||
|
||||
void copy_groups_array_from(
|
||||
const table_core& x, std::true_type /* -> memcpy */)
|
||||
{
|
||||
std::memcpy(
|
||||
arrays.groups(),x.arrays.groups(),
|
||||
arrays.groups,x.arrays.groups,
|
||||
(arrays.groups_size_mask+1)*sizeof(group_type));
|
||||
}
|
||||
|
||||
void copy_groups_array_from(
|
||||
const table_core& x, std::false_type /* -> manual */)
|
||||
{
|
||||
auto pg=arrays.groups();
|
||||
auto xpg=x.arrays.groups();
|
||||
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
|
||||
pg[i]=xpg[i];
|
||||
arrays.groups[i]=x.arrays.groups[i];
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
/* 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).
|
||||
*/
|
||||
size_ctrl.ml-=group_type::maybe_caused_overflow(pc);
|
||||
@@ -2094,6 +1974,19 @@ private:
|
||||
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;
|
||||
|
||||
auto capacity_=capacity();
|
||||
if(capacity_<=small_capacity){
|
||||
return capacity_; /* we allow 100% usage */
|
||||
}
|
||||
else{
|
||||
return (std::size_t)(mlf*(float)(capacity_));
|
||||
}
|
||||
}
|
||||
|
||||
static std::size_t capacity_for(std::size_t n)
|
||||
{
|
||||
return size_policy::size(size_index_for<group_type,size_policy>(n))*N-1;
|
||||
@@ -2191,11 +2084,11 @@ private:
|
||||
{
|
||||
for(prober pb(pos0);;pb.next(arrays_.groups_size_mask)){
|
||||
auto pos=pb.get();
|
||||
auto pg=arrays_.groups()+pos;
|
||||
auto pg=arrays_.groups+pos;
|
||||
auto mask=pg->match_available();
|
||||
if(BOOST_LIKELY(mask!=0)){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
auto p=arrays_.elements()+pos*N+n;
|
||||
auto p=arrays_.elements+pos*N+n;
|
||||
construct_element(p,std::forward<Args>(args)...);
|
||||
pg->set(n,hash);
|
||||
return {pg,n,p};
|
||||
|
||||
@@ -9,30 +9,26 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||
|
||||
#include <boost/core/pointer_traits.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template<class T,class VoidPtr>
|
||||
template<class T>
|
||||
struct element_type
|
||||
{
|
||||
using value_type=T;
|
||||
using pointer=typename boost::pointer_traits<VoidPtr>::template rebind<T>;
|
||||
|
||||
pointer p;
|
||||
value_type* p;
|
||||
|
||||
/*
|
||||
* we use a deleted copy constructor here so the type is no longer
|
||||
* trivially copy-constructible which inhibits our memcpy
|
||||
* optimizations when copying the tables
|
||||
*/
|
||||
element_type()=default;
|
||||
element_type(pointer p_):p(p_){}
|
||||
element_type(element_type const&)=delete;
|
||||
element_type(element_type&& rhs)noexcept
|
||||
element_type() = default;
|
||||
element_type(value_type* p_):p(p_){}
|
||||
element_type(element_type const&) = delete;
|
||||
element_type(element_type&& rhs) noexcept
|
||||
{
|
||||
p = rhs.p;
|
||||
rhs.p = nullptr;
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace boost {
|
||||
template <class Key, class T> struct flat_map_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
using raw_key_type = typename std::remove_const<Key>::type;
|
||||
using raw_mapped_type = typename std::remove_const<T>::type;
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
|
||||
|
||||
#include <boost/unordered/detail/opt_storage.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
|
||||
@@ -27,6 +25,14 @@ struct insert_return_type
|
||||
NodeType node;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
union opt_storage {
|
||||
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS T t_;
|
||||
|
||||
opt_storage(){}
|
||||
~opt_storage(){}
|
||||
};
|
||||
|
||||
template <class TypePolicy,class Allocator>
|
||||
struct node_handle_base
|
||||
{
|
||||
|
||||
@@ -5,17 +5,14 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/core/pointer_traits.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
namespace foa {
|
||||
template <class Key, class T, class VoidPtr> struct node_map_types
|
||||
template <class Key, class T> struct node_map_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
@@ -26,7 +23,7 @@ namespace boost {
|
||||
using value_type = std::pair<Key const, T>;
|
||||
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
|
||||
|
||||
using element_type = foa::element_type<value_type, VoidPtr>;
|
||||
using element_type = foa::element_type<value_type>;
|
||||
|
||||
static value_type& value_from(element_type const& x)
|
||||
{
|
||||
@@ -85,15 +82,18 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
p->p = boost::allocator_allocate(al, 1);
|
||||
p->p = boost::to_address(boost::allocator_allocate(al, 1));
|
||||
BOOST_TRY
|
||||
{
|
||||
boost::allocator_construct(
|
||||
al, boost::to_address(p->p), std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
boost::allocator_deallocate(al, p->p, 1);
|
||||
using pointer_type = typename boost::allocator_pointer<A>::type;
|
||||
using pointer_traits = boost::pointer_traits<pointer_type>;
|
||||
|
||||
boost::allocator_deallocate(
|
||||
al, pointer_traits::pointer_to(*(p->p)), 1);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
@@ -113,8 +113,12 @@ namespace boost {
|
||||
static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
if (p->p) {
|
||||
destroy(al, boost::to_address(p->p));
|
||||
boost::allocator_deallocate(al, p->p, 1);
|
||||
using pointer_type = typename boost::allocator_pointer<A>::type;
|
||||
using pointer_traits = boost::pointer_traits<pointer_type>;
|
||||
|
||||
destroy(al, p->p);
|
||||
boost::allocator_deallocate(
|
||||
al, pointer_traits::pointer_to(*(p->p)), 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,10 +5,7 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/core/pointer_traits.hpp>
|
||||
|
||||
namespace boost {
|
||||
@@ -16,7 +13,7 @@ namespace boost {
|
||||
namespace detail {
|
||||
namespace foa {
|
||||
|
||||
template <class Key, class VoidPtr> struct node_set_types
|
||||
template <class Key> struct node_set_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using init_type = Key;
|
||||
@@ -24,7 +21,7 @@ namespace boost {
|
||||
|
||||
static Key const& extract(value_type const& key) { return key; }
|
||||
|
||||
using element_type = foa::element_type<value_type, VoidPtr>;
|
||||
using element_type = foa::element_type<value_type>;
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *x.p; }
|
||||
static Key const& extract(element_type const& k) { return *k.p; }
|
||||
@@ -55,15 +52,17 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
p->p = boost::allocator_allocate(al, 1);
|
||||
p->p = boost::to_address(boost::allocator_allocate(al, 1));
|
||||
BOOST_TRY
|
||||
{
|
||||
boost::allocator_construct(
|
||||
al, boost::to_address(p->p), std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
boost::allocator_deallocate(al, p->p, 1);
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<typename boost::allocator_pointer<
|
||||
A>::type>::pointer_to(*p->p),
|
||||
1);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
@@ -78,8 +77,11 @@ namespace boost {
|
||||
static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
if (p->p) {
|
||||
destroy(al, boost::to_address(p->p));
|
||||
boost::allocator_deallocate(al, p->p, 1);
|
||||
destroy(al, p->p);
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<typename boost::allocator_pointer<
|
||||
A>::type>::pointer_to(*(p->p)),
|
||||
1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_REENTRANCY_CHECK_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_REENTRANCY_CHECK_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <utility>
|
||||
|
||||
#if !defined(BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK)&& \
|
||||
!defined(BOOST_ASSERT_IS_VOID)
|
||||
#define BOOST_UNORDERED_REENTRANCY_CHECK
|
||||
#endif
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
#if defined(BOOST_UNORDERED_REENTRANCY_CHECK)
|
||||
|
||||
class entry_trace
|
||||
{
|
||||
public:
|
||||
entry_trace(const void* px_):px{px_}
|
||||
{
|
||||
if(px){
|
||||
BOOST_ASSERT_MSG(!find(px),"reentrancy not allowed");
|
||||
header()=this;
|
||||
}
|
||||
}
|
||||
|
||||
/* not used but VS in pre-C++17 mode needs to see it for RVO */
|
||||
entry_trace(const entry_trace&);
|
||||
|
||||
~entry_trace(){clear();}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if(px){
|
||||
header()=next;
|
||||
px=nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static entry_trace*& header()
|
||||
{
|
||||
thread_local entry_trace *pe=nullptr;
|
||||
return pe;
|
||||
}
|
||||
|
||||
static bool find(const void* px)
|
||||
{
|
||||
for(auto pe=header();pe;pe=pe->next){
|
||||
if(pe->px==px)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const void *px;
|
||||
entry_trace *next=header();
|
||||
};
|
||||
|
||||
template<typename LockGuard>
|
||||
struct reentrancy_checked
|
||||
{
|
||||
template<typename... Args>
|
||||
reentrancy_checked(const void* px,Args&&... args):
|
||||
tr{px},lck{std::forward<Args>(args)...}{}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
lck.unlock();
|
||||
tr.clear();
|
||||
}
|
||||
|
||||
entry_trace tr;
|
||||
LockGuard lck;
|
||||
};
|
||||
|
||||
template<typename LockGuard>
|
||||
struct reentrancy_bichecked
|
||||
{
|
||||
template<typename... Args>
|
||||
reentrancy_bichecked(const void* px,const void* py,Args&&... args):
|
||||
tr1{px},tr2{py!=px?py:nullptr},lck{std::forward<Args>(args)...}{}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
lck.unlock();
|
||||
tr2.clear();
|
||||
tr1.clear();
|
||||
}
|
||||
|
||||
entry_trace tr1,tr2;
|
||||
LockGuard lck;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template<typename LockGuard>
|
||||
struct reentrancy_checked
|
||||
{
|
||||
template<typename... Args>
|
||||
reentrancy_checked(const void*,Args&&... args):
|
||||
lck{std::forward<Args>(args)...}{}
|
||||
|
||||
void unlock(){lck.unlock();}
|
||||
|
||||
LockGuard lck;
|
||||
};
|
||||
|
||||
template<typename LockGuard>
|
||||
struct reentrancy_bichecked
|
||||
{
|
||||
template<typename... Args>
|
||||
reentrancy_bichecked(const void*,const void*,Args&&... args):
|
||||
lck{std::forward<Args>(args)...}{}
|
||||
|
||||
void unlock(){lck.unlock();}
|
||||
|
||||
LockGuard lck;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} /* namespace foa */
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -15,9 +15,7 @@
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/unordered/detail/foa/core.hpp>
|
||||
#include <boost/unordered/detail/serialize_tracked_address.hpp>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
@@ -81,17 +79,12 @@ class table;
|
||||
/* internal conversion from const_iterator to iterator */
|
||||
struct const_iterator_cast_tag{};
|
||||
|
||||
template<typename TypePolicy,typename GroupPtr,bool Const>
|
||||
template<typename TypePolicy,typename Group,bool Const>
|
||||
class table_iterator
|
||||
{
|
||||
using group_pointer_traits=boost::pointer_traits<GroupPtr>;
|
||||
using type_policy=TypePolicy;
|
||||
using table_element_type=typename type_policy::element_type;
|
||||
using group_type=typename group_pointer_traits::element_type;
|
||||
using table_element_pointer=
|
||||
typename group_pointer_traits::template rebind<table_element_type>;
|
||||
using char_pointer=
|
||||
typename group_pointer_traits::template rebind<unsigned char>;
|
||||
using group_type=Group;
|
||||
static constexpr auto N=group_type::N;
|
||||
static constexpr auto regular_layout=group_type::regular_layout;
|
||||
|
||||
@@ -106,24 +99,24 @@ public:
|
||||
using element_type=
|
||||
typename std::conditional<Const,value_type const,value_type>::type;
|
||||
|
||||
table_iterator():pc_{nullptr},p_{nullptr}{};
|
||||
table_iterator()=default;
|
||||
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
|
||||
table_iterator(const table_iterator<TypePolicy,GroupPtr,Const2>& x):
|
||||
pc_{x.pc_},p_{x.p_}{}
|
||||
table_iterator(const table_iterator<TypePolicy,Group,Const2>& x):
|
||||
pc{x.pc},p{x.p}{}
|
||||
table_iterator(
|
||||
const_iterator_cast_tag, const table_iterator<TypePolicy,GroupPtr,true>& x):
|
||||
pc_{x.pc_},p_{x.p_}{}
|
||||
const_iterator_cast_tag, const table_iterator<TypePolicy,Group,true>& x):
|
||||
pc{x.pc},p{x.p}{}
|
||||
|
||||
inline reference operator*()const noexcept
|
||||
{return type_policy::value_from(*p());}
|
||||
{return type_policy::value_from(*p);}
|
||||
inline pointer operator->()const noexcept
|
||||
{return std::addressof(type_policy::value_from(*p()));}
|
||||
{return std::addressof(type_policy::value_from(*p));}
|
||||
inline table_iterator& operator++()noexcept{increment();return *this;}
|
||||
inline table_iterator operator++(int)noexcept
|
||||
{auto x=*this;increment();return x;}
|
||||
friend inline bool operator==(
|
||||
const table_iterator& x,const table_iterator& y)
|
||||
{return x.p()==y.p();}
|
||||
{return x.p==y.p;}
|
||||
friend inline bool operator!=(
|
||||
const table_iterator& x,const table_iterator& y)
|
||||
{return !(x==y);}
|
||||
@@ -133,106 +126,77 @@ private:
|
||||
template<typename> friend class table_erase_return_type;
|
||||
template<typename,typename,typename,typename> friend class table;
|
||||
|
||||
table_iterator(group_type* pg,std::size_t n,const table_element_type* ptet):
|
||||
pc_{to_pointer<char_pointer>(
|
||||
reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n)},
|
||||
p_{to_pointer<table_element_pointer>(const_cast<table_element_type*>(ptet))}
|
||||
{}
|
||||
|
||||
unsigned char* pc()const noexcept{return boost::to_address(pc_);}
|
||||
table_element_type* p()const noexcept{return boost::to_address(p_);}
|
||||
table_iterator(Group* pg,std::size_t n,const table_element_type* p_):
|
||||
pc{reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n},
|
||||
p{const_cast<table_element_type*>(p_)}
|
||||
{}
|
||||
|
||||
inline void increment()noexcept
|
||||
{
|
||||
BOOST_ASSERT(p()!=nullptr);
|
||||
BOOST_ASSERT(p!=nullptr);
|
||||
increment(std::integral_constant<bool,regular_layout>{});
|
||||
}
|
||||
|
||||
inline void increment(std::true_type /* regular layout */)noexcept
|
||||
{
|
||||
using diff_type=
|
||||
typename boost::pointer_traits<char_pointer>::difference_type;
|
||||
|
||||
for(;;){
|
||||
++p_;
|
||||
if(reinterpret_cast<uintptr_t>(pc())%sizeof(group_type)==N-1){
|
||||
pc_+=static_cast<diff_type>(sizeof(group_type)-(N-1));
|
||||
++p;
|
||||
if(reinterpret_cast<uintptr_t>(pc)%sizeof(group_type)==N-1){
|
||||
pc+=sizeof(group_type)-(N-1);
|
||||
break;
|
||||
}
|
||||
++pc_;
|
||||
if(!group_type::is_occupied(pc()))continue;
|
||||
if(BOOST_UNLIKELY(group_type::is_sentinel(pc())))p_=nullptr;
|
||||
++pc;
|
||||
if(!group_type::is_occupied(pc))continue;
|
||||
if(BOOST_UNLIKELY(group_type::is_sentinel(pc)))p=nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
for(;;){
|
||||
int mask=reinterpret_cast<group_type*>(pc())->match_occupied();
|
||||
int mask=reinterpret_cast<group_type*>(pc)->match_occupied();
|
||||
if(mask!=0){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
|
||||
p_=nullptr;
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
|
||||
p=nullptr;
|
||||
}
|
||||
else{
|
||||
pc_+=static_cast<diff_type>(n);
|
||||
p_+=static_cast<diff_type>(n);
|
||||
pc+=n;
|
||||
p+=n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
pc_+=static_cast<diff_type>(sizeof(group_type));
|
||||
p_+=static_cast<diff_type>(N);
|
||||
pc+=sizeof(group_type);
|
||||
p+=N;
|
||||
}
|
||||
}
|
||||
|
||||
inline void increment(std::false_type /* interleaved */)noexcept
|
||||
{
|
||||
using diff_type=
|
||||
typename boost::pointer_traits<char_pointer>::difference_type;
|
||||
|
||||
std::size_t n0=reinterpret_cast<uintptr_t>(pc())%sizeof(group_type);
|
||||
pc_-=static_cast<diff_type>(n0);
|
||||
std::size_t n0=reinterpret_cast<uintptr_t>(pc)%sizeof(group_type);
|
||||
pc-=n0;
|
||||
|
||||
int mask=(
|
||||
reinterpret_cast<group_type*>(pc())->match_occupied()>>(n0+1))<<(n0+1);
|
||||
reinterpret_cast<group_type*>(pc)->match_occupied()>>(n0+1))<<(n0+1);
|
||||
if(!mask){
|
||||
do{
|
||||
pc_+=sizeof(group_type);
|
||||
p_+=N;
|
||||
pc+=sizeof(group_type);
|
||||
p+=N;
|
||||
}
|
||||
while((mask=reinterpret_cast<group_type*>(pc())->match_occupied())==0);
|
||||
while((mask=reinterpret_cast<group_type*>(pc)->match_occupied())==0);
|
||||
}
|
||||
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
|
||||
p_=nullptr;
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
|
||||
p=nullptr;
|
||||
}
|
||||
else{
|
||||
pc_+=static_cast<diff_type>(n);
|
||||
p_-=static_cast<diff_type>(n0);
|
||||
p_+=static_cast<diff_type>(n);
|
||||
pc+=n;
|
||||
p-=n0;
|
||||
p+=n;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
friend void serialization_track(Archive& ar,const table_iterator& x)
|
||||
{
|
||||
if(x.p()){
|
||||
track_address(ar,x.pc_);
|
||||
track_address(ar,x.p_);
|
||||
}
|
||||
}
|
||||
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar,unsigned int)
|
||||
{
|
||||
if(!p())pc_=nullptr;
|
||||
serialize_tracked_address(ar,pc_);
|
||||
serialize_tracked_address(ar,p_);
|
||||
}
|
||||
|
||||
char_pointer pc_=nullptr;
|
||||
table_element_pointer p_=nullptr;
|
||||
unsigned char *pc=nullptr;
|
||||
table_element_type *p=nullptr;
|
||||
};
|
||||
|
||||
/* Returned by table::erase([const_]iterator) to avoid iterator increment
|
||||
@@ -242,11 +206,11 @@ private:
|
||||
template<typename Iterator>
|
||||
class table_erase_return_type;
|
||||
|
||||
template<typename TypePolicy,typename GroupPtr,bool Const>
|
||||
class table_erase_return_type<table_iterator<TypePolicy,GroupPtr,Const>>
|
||||
template<typename TypePolicy,typename Group,bool Const>
|
||||
class table_erase_return_type<table_iterator<TypePolicy,Group,Const>>
|
||||
{
|
||||
using iterator=table_iterator<TypePolicy,GroupPtr,Const>;
|
||||
using const_iterator=table_iterator<TypePolicy,GroupPtr,true>;
|
||||
using iterator=table_iterator<TypePolicy,Group,Const>;
|
||||
using const_iterator=table_iterator<TypePolicy,Group,true>;
|
||||
|
||||
public:
|
||||
/* can't delete it because VS in pre-C++17 mode needs to see it for RVO */
|
||||
@@ -300,9 +264,6 @@ private:
|
||||
* checking is done by boost::unordered_(flat|node)_(map|set).
|
||||
*/
|
||||
|
||||
template<typename,typename,typename,typename>
|
||||
class concurrent_table; /* concurrent/non-concurrent interop */
|
||||
|
||||
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
|
||||
using table_core_impl=
|
||||
table_core<TypePolicy,group15<plain_integral>,table_arrays,
|
||||
@@ -323,15 +284,7 @@ class table:table_core_impl<TypePolicy,Hash,Pred,Allocator>
|
||||
using group_type=typename super::group_type;
|
||||
using super::N;
|
||||
using prober=typename super::prober;
|
||||
using arrays_type=typename super::arrays_type;
|
||||
using size_ctrl_type=typename super::size_ctrl_type;
|
||||
using locator=typename super::locator;
|
||||
using compatible_concurrent_table=
|
||||
concurrent_table<TypePolicy,Hash,Pred,Allocator>;
|
||||
using group_type_pointer=typename boost::pointer_traits<
|
||||
typename boost::allocator_pointer<Allocator>::type
|
||||
>::template rebind<group_type>;
|
||||
friend compatible_concurrent_table;
|
||||
|
||||
public:
|
||||
using key_type=typename super::key_type;
|
||||
@@ -342,6 +295,7 @@ public:
|
||||
private:
|
||||
static constexpr bool has_mutable_iterator=
|
||||
!std::is_same<key_type,value_type>::value;
|
||||
|
||||
public:
|
||||
using hasher=typename super::hasher;
|
||||
using key_equal=typename super::key_equal;
|
||||
@@ -352,10 +306,10 @@ public:
|
||||
using const_reference=typename super::const_reference;
|
||||
using size_type=typename super::size_type;
|
||||
using difference_type=typename super::difference_type;
|
||||
using const_iterator=table_iterator<type_policy,group_type_pointer,true>;
|
||||
using const_iterator=table_iterator<type_policy,group_type,true>;
|
||||
using iterator=typename std::conditional<
|
||||
has_mutable_iterator,
|
||||
table_iterator<type_policy,group_type_pointer,false>,
|
||||
table_iterator<type_policy,group_type,false>,
|
||||
const_iterator>::type;
|
||||
using erase_return_type=table_erase_return_type<iterator>;
|
||||
|
||||
@@ -369,8 +323,6 @@ public:
|
||||
table(table&& x)=default;
|
||||
table(const table& x,const Allocator& al_):super{x,al_}{}
|
||||
table(table&& x,const Allocator& al_):super{std::move(x),al_}{}
|
||||
table(compatible_concurrent_table&& x):
|
||||
table(std::move(x),x.exclusive_access()){}
|
||||
~table()=default;
|
||||
|
||||
table& operator=(const table& x)=default;
|
||||
@@ -380,9 +332,9 @@ public:
|
||||
|
||||
iterator begin()noexcept
|
||||
{
|
||||
iterator it{this->arrays.groups(),0,this->arrays.elements()};
|
||||
if(this->arrays.elements()&&
|
||||
!(this->arrays.groups()[0].match_occupied()&0x1))++it;
|
||||
iterator it{this->arrays.groups,0,this->arrays.elements};
|
||||
if(this->arrays.elements&&
|
||||
!(this->arrays.groups[0].match_occupied()&0x1))++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
@@ -448,7 +400,7 @@ public:
|
||||
BOOST_FORCEINLINE
|
||||
erase_return_type erase(const_iterator pos)noexcept
|
||||
{
|
||||
super::erase(pos.pc(),pos.p());
|
||||
super::erase(pos.pc,pos.p);
|
||||
return {pos};
|
||||
}
|
||||
|
||||
@@ -479,7 +431,7 @@ public:
|
||||
BOOST_ASSERT(pos!=end());
|
||||
erase_on_exit e{*this,pos};
|
||||
(void)e;
|
||||
return std::move(*pos.p());
|
||||
return std::move(*pos.p);
|
||||
}
|
||||
|
||||
// TODO: should we accept different allocator too?
|
||||
@@ -544,31 +496,9 @@ public:
|
||||
friend bool operator!=(const table& x,const table& y){return !(x==y);}
|
||||
|
||||
private:
|
||||
template<typename ArraysType>
|
||||
table(compatible_concurrent_table&& x,arrays_holder<ArraysType,Allocator>&& ah):
|
||||
super{
|
||||
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
|
||||
[&x]{return arrays_type{
|
||||
x.arrays.groups_size_index,x.arrays.groups_size_mask,
|
||||
to_pointer<group_type_pointer>(
|
||||
reinterpret_cast<group_type*>(x.arrays.groups())),
|
||||
x.arrays.elements_};},
|
||||
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
|
||||
{
|
||||
compatible_concurrent_table::arrays_type::delete_group_access(x.al(),x.arrays);
|
||||
x.arrays=ah.release();
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
}
|
||||
|
||||
template<typename ExclusiveLockGuard>
|
||||
table(compatible_concurrent_table&& x,ExclusiveLockGuard):
|
||||
table(std::move(x),x.make_empty_arrays())
|
||||
{}
|
||||
|
||||
struct erase_on_exit
|
||||
{
|
||||
erase_on_exit(table& x_,const_iterator it_):x(x_),it(it_){}
|
||||
erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{}
|
||||
~erase_on_exit(){if(!rollback_)x.erase(it);}
|
||||
|
||||
void rollback(){rollback_=true;}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_TAKES_ARG_AS_CONST_REFERENCE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_TAKES_ARG_AS_CONST_REFERENCE_HPP
|
||||
|
||||
#include <boost/type_traits/make_void.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template<typename Arg,typename R,typename... Args>
|
||||
void check_function_1st_arg(R(*)(Arg,Args...));
|
||||
|
||||
template<typename Arg,typename R,typename... Args>
|
||||
void check_function_1st_arg(R(*)(Arg,Args...,...));
|
||||
|
||||
#define BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(qualifier) \
|
||||
template<typename Arg,typename R,typename C,typename... Args> \
|
||||
void check_memfun_1st_arg(R(C::*)(Arg,Args...)qualifier); \
|
||||
\
|
||||
template<typename Arg,typename R,typename C,typename... Args> \
|
||||
void check_memfun_1st_arg(R(C::*)(Arg,Args...,...)qualifier);
|
||||
|
||||
/* VS warns when a pp function is directly called with an empty arg */
|
||||
#define BOOST_UNORDERED_EMPTY_PP_ARG()
|
||||
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(BOOST_UNORDERED_EMPTY_PP_ARG())
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(volatile)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const volatile)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(&)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const&)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(volatile&)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const volatile&)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(&&)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const&&)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(volatile&&)
|
||||
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const volatile&&)
|
||||
|
||||
#undef BOOST_UNORDERED_EMPTY_PP_ARG
|
||||
#undef BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG
|
||||
|
||||
/* Detects if f(x) takes x as a const reference. From an implementation
|
||||
* technique by Kenneth Gorking.
|
||||
* Requires: F is invocable with an Arg&.
|
||||
*/
|
||||
|
||||
template<typename F,typename Arg,typename=void>
|
||||
struct takes_arg_as_const_reference0:std::false_type{};
|
||||
|
||||
template<typename F,typename Arg>
|
||||
struct takes_arg_as_const_reference0<
|
||||
F,Arg,
|
||||
decltype(check_function_1st_arg<const Arg&>(std::declval<F>()))
|
||||
>:std::true_type{};
|
||||
|
||||
template<typename F,typename Arg>
|
||||
struct takes_arg_as_const_reference0<
|
||||
F,Arg,
|
||||
boost::void_t<
|
||||
decltype(std::declval<F>().operator()(std::declval<Arg&>()))
|
||||
>
|
||||
>:std::true_type{};
|
||||
|
||||
template<typename F,typename Arg,typename=void>
|
||||
struct takes_arg_as_const_reference:takes_arg_as_const_reference0<F,Arg>{};
|
||||
|
||||
template<
|
||||
typename F,typename Arg,
|
||||
typename R=decltype(std::declval<F>()(std::declval<Arg&>())),
|
||||
typename RawF=
|
||||
typename std::remove_cv<typename std::remove_reference<F>::type>::type
|
||||
>
|
||||
decltype(check_memfun_1st_arg<Arg&,R,RawF>(&RawF::operator()))
|
||||
check_operator_call_takes_arg_as_reference();
|
||||
|
||||
template<typename F,typename Arg>
|
||||
struct takes_arg_as_const_reference<
|
||||
F,Arg,
|
||||
decltype(check_operator_call_takes_arg_as_reference<F,Arg>())
|
||||
>:std::false_type{};
|
||||
|
||||
} /* namespace foa */
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,150 @@
|
||||
|
||||
// Copyright (C) 2008-2016 Daniel James.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_FWD_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_FWD_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/predef.h>
|
||||
|
||||
#if defined(BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT)
|
||||
// Already defined.
|
||||
#elif defined(BOOST_LIBSTDCXX11)
|
||||
// https://github.com/gcc-mirror/gcc/blob/gcc-4_6-branch/libstdc++-v3/include/bits/stl_pair.h#L70
|
||||
#if BOOST_LIBSTDCXX_VERSION > 40600
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
|
||||
#endif
|
||||
#elif BOOST_LIB_STD_CXX
|
||||
// https://github.com/llvm-mirror/libcxx/blob/release_30/include/utility#L206
|
||||
#if BOOST_LIB_STD_CXX >= BOOST_VERSION_NUMBER(3, 0, 0)
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
|
||||
#endif
|
||||
#elif defined(BOOST_LIB_STD_DINKUMWARE)
|
||||
// Apparently C++11 standard supported in Visual Studio 2012
|
||||
// https://msdn.microsoft.com/en-us/library/hh567368.aspx#stl
|
||||
// 2012 = VC+11 = BOOST_MSVC 1700 Hopefully!
|
||||
// I have no idea when Dinkumware added it, probably a lot
|
||||
// earlier than this check.
|
||||
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 10, 0) || \
|
||||
BOOST_COMP_MSVC >= BOOST_VERSION_NUMBER(17, 0, 0)
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Assume that an unknown library does not support piecewise construction.
|
||||
#if !defined(BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT)
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 0
|
||||
#endif
|
||||
|
||||
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
|
||||
using std::piecewise_construct_t;
|
||||
using std::piecewise_construct;
|
||||
#else
|
||||
struct piecewise_construct_t
|
||||
{
|
||||
};
|
||||
const piecewise_construct_t piecewise_construct = piecewise_construct_t();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// BOOST_UNORDERED_EMPLACE_LIMIT = The maximum number of parameters in
|
||||
// emplace (not including things like hints). Don't set it to a lower value, as
|
||||
// that might break something.
|
||||
|
||||
#if !defined BOOST_UNORDERED_EMPLACE_LIMIT
|
||||
#define BOOST_UNORDERED_EMPLACE_LIMIT 10
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Configuration
|
||||
//
|
||||
// Unless documented elsewhere these configuration macros should be considered
|
||||
// an implementation detail, I'll try not to break them, but you never know.
|
||||
|
||||
// Use Sun C++ workarounds
|
||||
// I'm not sure which versions of the compiler require these workarounds, so
|
||||
// I'm just using them of everything older than the current test compilers
|
||||
// (as of May 2017).
|
||||
|
||||
#if !defined(BOOST_UNORDERED_SUN_WORKAROUNDS1)
|
||||
#if BOOST_COMP_SUNPRO && BOOST_COMP_SUNPRO < BOOST_VERSION_NUMBER(5, 20, 0)
|
||||
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 1
|
||||
#else
|
||||
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// BOOST_UNORDERED_TUPLE_ARGS
|
||||
//
|
||||
// Maximum number of std::tuple members to support, or 0 if std::tuple
|
||||
// isn't avaiable. More are supported when full C++11 is used.
|
||||
|
||||
// Already defined, so do nothing
|
||||
#if defined(BOOST_UNORDERED_TUPLE_ARGS)
|
||||
|
||||
// Assume if we have C++11 tuple it's properly variadic,
|
||||
// and just use a max number of 10 arguments.
|
||||
#elif !defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 10
|
||||
|
||||
// Visual C++ has a decent enough tuple for piecewise construction,
|
||||
// so use that if available, using _VARIADIC_MAX for the maximum
|
||||
// number of parameters. Note that this comes after the check
|
||||
// for a full C++11 tuple.
|
||||
#elif defined(BOOST_MSVC)
|
||||
#if !BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 0
|
||||
#elif defined(_VARIADIC_MAX)
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS _VARIADIC_MAX
|
||||
#else
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 5
|
||||
#endif
|
||||
|
||||
// Assume that we don't have std::tuple
|
||||
#else
|
||||
#define BOOST_UNORDERED_TUPLE_ARGS 0
|
||||
#endif
|
||||
|
||||
#if BOOST_UNORDERED_TUPLE_ARGS
|
||||
#include <tuple>
|
||||
#endif
|
||||
|
||||
// BOOST_UNORDERED_CXX11_CONSTRUCTION
|
||||
//
|
||||
// Use C++11 construction, requires variadic arguments, good construct support
|
||||
// in allocator_traits and piecewise construction of std::pair
|
||||
// Otherwise allocators aren't used for construction/destruction
|
||||
|
||||
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT && \
|
||||
!defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && BOOST_UNORDERED_TUPLE_ARGS
|
||||
#if BOOST_COMP_SUNPRO && BOOST_LIB_STD_GNU
|
||||
// Sun C++ std::pair piecewise construction doesn't seem to be exception safe.
|
||||
// (At least for Sun C++ 12.5 using libstdc++).
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#elif BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 7, 0)
|
||||
// Piecewise construction in GCC 4.6 doesn't work for uncopyable types.
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#elif !defined(BOOST_NO_CXX11_ALLOCATOR)
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_UNORDERED_CXX11_CONSTRUCTION)
|
||||
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -9,21 +9,21 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP
|
||||
|
||||
#include <boost/unordered/detail/static_assert.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <type_traits>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/type_traits/is_integral.hpp>
|
||||
#include <boost/type_traits/make_unsigned.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
template<typename To,typename From>
|
||||
constexpr To narrow_cast(From x) noexcept
|
||||
BOOST_CONSTEXPR To narrow_cast(From x) BOOST_NOEXCEPT
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT(std::is_integral<From>::value);
|
||||
BOOST_UNORDERED_STATIC_ASSERT(std::is_integral<To>::value);
|
||||
BOOST_UNORDERED_STATIC_ASSERT(sizeof(From)>=sizeof(To));
|
||||
BOOST_STATIC_ASSERT(boost::is_integral<From>::value);
|
||||
BOOST_STATIC_ASSERT(boost::is_integral<To>::value);
|
||||
BOOST_STATIC_ASSERT(sizeof(From)>=sizeof(To));
|
||||
|
||||
return static_cast<To>(
|
||||
x
|
||||
@@ -32,7 +32,7 @@ constexpr To narrow_cast(From x) noexcept
|
||||
/* Avoids VS's "Run-Time Check Failure #1 - A cast to a smaller data type
|
||||
* has caused a loss of data."
|
||||
*/
|
||||
&static_cast<typename std::make_unsigned<To>::type>(~static_cast<To>(0))
|
||||
&static_cast<typename boost::make_unsigned<To>::type>(~static_cast<To>(0))
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
template <class T> union opt_storage
|
||||
{
|
||||
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS T t_;
|
||||
|
||||
opt_storage() {}
|
||||
~opt_storage() {}
|
||||
|
||||
T* address() noexcept { return std::addressof(t_); }
|
||||
T const* address() const noexcept { return std::addressof(t_); }
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2022 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -7,10 +7,13 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/preprocessor/seq/enum.hpp>
|
||||
#include <boost/preprocessor/seq/for_each.hpp>
|
||||
#include <boost/preprocessor/seq/size.hpp>
|
||||
#include <boost/unordered/detail/narrow_cast.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
@@ -34,90 +37,21 @@ namespace boost {
|
||||
namespace detail {
|
||||
template <class = void> struct prime_fmod_size
|
||||
{
|
||||
constexpr static std::size_t const sizes[] = {13ul, 29ul, 53ul, 97ul,
|
||||
193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
|
||||
49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
|
||||
6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul,
|
||||
201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul,
|
||||
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
4294967291ul
|
||||
#else
|
||||
6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull,
|
||||
103079215111ull, 206158430209ull, 412316860441ull, 824633720831ull,
|
||||
1649267441651ull
|
||||
#endif
|
||||
};
|
||||
|
||||
constexpr static std::size_t const sizes_len =
|
||||
sizeof(sizes) / sizeof(sizes[0]);
|
||||
// Because we compile for C++03, we don't have access to any inline
|
||||
// initialization for array data members so the definitions must exist
|
||||
// out-of-line. To keep the library header-only, we introduce a dummy
|
||||
// template parameter which permits the definition to be included in
|
||||
// multiple TUs without conflict.
|
||||
//
|
||||
static std::size_t sizes[];
|
||||
static std::size_t const sizes_len;
|
||||
static std::size_t (*positions[])(std::size_t);
|
||||
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
constexpr static boost::uint64_t const inv_sizes32[] = {
|
||||
1418980313362273202ull, 636094623231363849ull, 348051774975651918ull,
|
||||
190172619316593316ull, 95578984837873325ull, 47420935922132524ull,
|
||||
23987963684927896ull, 11955116055547344ull, 5991147799191151ull,
|
||||
2998982941588287ull, 1501077717772769ull, 750081082979285ull,
|
||||
375261795343686ull, 187625172388393ull, 93822606204624ull,
|
||||
46909513691883ull, 23456218233098ull, 11728086747027ull,
|
||||
5864041509391ull, 2932024948977ull, 1466014921160ull, 733007198436ull,
|
||||
366503839517ull, 183251896093ull, 91625960335ull, 45812983922ull,
|
||||
22906489714ull, 11453246088ull, 5726623060ull};
|
||||
|
||||
constexpr static std::size_t const inv_sizes32_len =
|
||||
sizeof(inv_sizes32) / sizeof(inv_sizes32[0]);
|
||||
static boost::uint64_t inv_sizes32[];
|
||||
static std::size_t const inv_sizes32_len;
|
||||
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
|
||||
|
||||
template <std::size_t SizeIndex, std::size_t Size = sizes[SizeIndex]>
|
||||
static std::size_t position(std::size_t hash)
|
||||
{
|
||||
return hash % Size;
|
||||
}
|
||||
|
||||
constexpr static std::size_t (*positions[])(std::size_t) = {
|
||||
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
position<0, sizes[0]>,
|
||||
position<1, sizes[1]>,
|
||||
position<2, sizes[2]>,
|
||||
position<3, sizes[3]>,
|
||||
position<4, sizes[4]>,
|
||||
position<5, sizes[5]>,
|
||||
position<6, sizes[6]>,
|
||||
position<7, sizes[7]>,
|
||||
position<8, sizes[8]>,
|
||||
position<9, sizes[9]>,
|
||||
position<10, sizes[10]>,
|
||||
position<11, sizes[11]>,
|
||||
position<12, sizes[12]>,
|
||||
position<13, sizes[13]>,
|
||||
position<14, sizes[14]>,
|
||||
position<15, sizes[15]>,
|
||||
position<16, sizes[16]>,
|
||||
position<17, sizes[17]>,
|
||||
position<18, sizes[18]>,
|
||||
position<19, sizes[19]>,
|
||||
position<20, sizes[20]>,
|
||||
position<21, sizes[21]>,
|
||||
position<22, sizes[22]>,
|
||||
position<23, sizes[23]>,
|
||||
position<24, sizes[24]>,
|
||||
position<25, sizes[25]>,
|
||||
position<26, sizes[26]>,
|
||||
position<27, sizes[27]>,
|
||||
position<28, sizes[28]>,
|
||||
position<29, sizes[29]>,
|
||||
#else
|
||||
position<29, sizes[29]>,
|
||||
position<30, sizes[30]>,
|
||||
position<31, sizes[31]>,
|
||||
position<32, sizes[32]>,
|
||||
position<33, sizes[33]>,
|
||||
position<34, sizes[34]>,
|
||||
position<35, sizes[35]>,
|
||||
position<36, sizes[36]>,
|
||||
position<37, sizes[37]>,
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline std::size_t size_index(std::size_t n)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
@@ -134,6 +68,11 @@ namespace boost {
|
||||
return sizes[size_index];
|
||||
}
|
||||
|
||||
template <std::size_t Size> static std::size_t modulo(std::size_t hash)
|
||||
{
|
||||
return hash % Size;
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
// We emulate the techniques taken from:
|
||||
// Faster Remainder by Direct Computation: Applications to Compilers and
|
||||
@@ -179,8 +118,8 @@ namespace boost {
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
std::size_t sizes_under_32bit = inv_sizes32_len;
|
||||
if (BOOST_LIKELY(size_index < sizes_under_32bit)) {
|
||||
return fast_modulo(narrow_cast<boost::uint32_t>(hash) +
|
||||
narrow_cast<boost::uint32_t>(hash >> 32),
|
||||
return fast_modulo(
|
||||
narrow_cast<boost::uint32_t>(hash) + narrow_cast<boost::uint32_t>(hash >> 32),
|
||||
inv_sizes32[size_index], boost::uint32_t(sizes[size_index]));
|
||||
} else {
|
||||
return positions[size_index - sizes_under_32bit](hash);
|
||||
@@ -191,24 +130,134 @@ namespace boost {
|
||||
}
|
||||
}; // prime_fmod_size
|
||||
|
||||
#if defined(BOOST_NO_CXX17_INLINE_VARIABLES)
|
||||
// https://en.cppreference.com/w/cpp/language/static#Constant_static_members
|
||||
// If a const non-inline (since C++17) static data member or a constexpr
|
||||
// static data member (since C++11)(until C++17) is odr-used, a definition
|
||||
// at namespace scope is still required, but it cannot have an
|
||||
// initializer.
|
||||
template <class T> constexpr std::size_t prime_fmod_size<T>::sizes[];
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE \
|
||||
(13ul)(29ul)(53ul)(97ul)(193ul)(389ul)(769ul)(1543ul)(3079ul)(6151ul)( \
|
||||
12289ul)(24593ul)(49157ul)(98317ul)(196613ul)(393241ul)(786433ul)( \
|
||||
1572869ul)(3145739ul)(6291469ul)(12582917ul)(25165843ul)(50331653ul)( \
|
||||
100663319ul)(201326611ul)(402653189ul)(805306457ul)(1610612741ul)( \
|
||||
3221225473ul)
|
||||
|
||||
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE(4294967291ul)
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
|
||||
|
||||
// The original sequence here is this:
|
||||
// (6442450939ul)
|
||||
// (12884901893ul)
|
||||
// (25769803751ul)
|
||||
// (51539607551ul)
|
||||
// (103079215111ul)
|
||||
// (206158430209ul)
|
||||
// (412316860441ul)
|
||||
// (824633720831ul)
|
||||
// (1649267441651ul)
|
||||
//
|
||||
// but this causes problems on versions of mingw where the `long` type is 32
|
||||
// bits, even for 64-bit targets. We work around this by replacing the literals
|
||||
// with compile-time arithmetic, using bitshifts to reconstruct the number.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT \
|
||||
((boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(2147483643ul)) \
|
||||
((boost::ulong_long_type(3ul) << 32) + boost::ulong_long_type(5ul)) \
|
||||
((boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(4294967271ul)) \
|
||||
((boost::ulong_long_type(11ul) << 32) + boost::ulong_long_type(4294967295ul)) \
|
||||
((boost::ulong_long_type(24ul) << 32) + boost::ulong_long_type(7ul)) \
|
||||
((boost::ulong_long_type(48ul) << 32) + boost::ulong_long_type(1ul)) \
|
||||
((boost::ulong_long_type(96ul) << 32) + boost::ulong_long_type(25ul)) \
|
||||
((boost::ulong_long_type(191ul) << 32) + boost::ulong_long_type(4294967295ul)) \
|
||||
((boost::ulong_long_type(383ul) << 32) + boost::ulong_long_type(4294967283ul))
|
||||
// clang-format on
|
||||
|
||||
#endif /* BOOST_UNORDERED_FCA_HAS_64B_SIZE_T */
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES \
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
|
||||
|
||||
template <class T>
|
||||
std::size_t prime_fmod_size<T>::sizes[] = {
|
||||
BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIME_FMOD_SIZES)};
|
||||
|
||||
template <class T>
|
||||
std::size_t const prime_fmod_size<T>::sizes_len = BOOST_PP_SEQ_SIZE(
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES);
|
||||
|
||||
// Similarly here, we have to re-express the integer initialization using
|
||||
// arithmetic such that each literal can fit in a 32-bit value.
|
||||
//
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
template <class T>
|
||||
constexpr boost::uint64_t prime_fmod_size<T>::inv_sizes32[];
|
||||
#endif
|
||||
// clang-format off
|
||||
template <class T>
|
||||
boost::uint64_t prime_fmod_size<T>::inv_sizes32[] = {
|
||||
(boost::ulong_long_type(330382099ul) << 32) + boost::ulong_long_type(2973438898ul) /* = 1418980313362273202 */,
|
||||
(boost::ulong_long_type(148102320ul) << 32) + boost::ulong_long_type(2369637129ul) /* = 636094623231363849 */,
|
||||
(boost::ulong_long_type(81037118ul) << 32) + boost::ulong_long_type(3403558990ul) /* = 348051774975651918 */,
|
||||
(boost::ulong_long_type(44278013ul) << 32) + boost::ulong_long_type(1549730468ul) /* = 190172619316593316 */,
|
||||
(boost::ulong_long_type(22253716ul) << 32) + boost::ulong_long_type(2403401389ul) /* = 95578984837873325 */,
|
||||
(boost::ulong_long_type(11041047ul) << 32) + boost::ulong_long_type(143533612ul) /* = 47420935922132524 */,
|
||||
(boost::ulong_long_type(5585133ul) << 32) + boost::ulong_long_type(106117528ul) /* = 23987963684927896 */,
|
||||
(boost::ulong_long_type(2783517ul) << 32) + boost::ulong_long_type(1572687312ul) /* = 11955116055547344 */,
|
||||
(boost::ulong_long_type(1394922ul) << 32) + boost::ulong_long_type(3428720239ul) /* = 5991147799191151 */,
|
||||
(boost::ulong_long_type(698255ul) << 32) + boost::ulong_long_type(552319807ul) /* = 2998982941588287 */,
|
||||
(boost::ulong_long_type(349496ul) << 32) + boost::ulong_long_type(3827689953ul) /* = 1501077717772769 */,
|
||||
(boost::ulong_long_type(174641ul) << 32) + boost::ulong_long_type(3699438549ul) /* = 750081082979285 */,
|
||||
(boost::ulong_long_type(87372ul) << 32) + boost::ulong_long_type(1912757574ul) /* = 375261795343686 */,
|
||||
(boost::ulong_long_type(43684ul) << 32) + boost::ulong_long_type(3821029929ul) /* = 187625172388393 */,
|
||||
(boost::ulong_long_type(21844ul) << 32) + boost::ulong_long_type(3340590800ul) /* = 93822606204624 */,
|
||||
(boost::ulong_long_type(10921ul) << 32) + boost::ulong_long_type(4175852267ul) /* = 46909513691883 */,
|
||||
(boost::ulong_long_type(5461ul) << 32) + boost::ulong_long_type(1401829642ul) /* = 23456218233098 */,
|
||||
(boost::ulong_long_type(2730ul) << 32) + boost::ulong_long_type(2826028947ul) /* = 11728086747027 */,
|
||||
(boost::ulong_long_type(1365ul) << 32) + boost::ulong_long_type(1411150351ul) /* = 5864041509391 */,
|
||||
(boost::ulong_long_type(682ul) << 32) + boost::ulong_long_type(2857253105ul) /* = 2932024948977 */,
|
||||
(boost::ulong_long_type(341ul) << 32) + boost::ulong_long_type(1431073224ul) /* = 1466014921160 */,
|
||||
(boost::ulong_long_type(170ul) << 32) + boost::ulong_long_type(2862758116ul) /* = 733007198436 */,
|
||||
(boost::ulong_long_type(85ul) << 32) + boost::ulong_long_type(1431619357ul) /* = 366503839517 */,
|
||||
(boost::ulong_long_type(42ul) << 32) + boost::ulong_long_type(2863269661ul) /* = 183251896093 */,
|
||||
(boost::ulong_long_type(21ul) << 32) + boost::ulong_long_type(1431647119ul) /* = 91625960335 */,
|
||||
(boost::ulong_long_type(10ul) << 32) + boost::ulong_long_type(2863310962ul) /* = 45812983922 */,
|
||||
(boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(1431653234ul) /* = 22906489714 */,
|
||||
(boost::ulong_long_type(2ul) << 32) + boost::ulong_long_type(2863311496ul) /* = 11453246088 */,
|
||||
(boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(1431655764ul) /* = 5726623060 */,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
template <class T>
|
||||
constexpr std::size_t (*prime_fmod_size<T>::positions[])(std::size_t);
|
||||
std::size_t const
|
||||
prime_fmod_size<T>::inv_sizes32_len = sizeof(inv_sizes32) /
|
||||
sizeof(inv_sizes32[0]);
|
||||
|
||||
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT(z, _, n) \
|
||||
prime_fmod_size<T>::template modulo<n>,
|
||||
|
||||
template <class T>
|
||||
std::size_t (*prime_fmod_size<T>::positions[])(std::size_t) = {
|
||||
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT)
|
||||
#else
|
||||
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT)
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
|
||||
// Copyright 2023 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \
|
||||
defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \
|
||||
defined(BOOST_NO_CXX11_DECLTYPE) || \
|
||||
defined(BOOST_NO_CXX11_CONSTEXPR) || \
|
||||
defined(BOOST_NO_CXX11_NOEXCEPT)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Unordered 1.82 and will be removed in Boost.Unordered 1.84.")
|
||||
|
||||
#endif
|
||||
|
||||
#endif // #ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
@@ -1,74 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_SERIALIZATION_VERSION_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_SERIALIZATION_VERSION_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
/* boost::serialization::load_construct_adl(ar,t,version) requires user code
|
||||
* to pass the serialization version for t, when this information is really
|
||||
* stored in the archive. serialization_version<T> circumvents this design
|
||||
* error by acting as a regular serializable type with the same serialization
|
||||
* version as T; loading/saving serialization_version<T> does nothing with
|
||||
* the archive data itself but captures the stored serialization version
|
||||
* at load() time.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
struct serialization_version
|
||||
{
|
||||
serialization_version():
|
||||
value(boost::serialization::version<serialization_version>::value){}
|
||||
|
||||
serialization_version& operator=(unsigned int x){value=x;return *this;};
|
||||
|
||||
operator unsigned int()const{return value;}
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar,unsigned int version)
|
||||
{
|
||||
core::split_member(ar,*this,version);
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
void save(Archive&,unsigned int)const{}
|
||||
|
||||
template<class Archive>
|
||||
void load(Archive&,unsigned int version)
|
||||
{
|
||||
this->value=version;
|
||||
}
|
||||
|
||||
unsigned int value;
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
|
||||
namespace serialization{
|
||||
|
||||
template<typename T>
|
||||
struct version<boost::unordered::detail::serialization_version<T> >
|
||||
{
|
||||
BOOST_STATIC_CONSTANT(int,value=version<T>::value);
|
||||
};
|
||||
|
||||
} /* namespace serialization */
|
||||
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,204 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_SERIALIZE_CONTAINER_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_SERIALIZE_CONTAINER_HPP
|
||||
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/unordered/detail/archive_constructed.hpp>
|
||||
#include <boost/unordered/detail/bad_archive_exception.hpp>
|
||||
#include <boost/unordered/detail/serialization_version.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
/* serialize_container(ar,x,v) serializes any of the unordered associative
|
||||
* containers in Boost.Unordered. Iterator serialization is also supported
|
||||
* through the following protocol:
|
||||
* - At saving time, for each iterator it in [x.begin(),x.end()),
|
||||
* serialization_track(ar,it) is ADL-called to instruct the archive to
|
||||
* track the positions internally pointed to by the iterator via
|
||||
* track_address().
|
||||
* - At loading time, these addresses are mapped to those of the equivalent
|
||||
* reconstructed positions using again serialization_track(ar,it).
|
||||
* - Serializing an iterator reduces to serializing pointers to previously
|
||||
* tracked addresses via serialize_address().
|
||||
*/
|
||||
|
||||
template<typename Iterator>
|
||||
std::pair<Iterator,bool> adapt_insert_return_type(Iterator it)
|
||||
{
|
||||
return std::pair<Iterator,bool>(it,true);
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
std::pair<Iterator,bool> adapt_insert_return_type(std::pair<Iterator,bool> p)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
template<typename Set,bool IsSaving> struct load_or_save_unordered_set;
|
||||
|
||||
template<typename Set> struct load_or_save_unordered_set<Set,true> /* save */
|
||||
{
|
||||
template<typename Archive>
|
||||
void operator()(Archive& ar,const Set& x,unsigned int)const
|
||||
{
|
||||
typedef typename Set::value_type value_type;
|
||||
typedef typename Set::const_iterator const_iterator;
|
||||
|
||||
const std::size_t s=x.size();
|
||||
const serialization_version<value_type> value_version;
|
||||
|
||||
ar<<core::make_nvp("count",s);
|
||||
ar<<core::make_nvp("value_version",value_version);
|
||||
|
||||
for(const_iterator first=x.begin(),last=x.end();first!=last;++first){
|
||||
core::save_construct_data_adl(ar,std::addressof(*first),value_version);
|
||||
ar<<core::make_nvp("item",*first);
|
||||
serialization_track(ar,first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Set> struct load_or_save_unordered_set<Set,false> /* load */
|
||||
{
|
||||
template<typename Archive>
|
||||
void operator()(Archive& ar,Set& x,unsigned int)const
|
||||
{
|
||||
typedef typename Set::value_type value_type;
|
||||
typedef typename Set::iterator iterator;
|
||||
|
||||
std::size_t s;
|
||||
serialization_version<value_type> value_version;
|
||||
|
||||
ar>>core::make_nvp("count",s);
|
||||
ar>>core::make_nvp("value_version",value_version);
|
||||
|
||||
x.clear();
|
||||
x.reserve(s); /* critical so that iterator tracking is stable */
|
||||
|
||||
for(std::size_t n=0;n<s;++n){
|
||||
archive_constructed<value_type> value("item",ar,value_version);
|
||||
|
||||
std::pair<iterator,bool> p=adapt_insert_return_type(
|
||||
x.insert(std::move(value.get())));
|
||||
if(!p.second)throw_exception(bad_archive_exception());
|
||||
ar.reset_object_address(
|
||||
std::addressof(*p.first),std::addressof(value.get()));
|
||||
serialization_track(ar,p.first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Map,bool IsSaving> struct load_or_save_unordered_map;
|
||||
|
||||
template<typename Map> struct load_or_save_unordered_map<Map,true> /* save */
|
||||
{
|
||||
template<typename Archive>
|
||||
void operator()(Archive& ar,const Map& x,unsigned int)const
|
||||
{
|
||||
typedef typename std::remove_const<
|
||||
typename Map::key_type>::type key_type;
|
||||
typedef typename std::remove_const<
|
||||
typename Map::mapped_type>::type mapped_type;
|
||||
typedef typename Map::const_iterator const_iterator;
|
||||
|
||||
const std::size_t s=x.size();
|
||||
const serialization_version<key_type> key_version;
|
||||
const serialization_version<mapped_type> mapped_version;
|
||||
|
||||
ar<<core::make_nvp("count",s);
|
||||
ar<<core::make_nvp("key_version",key_version);
|
||||
ar<<core::make_nvp("mapped_version",mapped_version);
|
||||
|
||||
for(const_iterator first=x.begin(),last=x.end();first!=last;++first){
|
||||
/* To remain lib-independent from Boost.Serialization and not rely on
|
||||
* the user having included the serialization code for std::pair
|
||||
* (boost/serialization/utility.hpp), we serialize the key and the
|
||||
* mapped value separately.
|
||||
*/
|
||||
|
||||
core::save_construct_data_adl(
|
||||
ar,std::addressof(first->first),key_version);
|
||||
ar<<core::make_nvp("key",first->first);
|
||||
core::save_construct_data_adl(
|
||||
ar,std::addressof(first->second),mapped_version);
|
||||
ar<<core::make_nvp("mapped",first->second);
|
||||
serialization_track(ar,first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Map> struct load_or_save_unordered_map<Map,false> /* load */
|
||||
{
|
||||
template<typename Archive>
|
||||
void operator()(Archive& ar,Map& x,unsigned int)const
|
||||
{
|
||||
typedef typename std::remove_const<
|
||||
typename Map::key_type>::type key_type;
|
||||
typedef typename std::remove_const<
|
||||
typename Map::mapped_type>::type mapped_type;
|
||||
typedef typename Map::iterator iterator;
|
||||
|
||||
std::size_t s;
|
||||
serialization_version<key_type> key_version;
|
||||
serialization_version<mapped_type> mapped_version;
|
||||
|
||||
ar>>core::make_nvp("count",s);
|
||||
ar>>core::make_nvp("key_version",key_version);
|
||||
ar>>core::make_nvp("mapped_version",mapped_version);
|
||||
|
||||
x.clear();
|
||||
x.reserve(s); /* critical so that iterator tracking is stable */
|
||||
|
||||
for(std::size_t n=0;n<s;++n){
|
||||
archive_constructed<key_type> key("key",ar,key_version);
|
||||
archive_constructed<mapped_type> mapped("mapped",ar,mapped_version);
|
||||
|
||||
std::pair<iterator,bool> p=adapt_insert_return_type(
|
||||
x.emplace(std::move(key.get()),std::move(mapped.get())));
|
||||
if(!p.second)throw_exception(bad_archive_exception());
|
||||
ar.reset_object_address(
|
||||
std::addressof(p.first->first),std::addressof(key.get()));
|
||||
ar.reset_object_address(
|
||||
std::addressof(p.first->second),std::addressof(mapped.get()));
|
||||
serialization_track(ar,p.first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Container,bool IsSet,bool IsSaving>
|
||||
struct load_or_save_container;
|
||||
|
||||
template<typename Set,bool IsSaving>
|
||||
struct load_or_save_container<Set,true,IsSaving>:
|
||||
load_or_save_unordered_set<Set,IsSaving>{};
|
||||
|
||||
template<typename Map,bool IsSaving>
|
||||
struct load_or_save_container<Map,false,IsSaving>:
|
||||
load_or_save_unordered_map<Map,IsSaving>{};
|
||||
|
||||
template<typename Archive,typename Container>
|
||||
void serialize_container(Archive& ar,Container& x,unsigned int version)
|
||||
{
|
||||
load_or_save_container<
|
||||
Container,
|
||||
std::is_same<
|
||||
typename Container::key_type,typename Container::value_type>::value,
|
||||
Archive::is_saving::value>()(ar,x,version);
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,156 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_SERIALIZE_FCA_CONTAINER_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_SERIALIZE_FCA_CONTAINER_HPP
|
||||
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
|
||||
|
||||
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
|
||||
<boost/serialization/archive_input_unordered_map.hpp>
|
||||
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
|
||||
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
|
||||
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
|
||||
<boost/serialization/archive_input_unordered_set.hpp>
|
||||
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
|
||||
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
|
||||
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
|
||||
<boost/serialization/unordered_collections_load_imp.hpp>
|
||||
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
|
||||
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
|
||||
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
|
||||
<boost/serialization/utility.hpp>
|
||||
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
|
||||
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
|
||||
|
||||
#include <boost/unordered/unordered_map_fwd.hpp>
|
||||
#include <boost/unordered/unordered_set_fwd.hpp>
|
||||
|
||||
#else
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
#endif
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
/* Support for boost::unordered_[multi](map|set) loading from legacy archives.
|
||||
* Until Boost 1.84, serialization of these containers was provided from
|
||||
* Boost.Serialization via boost/serialization/boost_unordered_(map|set).hpp,
|
||||
* from that release on support is native in Boost.Unordered. To enable legacy
|
||||
* archive loading, BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
|
||||
* must be defined (it implies header dependency from Boost.Serialization).
|
||||
*/
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
|
||||
|
||||
template<typename Archive,typename Container>
|
||||
struct archive_input;
|
||||
|
||||
template<
|
||||
typename Archive,typename K,typename T,typename H,typename P,typename A
|
||||
>
|
||||
struct archive_input<Archive,boost::unordered_map<K,T,H,P,A> >:
|
||||
boost::serialization::stl::archive_input_unordered_map<
|
||||
Archive,
|
||||
boost::unordered_map<K,T,H,P,A>
|
||||
>
|
||||
{};
|
||||
|
||||
template<
|
||||
typename Archive,typename K,typename T,typename H,typename P,typename A
|
||||
>
|
||||
struct archive_input<Archive,boost::unordered_multimap<K,T,H,P,A> >:
|
||||
boost::serialization::stl::archive_input_unordered_multimap<
|
||||
Archive,
|
||||
boost::unordered_multimap<K,T,H,P,A>
|
||||
>
|
||||
{};
|
||||
|
||||
template<
|
||||
typename Archive,typename K,typename H,typename P,typename A
|
||||
>
|
||||
struct archive_input<Archive,boost::unordered_set<K,H,P,A> >:
|
||||
boost::serialization::stl::archive_input_unordered_set<
|
||||
Archive,
|
||||
boost::unordered_set<K,H,P,A>
|
||||
>
|
||||
{};
|
||||
|
||||
template<
|
||||
typename Archive,typename K,typename H,typename P,typename A
|
||||
>
|
||||
struct archive_input<Archive,boost::unordered_multiset<K,H,P,A> >:
|
||||
boost::serialization::stl::archive_input_unordered_multiset<
|
||||
Archive,
|
||||
boost::unordered_multiset<K,H,P,A>
|
||||
>
|
||||
{};
|
||||
|
||||
#else
|
||||
|
||||
struct legacy_archive_exception:std::runtime_error
|
||||
{
|
||||
legacy_archive_exception():std::runtime_error(
|
||||
"Legacy archive detected, define "
|
||||
"BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0 to load"){}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
template<typename Container,bool IsSaving>
|
||||
struct load_or_save_fca_container;
|
||||
|
||||
template<typename Container>
|
||||
struct load_or_save_fca_container<Container,true> /* save */
|
||||
{
|
||||
template<typename Archive>
|
||||
void operator()(Archive& ar,Container& x,unsigned int version)const
|
||||
{
|
||||
serialize_container(ar,x,version);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Container>
|
||||
struct load_or_save_fca_container<Container,false> /* load */
|
||||
{
|
||||
template<typename Archive>
|
||||
void operator()(Archive& ar,Container& x,unsigned int version)const
|
||||
{
|
||||
if(version==0){
|
||||
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
|
||||
boost::serialization::stl::load_unordered_collection<
|
||||
Archive,Container,archive_input<Archive,Container>
|
||||
>(ar,x);
|
||||
#else
|
||||
throw_exception(legacy_archive_exception());
|
||||
#endif
|
||||
}
|
||||
else{
|
||||
serialize_container(ar,x,version);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Archive,typename Container>
|
||||
void serialize_fca_container(Archive& ar,Container& x,unsigned int version)
|
||||
{
|
||||
load_or_save_fca_container<Container,Archive::is_saving::value>()(
|
||||
ar,x,version);
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,104 +0,0 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_SERIALIZE_TRACKED_ADDRESS_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_SERIALIZE_TRACKED_ADDRESS_HPP
|
||||
|
||||
#include <boost/unordered/detail/bad_archive_exception.hpp>
|
||||
|
||||
#include <boost/core/pointer_traits.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
|
||||
/* Tracked address serialization to support iterator serialization as described
|
||||
* in serialize_container.hpp. The underlying technique is to reinterpret_cast
|
||||
* T pointers to serialization_tracker<T> pointers, which, when dereferenced
|
||||
* and serialized, do not emit any serialization payload to the
|
||||
* archive, but activate object tracking on the relevant addresses for later
|
||||
* use with serialize_tracked_address().
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
struct serialization_tracker
|
||||
{
|
||||
/* An attempt to construct a serialization_tracker means a stray address
|
||||
* in the archive, that is, one without a previously tracked address.
|
||||
*/
|
||||
serialization_tracker(){throw_exception(bad_archive_exception());}
|
||||
|
||||
template<typename Archive>
|
||||
void serialize(Archive&,unsigned int){} /* no data emitted */
|
||||
};
|
||||
|
||||
template<typename Archive,typename Ptr>
|
||||
void track_address(Archive& ar,Ptr p)
|
||||
{
|
||||
typedef typename boost::pointer_traits<Ptr> ptr_traits;
|
||||
typedef typename std::remove_const<
|
||||
typename ptr_traits::element_type>::type element_type;
|
||||
|
||||
if(p){
|
||||
ar&core::make_nvp(
|
||||
"address",
|
||||
*reinterpret_cast<serialization_tracker<element_type>*>(
|
||||
const_cast<element_type*>(
|
||||
boost::to_address(p))));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Archive,typename Ptr>
|
||||
void serialize_tracked_address(Archive& ar,Ptr& p,std::true_type /* save */)
|
||||
{
|
||||
typedef typename boost::pointer_traits<Ptr> ptr_traits;
|
||||
typedef typename std::remove_const<
|
||||
typename ptr_traits::element_type>::type element_type;
|
||||
typedef serialization_tracker<element_type> tracker;
|
||||
|
||||
tracker* pt=
|
||||
const_cast<tracker*>(
|
||||
reinterpret_cast<const tracker*>(
|
||||
const_cast<const element_type*>(
|
||||
boost::to_address(p))));
|
||||
ar<<core::make_nvp("pointer",pt);
|
||||
}
|
||||
|
||||
template<typename Archive,typename Ptr>
|
||||
void serialize_tracked_address(Archive& ar,Ptr& p,std::false_type /* load */)
|
||||
{
|
||||
typedef typename boost::pointer_traits<Ptr> ptr_traits;
|
||||
typedef typename std::remove_const<
|
||||
typename ptr_traits::element_type>::type element_type;
|
||||
typedef serialization_tracker<element_type> tracker;
|
||||
|
||||
tracker* pt;
|
||||
ar>>core::make_nvp("pointer",pt);
|
||||
element_type* pn=const_cast<element_type*>(
|
||||
reinterpret_cast<const element_type*>(
|
||||
const_cast<const tracker*>(pt)));
|
||||
p=pn?ptr_traits::pointer_to(*pn):0;
|
||||
}
|
||||
|
||||
template<typename Archive,typename Ptr>
|
||||
void serialize_tracked_address(Archive& ar,Ptr& p)
|
||||
{
|
||||
serialize_tracked_address(
|
||||
ar,p,
|
||||
std::integral_constant<bool,Archive::is_saving::value>());
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#endif
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright 2023 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT(...) \
|
||||
static_assert(__VA_ARGS__, #__VA_ARGS__)
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -11,14 +11,19 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/type_traits/is_convertible.hpp>
|
||||
#include <boost/type_traits/make_void.hpp>
|
||||
#include <boost/type_traits/type_identity.hpp>
|
||||
|
||||
#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
|
||||
#include <iterator>
|
||||
#endif
|
||||
#include <boost/type_traits/enable_if.hpp>
|
||||
#include <boost/type_traits/is_integral.hpp>
|
||||
#include <boost/type_traits/remove_const.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
// BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
|
||||
@@ -35,96 +40,19 @@
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
|
||||
template <class T> struct type_identity
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename... Ts> struct make_void
|
||||
{
|
||||
typedef void type;
|
||||
};
|
||||
|
||||
template <typename... Ts> using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
|
||||
/* std::is_trivially_default_constructible not provided */
|
||||
template <class T>
|
||||
struct is_trivially_default_constructible
|
||||
: public std::integral_constant<bool,
|
||||
std::is_default_constructible<T>::value &&
|
||||
std::has_trivial_default_constructor<T>::value>
|
||||
{
|
||||
};
|
||||
#else
|
||||
using std::is_trivially_default_constructible;
|
||||
#endif
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
|
||||
/* std::is_trivially_copy_constructible not provided */
|
||||
template <class T>
|
||||
struct is_trivially_copy_constructible
|
||||
: public std::integral_constant<bool,
|
||||
std::is_copy_constructible<T>::value &&
|
||||
std::has_trivial_copy_constructor<T>::value>
|
||||
{
|
||||
};
|
||||
#else
|
||||
using std::is_trivially_copy_constructible;
|
||||
#endif
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
|
||||
/* std::is_trivially_copy_assignable not provided */
|
||||
template <class T>
|
||||
struct is_trivially_copy_assignable
|
||||
: public std::integral_constant<bool,
|
||||
std::is_copy_assignable<T>::value &&
|
||||
std::has_trivial_copy_assign<T>::value>
|
||||
{
|
||||
};
|
||||
#else
|
||||
using std::is_trivially_copy_assignable;
|
||||
#endif
|
||||
|
||||
namespace type_traits_detail {
|
||||
using std::swap;
|
||||
|
||||
template <class T, class = void> struct is_nothrow_swappable_helper
|
||||
{
|
||||
constexpr static bool const value = false;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct is_nothrow_swappable_helper<T,
|
||||
void_t<decltype(swap(std::declval<T&>(), std::declval<T&>()))> >
|
||||
{
|
||||
constexpr static bool const value =
|
||||
noexcept(swap(std::declval<T&>(), std::declval<T&>()));
|
||||
};
|
||||
|
||||
} // namespace type_traits_detail
|
||||
|
||||
template <class T>
|
||||
struct is_nothrow_swappable
|
||||
: public std::integral_constant<bool,
|
||||
type_traits_detail::is_nothrow_swappable_helper<T>::value>
|
||||
{
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Type checkers used for the transparent member functions added by C++20
|
||||
// and up
|
||||
|
||||
template <class, class = void>
|
||||
struct is_transparent : public std::false_type
|
||||
struct is_transparent : public boost::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct is_transparent<T,
|
||||
boost::unordered::detail::void_t<typename T::is_transparent> >
|
||||
: public std::true_type
|
||||
typename boost::make_void<typename T::is_transparent>::type>
|
||||
: public boost::true_type
|
||||
{
|
||||
};
|
||||
|
||||
@@ -143,8 +71,8 @@ namespace boost {
|
||||
|
||||
static bool const value =
|
||||
are_transparent<Key, hash, key_equal>::value &&
|
||||
!std::is_convertible<Key, iterator>::value &&
|
||||
!std::is_convertible<Key, const_iterator>::value;
|
||||
!boost::is_convertible<Key, iterator>::value &&
|
||||
!boost::is_convertible<Key, const_iterator>::value;
|
||||
};
|
||||
|
||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
@@ -153,7 +81,7 @@ namespace boost {
|
||||
|
||||
template <class InputIterator>
|
||||
constexpr bool const is_input_iterator_v =
|
||||
!std::is_integral<InputIterator>::value;
|
||||
!boost::is_integral<InputIterator>::value;
|
||||
|
||||
template <class A, class = void> struct is_allocator
|
||||
{
|
||||
@@ -162,7 +90,7 @@ namespace boost {
|
||||
|
||||
template <class A>
|
||||
struct is_allocator<A,
|
||||
boost::unordered::detail::void_t<typename A::value_type,
|
||||
boost::void_t<typename A::value_type,
|
||||
decltype(std::declval<A&>().allocate(std::size_t{}))> >
|
||||
{
|
||||
constexpr static bool const value = true;
|
||||
@@ -173,7 +101,7 @@ namespace boost {
|
||||
|
||||
template <class H>
|
||||
constexpr bool const is_hash_v =
|
||||
!std::is_integral<H>::value && !is_allocator_v<H>;
|
||||
!boost::is_integral<H>::value && !is_allocator_v<H>;
|
||||
|
||||
template <class P> constexpr bool const is_pred_v = !is_allocator_v<P>;
|
||||
|
||||
@@ -188,7 +116,7 @@ namespace boost {
|
||||
typename std::pair<iter_key_t<T> const, iter_val_t<T> >;
|
||||
#endif
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP
|
||||
#define BOOST_UNORDERED_HASH_TRAITS_HPP
|
||||
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/type_traits/make_void.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
@@ -19,12 +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,
|
||||
boost::unordered::detail::void_t<typename Hash::is_avalanching> >:
|
||||
std::true_type{};
|
||||
typename boost::make_void<typename Hash::is_avalanching>::type>:
|
||||
boost::true_type{};
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -10,15 +10,13 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/detail/foa/flat_map_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_map_fwd.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
@@ -38,10 +36,6 @@ namespace boost {
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_flat_map
|
||||
{
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class concurrent_flat_map;
|
||||
|
||||
using map_types = detail::foa::flat_map_types<Key, T>;
|
||||
|
||||
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
|
||||
@@ -65,9 +59,9 @@ namespace boost {
|
||||
using init_type = typename map_types::init_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
|
||||
using key_equal = typename boost::unordered::detail::type_identity<KeyEqual>::type;
|
||||
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
|
||||
using hasher = typename boost::type_identity<Hash>::type;
|
||||
using key_equal = typename boost::type_identity<KeyEqual>::type;
|
||||
using allocator_type = typename boost::type_identity<Allocator>::type;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
@@ -141,7 +135,9 @@ namespace boost {
|
||||
}
|
||||
|
||||
unordered_flat_map(unordered_flat_map&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<table_type>::value)
|
||||
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
|
||||
std::is_nothrow_move_constructible<key_equal>::value&&
|
||||
std::is_nothrow_move_constructible<allocator_type>::value)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
@@ -177,12 +173,6 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
unordered_flat_map(
|
||||
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~unordered_flat_map() = default;
|
||||
|
||||
unordered_flat_map& operator=(unordered_flat_map const& other)
|
||||
@@ -695,15 +685,6 @@ namespace boost {
|
||||
return erase_if(map.table_, pred);
|
||||
}
|
||||
|
||||
template <class Archive, class Key, class T, class Hash, class KeyEqual,
|
||||
class Allocator>
|
||||
void serialize(Archive& ar,
|
||||
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& map,
|
||||
unsigned int version)
|
||||
{
|
||||
detail::serialize_container(ar, map, version);
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) /* C4714 */
|
||||
#endif
|
||||
@@ -717,10 +698,10 @@ namespace boost {
|
||||
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
class Allocator = std::allocator<
|
||||
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_map(InputIterator, InputIterator,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
@@ -729,21 +710,21 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class Key, class T,
|
||||
class Hash = boost::hash<std::remove_const_t<Key> >,
|
||||
class Pred = std::equal_to<std::remove_const_t<Key> >,
|
||||
class Hash = boost::hash<boost::remove_const_t<Key> >,
|
||||
class Pred = std::equal_to<boost::remove_const_t<Key> >,
|
||||
class Allocator = std::allocator<std::pair<const Key, T> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_map(std::initializer_list<std::pair<Key, T> >,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> unordered_flat_map<std::remove_const_t<Key>, T, Hash, Pred,
|
||||
-> unordered_flat_map<boost::remove_const_t<Key>, T, Hash, Pred,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_map(InputIterator, InputIterator, std::size_t, Allocator)
|
||||
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
boost::unordered::detail::iter_val_t<InputIterator>,
|
||||
@@ -752,8 +733,8 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_map(InputIterator, InputIterator, Allocator)
|
||||
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
boost::unordered::detail::iter_val_t<InputIterator>,
|
||||
@@ -762,9 +743,9 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_map(
|
||||
InputIterator, InputIterator, std::size_t, Hash, Allocator)
|
||||
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
@@ -773,25 +754,25 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class Key, class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
|
||||
Allocator) -> unordered_flat_map<std::remove_const_t<Key>, T,
|
||||
boost::hash<std::remove_const_t<Key> >,
|
||||
std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
Allocator) -> unordered_flat_map<boost::remove_const_t<Key>, T,
|
||||
boost::hash<boost::remove_const_t<Key> >,
|
||||
std::equal_to<boost::remove_const_t<Key> >, Allocator>;
|
||||
|
||||
template <class Key, class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, Allocator)
|
||||
-> unordered_flat_map<std::remove_const_t<Key>, T,
|
||||
boost::hash<std::remove_const_t<Key> >,
|
||||
std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
-> unordered_flat_map<boost::remove_const_t<Key>, T,
|
||||
boost::hash<boost::remove_const_t<Key> >,
|
||||
std::equal_to<boost::remove_const_t<Key> >, Allocator>;
|
||||
|
||||
template <class Key, class T, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
|
||||
Hash, Allocator) -> unordered_flat_map<std::remove_const_t<Key>, T,
|
||||
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
Hash, Allocator) -> unordered_flat_map<boost::remove_const_t<Key>, T,
|
||||
Hash, std::equal_to<boost::remove_const_t<Key> >, Allocator>;
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -10,15 +10,13 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <boost/unordered/detail/foa/flat_set_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_flat_set_fwd.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
@@ -36,9 +34,6 @@ namespace boost {
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_flat_set
|
||||
{
|
||||
template <class Key2, class Hash2, class KeyEqual2, class Allocator2>
|
||||
friend class concurrent_flat_set;
|
||||
|
||||
using set_types = detail::foa::flat_set_types<Key>;
|
||||
|
||||
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
|
||||
@@ -137,7 +132,9 @@ namespace boost {
|
||||
}
|
||||
|
||||
unordered_flat_set(unordered_flat_set&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<table_type>::value)
|
||||
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
|
||||
std::is_nothrow_move_constructible<key_equal>::value&&
|
||||
std::is_nothrow_move_constructible<allocator_type>::value)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
@@ -173,12 +170,6 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
unordered_flat_set(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~unordered_flat_set() = default;
|
||||
|
||||
unordered_flat_set& operator=(unordered_flat_set const& other)
|
||||
@@ -514,15 +505,6 @@ namespace boost {
|
||||
return erase_if(set.table_, pred);
|
||||
}
|
||||
|
||||
template <class Archive, class Key, class Hash, class KeyEqual,
|
||||
class Allocator>
|
||||
void serialize(Archive& ar,
|
||||
unordered_flat_set<Key, Hash, KeyEqual, Allocator>& set,
|
||||
unsigned int version)
|
||||
{
|
||||
detail::serialize_container(ar, set, version);
|
||||
}
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) /* C4714 */
|
||||
#endif
|
||||
@@ -535,10 +517,10 @@ namespace boost {
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class Allocator = std::allocator<
|
||||
typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_set(InputIterator, InputIterator,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
@@ -548,17 +530,17 @@ namespace boost {
|
||||
|
||||
template <class T, class Hash = boost::hash<T>,
|
||||
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_set(std::initializer_list<T>,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> unordered_flat_set<T, Hash, Pred, Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_set(InputIterator, InputIterator, std::size_t, Allocator)
|
||||
-> unordered_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
@@ -567,9 +549,9 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_set(
|
||||
InputIterator, InputIterator, std::size_t, Hash, Allocator)
|
||||
-> unordered_flat_set<
|
||||
@@ -578,19 +560,19 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_set(std::initializer_list<T>, std::size_t, Allocator)
|
||||
-> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
|
||||
|
||||
template <class T, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_set(std::initializer_list<T>, std::size_t, Hash, Allocator)
|
||||
-> unordered_flat_set<T, Hash, std::equal_to<T>, Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_set(InputIterator, InputIterator, Allocator)
|
||||
-> unordered_flat_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
@@ -599,7 +581,7 @@ namespace boost {
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
unordered_flat_set(std::initializer_list<T>, Allocator)
|
||||
-> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
|
||||
#endif
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
#include <boost/functional/hash_fwd.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
|
||||