Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c0b763176 | |||
| 40cf55240b | |||
| 3c452f93c5 | |||
| cb835cdf1d | |||
| 07f6463c1c | |||
| c0c8d61688 | |||
| d0116814c9 | |||
| a885388480 | |||
| accae6fdd9 | |||
| a633bbaa4d | |||
| 1389043933 | |||
| b41c054c66 | |||
| fbc981eac9 | |||
| 5d2b27d1d9 | |||
| a36937d9b5 | |||
| e214ecdbd0 | |||
| 94ab2f0776 | |||
| 1bdb3717d3 | |||
| cfdfae0a65 | |||
| 489b50bc0f | |||
| 3fe7871745 | |||
| 1e043993ac | |||
| 834580b539 | |||
| 57546ed7e3 | |||
| 6067d9e515 | |||
| ad4f00a825 | |||
| d1c166558a | |||
| d8a75accff | |||
| cdb31c8309 | |||
| f48ae7f7f4 | |||
| 843d6df58c | |||
| 95f36f334d | |||
| 3f4d5fc567 | |||
| 9a2483dd2c | |||
| cd9a592f00 | |||
| aa67315922 | |||
| 21937249c4 | |||
| 5dd2068b62 | |||
| 85098f0506 | |||
| 1b0037135c | |||
| f734e399e3 | |||
| 2afef71107 | |||
| 35bdabf259 | |||
| 1ebe692957 | |||
| 111a503195 | |||
| 5206ac46a3 | |||
| 0bca47c690 | |||
| 7f9aa8505f | |||
| ae364ea311 | |||
| f415d17e58 | |||
| 10fd6c8647 | |||
| 45e50f6cd2 | |||
| 600dd0a8e2 | |||
| a6a0cd0a13 | |||
| a39cf60e93 | |||
| a14b159665 | |||
| 793fad5620 | |||
| c117f4448f | |||
| 65f3ea60dd | |||
| 7645e4f20d | |||
| cd4c1f3713 | |||
| 32ce058ebe | |||
| 4c6d0211fb | |||
| 1edbe3fdbe | |||
| 42579dee10 | |||
| 0928b3d016 | |||
| 5d7786b47e | |||
| 228fede498 | |||
| 1d1f0d306d | |||
| 7ddd562532 | |||
| 3ad8ac4c34 | |||
| ac1a2b4d23 | |||
| e354fb432b | |||
| 88535dca1f | |||
| 3d9b24019d | |||
| 0387791dc1 | |||
| 4126a56528 | |||
| 38acf9c619 | |||
| e5d89f1a15 | |||
| 8630a588bd | |||
| 390efc275f | |||
| 3868496ac9 | |||
| a1f9097f2b | |||
| f177f437b9 | |||
| a3873dc19b | |||
| 89515ee604 |
@@ -107,14 +107,14 @@ environment:
|
||||
B2_TOOLSET: gcc
|
||||
B2_VARIANT: release
|
||||
|
||||
- FLAVOR: Cygwin (64 bit, C++11, Release)
|
||||
- FLAVOR: Cygwin (64 bit, C++11, Debug)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 11
|
||||
B2_TOOLSET: gcc
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
B2_VARIANT: release
|
||||
B2_VARIANT: debug
|
||||
|
||||
- FLAVOR: Cygwin (64 bit, C++14, Debug)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
@@ -125,14 +125,14 @@ environment:
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
B2_VARIANT: debug
|
||||
|
||||
- FLAVOR: Cygwin (64 bit, C++17, Release)
|
||||
- FLAVOR: Cygwin (64 bit, C++17, Debug)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 17
|
||||
B2_TOOLSET: gcc
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
B2_VARIANT: release
|
||||
B2_VARIANT: debug
|
||||
|
||||
- FLAVOR: Cygwin (64 bit, C++2a, Debug)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
|
||||
@@ -30,6 +30,7 @@ local linux_pipeline(name, image, environment, packages = "", sources = [], arch
|
||||
name: "everything",
|
||||
image: image,
|
||||
environment: environment,
|
||||
privileged: true,
|
||||
commands:
|
||||
[
|
||||
'set -e',
|
||||
@@ -278,14 +279,14 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 23.04 GCC 13 32/64 (11,14)",
|
||||
"cppalliance/droneubuntu2304:1",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '11,14', ADDRMD: '32,64' },
|
||||
"g++-13 g++-13-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 23.04 GCC 13 32/64 (17,20,2b)",
|
||||
"cppalliance/droneubuntu2304:1",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '17,20,2b', ADDRMD: '32,64' },
|
||||
"g++-13 g++-13-multilib",
|
||||
),
|
||||
|
||||
@@ -14,6 +14,11 @@ DRONE_BUILD_DIR=$(pwd)
|
||||
BOOST_BRANCH=develop
|
||||
if [ "$DRONE_BRANCH" = "master" ]; then BOOST_BRANCH=master; fi
|
||||
|
||||
if [[ $(uname) == "Linux" && ( "$TSAN" == 1 || "$ASAN" == 1 ) ]]; then
|
||||
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
|
||||
sudo sysctl vm.mmap_rnd_bits=28
|
||||
fi
|
||||
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
|
||||
@@ -57,20 +57,23 @@ jobs:
|
||||
- { 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-12, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', install: 'g++-12' }
|
||||
- { compiler: gcc-13, cxxstd: '11,14,17,20', os: 'ubuntu-24.04', install: 'g++-13' }
|
||||
- { compiler: gcc-14, cxxstd: '11,14,17,20', os: 'ubuntu-24.04', install: 'g++-14' }
|
||||
- { name: "gcc-14 w/ sanitizers (11)", sanitize: yes,
|
||||
compiler: gcc-14, cxxstd: '11', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san1" }
|
||||
- { name: "gcc-14 w/ sanitizers (14)", sanitize: yes,
|
||||
compiler: gcc-14, cxxstd: '14', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san1" }
|
||||
- { name: "gcc-14 w/ sanitizers (17)", sanitize: yes,
|
||||
compiler: gcc-14, cxxstd: '17', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san2" }
|
||||
- { name: "gcc-14 w/ sanitizers (20)", sanitize: yes,
|
||||
compiler: gcc-14, cxxstd: '20', os: 'ubuntu-24.04', install: 'g++-14', ccache_key: "san2" }
|
||||
- { name: "gcc-14 w/ sanitizers (2b)", sanitize: yes,
|
||||
compiler: gcc-14, cxxstd: '2b', os: 'ubuntu-24.04', install: 'g++-14', 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-14, cxxstd: '20', os: 'ubuntu-24.04', install: 'g++-14 g++-14-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-14)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-24.04', install: 'g++-14', compiler: gcc-14,
|
||||
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes, ccache_key: "tsan" }
|
||||
|
||||
# Linux, clang, libc++
|
||||
@@ -88,29 +91,36 @@ jobs:
|
||||
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-14', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
|
||||
- { compiler: 'clang-14', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
|
||||
- { 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-16', cxxstd: '11,14', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-16 libc++-16-dev libc++abi-16-dev' }
|
||||
- { compiler: 'clang-16', cxxstd: '17,20,2b', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-16 libc++-16-dev libc++abi-16-dev' }
|
||||
- { compiler: 'clang-17', cxxstd: '11,14', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-17 libc++-17-dev libc++abi-17-dev' }
|
||||
- { compiler: 'clang-17', cxxstd: '17,20,2b', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-17 libc++-17-dev libc++abi-17-dev' }
|
||||
- { compiler: 'clang-18', cxxstd: '11,14', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-18 libc++-18-dev libc++abi-18-dev' }
|
||||
- { compiler: 'clang-18', cxxstd: '17,20,2b', os: 'ubuntu-24.04', stdlib: libc++, install: 'clang-18 libc++-18-dev libc++abi-18-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', container: 'ubuntu:22.04', os: 'ubuntu-latest', ccache_key: "san2" }
|
||||
- { name: "clang-14 w/ sanitizers (2b)", sanitize: yes,
|
||||
compiler: clang-14, cxxstd: '2b', container: 'ubuntu:22.04', os: 'ubuntu-latest', ccache_key: "san2" }
|
||||
- { name: "clang-18 w/ sanitizers (11,14)", sanitize: yes,
|
||||
compiler: clang-18, cxxstd: '11,14', os: 'ubuntu-24.04', ccache_key: "san1" }
|
||||
- { name: "clang-18 w/ sanitizers (17)", sanitize: yes,
|
||||
compiler: clang-18, cxxstd: '17', os: 'ubuntu-24.04', ccache_key: "san2" }
|
||||
- { name: "clang-18 w/ sanitizers (20)", sanitize: yes,
|
||||
compiler: clang-18, cxxstd: '20', container: 'ubuntu:24.04', os: 'ubuntu-latest', ccache_key: "san2" }
|
||||
- { name: "clang-18 w/ sanitizers (2b)", sanitize: yes,
|
||||
compiler: clang-18, cxxstd: '2b', container: 'ubuntu:24.04', os: 'ubuntu-latest', 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-18)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-24.04', compiler: clang-18,
|
||||
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' }
|
||||
stdlib: libc++, install: 'clang-18 libc++-18-dev libc++abi-18-dev', ccache_key: "tsan" }
|
||||
|
||||
# OSX, clang
|
||||
- { 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: '11,14,17,20,2b', os: 'macos-13', sanitize: yes, ccache_key: "san1" }
|
||||
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-13', thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan" }
|
||||
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-14' }
|
||||
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-15' }
|
||||
|
||||
timeout-minutes: 360
|
||||
# posix (gcc-12 w/ sanitizers is taking longer than 210 minutes
|
||||
@@ -239,7 +249,12 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
if: '!matrix.coverity'
|
||||
run: B2_TARGETS=${{matrix.targets}} ci/build.sh
|
||||
run: |
|
||||
if [[ ${B2_TSAN} == "yes" ]] && [[ $(uname) == "Linux" ]]; then
|
||||
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
|
||||
sudo sysctl vm.mmap_rnd_bits=28
|
||||
fi
|
||||
B2_TARGETS=${{matrix.targets}} ci/build.sh
|
||||
|
||||
- name: Upload coverage
|
||||
if: matrix.coverage
|
||||
@@ -263,7 +278,8 @@ jobs:
|
||||
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.2, cxxstd: '14,17', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
|
||||
- { toolset: msvc-14.2, cxxstd: '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"' }
|
||||
@@ -326,8 +342,8 @@ 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: '11,17,20', variant: 'release' }
|
||||
- { sys: MINGW64, compiler: gcc, cxxstd: '11,17,20', variant: 'debug,release' }
|
||||
|
||||
needs: [runner-selection]
|
||||
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)['windows-latest'] }}
|
||||
@@ -361,9 +377,12 @@ jobs:
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_SANITIZE: ${{matrix.sanitize}}
|
||||
B2_STDLIB: ${{matrix.stdlib}}
|
||||
B2_VARIANT: ${{matrix.variant}}
|
||||
run: ci/github/install.sh
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
B2_VARIANT: ${{matrix.variant}}
|
||||
run: ci/build.sh
|
||||
|
||||
# Run also the CMake tests to avoid having to setup another matrix for CMake on MSYS
|
||||
|
||||
@@ -25,6 +25,10 @@ deviate from the standard in exchange for top performance.</ul>
|
||||
|
||||
<ul>High performance for multithreaded scenarios. Introducing a new non-standard, iterator-free API.</ul>
|
||||
|
||||
**`boost::concurrent_node_set` `boost::concurrent_node_map`**
|
||||
|
||||
<ul>Variations of <code>boost::concurrent_flat_(set|map)</code> providing pointer stability.</ul>
|
||||
|
||||
## Learn about Boost.Unordered
|
||||
|
||||
* [Online documentation](https://boost.org/libs/unordered)
|
||||
@@ -34,16 +38,18 @@ deviate from the standard in exchange for top performance.</ul>
|
||||
* [Inside `boost::unordered_flat_map`](https://bannalia.blogspot.com/2022/11/inside-boostunorderedflatmap.html)
|
||||
* [Inside `boost::concurrent_flat_map`](https://bannalia.blogspot.com/2023/07/inside-boostconcurrentflatmap.html)
|
||||
* [Bulk visitation in `boost::concurrent_flat_map`](https://bannalia.blogspot.com/2023/10/bulk-visitation-in-boostconcurrentflatm.html)
|
||||
|
||||
## Get the library
|
||||
* Debugging visualizers for Boost.Unordered:
|
||||
* [Natvis for boost::unordered_map, and how to use <Intrinsic> elements](https://blog.ganets.ky/NatvisForUnordered/)
|
||||
* [Natvis for boost::concurrent_flat_map, and why fancy pointers are hard](https://blog.ganets.ky/NatvisForUnordered2/)
|
||||
* [Visualizing boost::unordered_map in GDB, with pretty-printer customization points](https://blog.ganets.ky/PrettyPrinter/)
|
||||
|
||||
Boost.Unordered can be installed in a number of ways:
|
||||
|
||||
* [Download Boost](https://www.boost.org/users/download/) and you're ready to go (this is a header-only library requiring no building).
|
||||
* Using Conan 2: In case you don't have it yet, add an entry for Boost in your `conanfile.txt` (the example requires at least Boost 1.83):
|
||||
* Using Conan 2: In case you don't have it yet, add an entry for Boost in your `conanfile.txt` (the example requires at least Boost 1.86):
|
||||
```
|
||||
[requires]
|
||||
boost/[>=1.83.0]
|
||||
boost/[>=1.86.0]
|
||||
```
|
||||
<ul>If you're not using any compiled Boost library, the following will skip building altogether:</ul>
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Copyright 2023-2024 René Ferdinand Rivera Morell
|
||||
# Copyright 2024 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
require-b2 5.2 ;
|
||||
|
||||
constant boost_dependencies :
|
||||
/boost/assert//boost_assert
|
||||
/boost/config//boost_config
|
||||
/boost/container_hash//boost_container_hash
|
||||
/boost/core//boost_core
|
||||
/boost/mp11//boost_mp11
|
||||
/boost/predef//boost_predef
|
||||
/boost/throw_exception//boost_throw_exception
|
||||
;
|
||||
|
||||
project /boost/unordered ;
|
||||
|
||||
explicit
|
||||
[ alias boost_unordered : : : : <include>include <library>$(boost_dependencies) ]
|
||||
[ alias all : boost_unordered test ]
|
||||
;
|
||||
|
||||
call-if : boost-library unordered
|
||||
;
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 30 KiB |
@@ -459,13 +459,14 @@ h|unsuccessful lookup
|
||||
|
||||
|===
|
||||
|
||||
== boost::concurrent_flat_map
|
||||
== boost::concurrent_(flat|node)_map
|
||||
|
||||
All benchmarks were created using:
|
||||
|
||||
* `https://spec.oneapi.io/versions/latest/elements/oneTBB/source/containers/concurrent_hash_map_cls.html[oneapi::tbb::concurrent_hash_map^]<int, int>`
|
||||
* `https://github.com/greg7mdp/gtl/blob/main/docs/phmap.md[gtl::parallel_flat_hash_map^]<int, int>` with 64 submaps
|
||||
* `boost::concurrent_flat_map<int, int>`
|
||||
* `boost::concurrent_node_map<int, int>`
|
||||
|
||||
The source code can be https://github.com/boostorg/boost_unordered_benchmarks/tree/boost_concurrent_flat_map[found here^].
|
||||
|
||||
@@ -475,7 +476,7 @@ 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]:
|
||||
`boost::concurrent_flat_map` and `boost::concurrent_node_map` are 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`].
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ h|*Method* h|*Description*
|
||||
|
||||
|`float max_load_factor(float z)`
|
||||
|Changes the container's maximum load factor, using `z` as a hint. +
|
||||
**Open-addressing containers:** this function does nothing: users are not allowed to change the maximum load factor.
|
||||
**Open-addressing and concurrent containers:** this function does nothing: users are not allowed to change the maximum load factor.
|
||||
|
||||
|`void rehash(size_type n)`
|
||||
|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
|
||||
|
||||
@@ -6,6 +6,22 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.87.0 - Major update
|
||||
|
||||
* Added concurrent, node-based containers `boost::concurrent_node_map` and `boost::concurrent_node_set`.
|
||||
* Added `insert_and_visit(x, f1, f2)` and similar operations to concurrent containers, which
|
||||
allow for visitation of an element right after insertion (by contrast, `insert_or_visit(x, f)` only
|
||||
visits the element if insertion did _not_ take place).
|
||||
* Made visitation exclusive-locked within certain
|
||||
`boost::concurrent_flat_set` operations to allow for safe mutable modification of elements
|
||||
({github-pr-url}/265[PR#265^]).
|
||||
* In Visual Studio Natvis, supported any container with an allocator that uses fancy pointers. This applies to any fancy pointer type, as long as the proper Natvis customization point "Intrinsic" functions are written for the fancy pointer type.
|
||||
* Added GDB pretty-printers for all containers and iterators. For a container with an allocator that uses fancy pointers, these only work if the proper pretty-printer is written for the fancy pointer type itself.
|
||||
* Fixed `std::initializer_list` assignment issues for open-addressing containers
|
||||
({github-pr-url}/277[PR#277^]).
|
||||
* Allowed non-copyable callables to be passed to the `std::initializer_list` overloads of `insert_{and|or}_[c]visit` for concurrent containers, by internally passing a `std::reference_wrapper` of the callable to the iterator-pair overloads.
|
||||
|
||||
|
||||
== Release 1.86.0
|
||||
|
||||
* Added container `pmr` aliases when header `<memory_resource>` is available. The alias `boost::unordered::pmr::[container]` refers to `boost::unordered::[container]` with a `std::pmr::polymorphic_allocator` allocator type.
|
||||
|
||||
@@ -89,7 +89,8 @@ 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`
|
||||
data structure. The APIs of `boost::concurrent_flat_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map`
|
||||
are modelled after `std::unordered_flat_set` and `std::unordered_flat_map`, respectively,
|
||||
with the crucial difference that iterators are not provided
|
||||
due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking):
|
||||
@@ -105,7 +106,7 @@ 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.Unordered concurrent containers use _internal visitation_
|
||||
facilities as a thread-safe substitute. Classical operations returning an iterator to an
|
||||
element already existing in the container, like for instance:
|
||||
|
||||
@@ -141,7 +142,8 @@ 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.
|
||||
|
||||
One notable operation not provided by `boost::concurrent_flat_map` is `operator[]`/`at`, which can be
|
||||
One notable operation not provided by `boost::concurrent_flat_map`/`boost::concurrent_node_map`
|
||||
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,7 +3,8 @@
|
||||
|
||||
:idprefix: concurrent_
|
||||
|
||||
Boost.Unordered provides `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
Boost.Unordered provides `boost::concurrent_node_set`, `boost::concurrent_node_map`,
|
||||
`boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
hash tables that allow concurrent write/read access from
|
||||
different threads without having to implement any synchronzation mechanism on the user's side.
|
||||
|
||||
@@ -43,7 +44,7 @@ logical cores in the CPU).
|
||||
|
||||
== Visitation-based API
|
||||
|
||||
The first thing a new user of `boost::concurrent_flat_set` or `boost::concurrent_flat_map`
|
||||
The first thing a new user of Boost.Unordered concurrent containers
|
||||
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^]
|
||||
in the C++ standard sense). The reason for this is that iterators are inherently
|
||||
@@ -62,7 +63,7 @@ thread issues an `m.erase(k)` operation between A and B. There are designs that
|
||||
can remedy this by making iterators lock the element they point to, but this
|
||||
approach lends itself to high contention and can easily produce deadlocks in a program.
|
||||
`operator[]` has similar concurrency issues, and is not provided by
|
||||
`boost::concurrent_flat_map` either. Instead, element access is done through
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map` either. Instead, element access is done through
|
||||
so-called _visitation functions_:
|
||||
|
||||
[source,c++]
|
||||
@@ -112,7 +113,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 concurrent containers, and
|
||||
many classical operations have visitation-enabled variations:
|
||||
|
||||
[source,c++]
|
||||
@@ -125,13 +126,32 @@ m.insert_or_visit(x, [](auto& y) {
|
||||
----
|
||||
|
||||
Note that in this last example the visitation function could actually _modify_
|
||||
the element: as a general rule, operations on a `boost::concurrent_flat_map` `m`
|
||||
the element: as a general rule, operations on a concurrent 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,
|
||||
in higher parallelization. For concurrent sets, on the other hand,
|
||||
visitation is always const access.
|
||||
|
||||
Although expected to be used much less frequently, concurrent containers
|
||||
also provide insertion operations where an element can be visited right after
|
||||
element creation (in addition to the usual visitation when an equivalent
|
||||
element already exists):
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
m.insert_and_cvisit(x,
|
||||
[](const auto& y) {
|
||||
std::cout<< "(" << y.first << ", " << y.second <<") inserted\n";
|
||||
},
|
||||
[](const auto& y) {
|
||||
std::cout<< "(" << y.first << ", " << y.second << ") already exists\n";
|
||||
});
|
||||
----
|
||||
|
||||
Consult the references of
|
||||
xref:#concurrent_node_set[`boost::concurrent_node_set`],
|
||||
xref:#concurrent_flat_map[`boost::concurrent_node_map`],
|
||||
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.
|
||||
@@ -245,7 +265,7 @@ 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
|
||||
Concurrent containers can be copied, assigned, cleared and merged just like any other
|
||||
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
|
||||
@@ -258,9 +278,25 @@ reserving space in advance of bulk insertions will generally speed up the proces
|
||||
== 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.
|
||||
they can be efficiently move-constructed from their non-concurrent counterpart, and vice versa.
|
||||
|
||||
[caption=, title='Table {counter:table-counter}. Concurrent/non-concurrent interoperatibility']
|
||||
[cols="1,1", frame=all, grid=all]
|
||||
|===
|
||||
^|`boost::concurrent_node_set`
|
||||
^|`boost::unordered_node_set`
|
||||
|
||||
^|`boost::concurrent_node_map`
|
||||
^|`boost::unordered_node_map`
|
||||
|
||||
^|`boost::concurrent_flat_set`
|
||||
^|`boost::unordered_flat_set`
|
||||
|
||||
^|`boost::concurrent_flat_map`
|
||||
^|`boost::unordered_flat_map`
|
||||
|
||||
|===
|
||||
|
||||
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:
|
||||
|
||||
@@ -169,6 +169,27 @@ namespace boost {
|
||||
template<class F> size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list<value_type> il, F f);
|
||||
template<class F> size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list<value_type> il, F f);
|
||||
|
||||
template<class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_visit](const init_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_cvisit](const init_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_visit](init_type&& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_cvisit](init_type&& obj, F1 f1, F2 f2);
|
||||
template<class InputIterator,class F1, class F2>
|
||||
size_type xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2);
|
||||
template<class InputIterator,class F1, class F2>
|
||||
size_type xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2);
|
||||
template<class F1, class F2>
|
||||
size_type xref:#concurrent_flat_map_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list<value_type> il, F1 f1, F2 f2);
|
||||
template<class F1, class F2>
|
||||
size_type xref:#concurrent_flat_map_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
|
||||
|
||||
template<class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](const key_type& k, Args&&... args);
|
||||
template<class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](key_type&& k, Args&&... args);
|
||||
template<class K, class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](K&& k, Args&&... args);
|
||||
@@ -186,6 +207,19 @@ namespace boost {
|
||||
template<class K, class... Args, class F>
|
||||
bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_cvisit](K&& k, Args&&... args, F&& f);
|
||||
|
||||
template<class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](const key_type& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](const key_type& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](key_type&& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](key_type&& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class K, class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](K&& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class K, class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](K&& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
|
||||
template<class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj);
|
||||
template<class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj);
|
||||
template<class K, class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](K&& k, M&& obj);
|
||||
@@ -389,9 +423,14 @@ prior blocking operations on `x` synchronize with *op*. So, blocking operations
|
||||
An operation is said to be _blocking on rehashing of_ ``__x__`` if it blocks on `x`
|
||||
only when an internal rehashing is issued.
|
||||
|
||||
Access or modification of an element of a `boost::concurrent_flat_map` passed by reference to a
|
||||
user-provided visitation function do not introduce data races when the visitation function
|
||||
is executed internally by the `boost::concurrent_flat_map`.
|
||||
When executed internally by a `boost::concurrent_flat_map`, the following operations by a
|
||||
user-provided visitation function on the element passed do not introduce data races:
|
||||
|
||||
* Read access to the element.
|
||||
* Non-mutable modification of the element.
|
||||
* Mutable modification of the element:
|
||||
** Within a container function accepting two visitation functions, always for the first function.
|
||||
** Within a non-const container function whose name does not contain `cvisit`, for the last (or only) visitation function.
|
||||
|
||||
Any `boost::concurrent_flat_map operation` that inserts or modifies an element `e`
|
||||
synchronizes with the internal invocation of a visitation function on `e`.
|
||||
@@ -1109,7 +1148,113 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
|
||||
Equivalent to
|
||||
[listing,subs="+macros,+quotes"]
|
||||
-----
|
||||
this->xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f);
|
||||
this->xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f));
|
||||
-----
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The number of elements inserted.
|
||||
|
||||
---
|
||||
|
||||
==== emplace_and_[c]visit
|
||||
```c++
|
||||
template<class... Args, class F1, class F2>
|
||||
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
|
||||
```
|
||||
|
||||
Inserts an object, constructed with the arguments `args`, in the table if there is no element in the table with an equivalent key,
|
||||
and then invokes `f1` with a non-const reference to the newly created element.
|
||||
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff `emplace_and_cvisit` is used.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is constructible from `args`.
|
||||
Returns:;; `true` if an insert took place.
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
|
||||
+
|
||||
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
|
||||
|
||||
---
|
||||
|
||||
==== Copy insert_and_[c]visit
|
||||
```c++
|
||||
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool insert_and_visit(const init_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
|
||||
and then invokes `f1` with a non-const reference to the newly created element.
|
||||
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^].
|
||||
Returns:;; `true` if an insert took place. +
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
|
||||
+
|
||||
In a call of the form `insert_and_[c]visit(obj, f1, f2)`, the overloads accepting a `const value_type&` argument participate in overload resolution
|
||||
only if `std::remove_cv<std::remove_reference<decltype(obj)>::type>::type` is `value_type`.
|
||||
|
||||
---
|
||||
|
||||
==== Move insert_and_[c]visit
|
||||
```c++
|
||||
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool insert_and_visit(init_type&& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
|
||||
and then invokes `f1` with a non-const reference to the newly created element.
|
||||
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
|
||||
Returns:;; `true` if an insert took place. +
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
|
||||
+
|
||||
In a call of the form `insert_and_[c]visit(obj, f1, f2)`, the overloads accepting a `value_type&&` argument participate in overload resolution
|
||||
only if `std::remove_reference<decltype(obj)>::type` is `value_type`.
|
||||
|
||||
---
|
||||
|
||||
==== Insert Iterator Range and Visit
|
||||
```c++
|
||||
template<class InputIterator, class F1, class F2>
|
||||
size_type insert_or_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
|
||||
template<class InputIterator, class F1, class F2>
|
||||
size_type insert_or_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Equivalent to
|
||||
[listing,subs="+macros,+quotes"]
|
||||
-----
|
||||
while(first != last) this->xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2);
|
||||
-----
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The number of elements inserted.
|
||||
|
||||
---
|
||||
|
||||
==== Insert Initializer List and Visit
|
||||
```c++
|
||||
template<class F1, class F2>
|
||||
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
|
||||
template<class F1, class F2>
|
||||
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Equivalent to
|
||||
[listing,subs="+macros,+quotes"]
|
||||
-----
|
||||
this->xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2));
|
||||
-----
|
||||
|
||||
[horizontal]
|
||||
@@ -1203,6 +1348,55 @@ The `template<class K, class\... Args, class F>` overloads only participate in o
|
||||
|
||||
---
|
||||
|
||||
==== try_emplace_and_[c]visit
|
||||
```c++
|
||||
template<class... Args, class F1, class F2>
|
||||
bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class K, class... Args, class F1, class F2>
|
||||
bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class K, class... Args, class F1, class F2>
|
||||
bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2);
|
||||
```
|
||||
|
||||
Inserts an element constructed from `k` and `args` into the table if there is no existing element with key `k` contained within it,
|
||||
and then invokes `f1` with a non-const reference to the newly created element.
|
||||
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; `true` if an insert took place. +
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; No `value_type` is constructed
|
||||
if there is an element with an equivalent key; otherwise, the construction is of the form: +
|
||||
+
|
||||
--
|
||||
```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)...))
|
||||
|
||||
// last two overloads
|
||||
value_type(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<K>(k)),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
```
|
||||
|
||||
Invalidates pointers and references to elements if a rehashing is issued.
|
||||
|
||||
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
|
||||
|
||||
The `template<class K, class\... Args, class F1, class F2>` overloads only participate in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. 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.
|
||||
|
||||
--
|
||||
|
||||
---
|
||||
|
||||
==== insert_or_assign
|
||||
```c++
|
||||
template<class M> bool insert_or_assign(const key_type& k, M&& obj);
|
||||
|
||||
@@ -98,25 +98,35 @@ namespace boost {
|
||||
|
||||
|
||||
// visitation
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f);
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const key_type& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f);
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const K& k, F f) const;
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const;
|
||||
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f);
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[cvisit_all](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f);
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f) const;
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[cvisit_while](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
@@ -150,6 +160,25 @@ namespace boost {
|
||||
template<class F> size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list<value_type> il, F f);
|
||||
template<class F> size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list<value_type> il, F f);
|
||||
|
||||
template<class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_set_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_set_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_set_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool xref:#concurrent_flat_set_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
|
||||
template<class K, class F1, class F2> bool xref:#concurrent_flat_set_transparent_insert_and_cvisit[insert_and_visit](K&& k, F1 f1, F2 f2);
|
||||
template<class K, class F1, class F2> bool xref:#concurrent_flat_set_transparent_insert_and_cvisit[insert_and_cvisit](K&& k, F1 f1, F2 f2);
|
||||
template<class InputIterator,class F1, class F2>
|
||||
size_type xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2);
|
||||
template<class InputIterator,class F1, class F2>
|
||||
size_type xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2);
|
||||
template<class F1, class F2>
|
||||
size_type xref:#concurrent_flat_set_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list<value_type> il, F1 f1, F2 f2);
|
||||
template<class F1, class F2>
|
||||
size_type xref:#concurrent_flat_set_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
|
||||
|
||||
size_type xref:#concurrent_flat_set_erase[erase](const key_type& k);
|
||||
template<class K> size_type xref:#concurrent_flat_set_erase[erase](const K& k);
|
||||
|
||||
@@ -340,9 +369,14 @@ prior blocking operations on `x` synchronize with *op*. So, blocking operations
|
||||
An operation is said to be _blocking on rehashing of_ ``__x__`` if it blocks on `x`
|
||||
only when an internal rehashing is issued.
|
||||
|
||||
Access or modification of an element of a `boost::concurrent_flat_set` passed by reference to a
|
||||
user-provided visitation function do not introduce data races when the visitation function
|
||||
is executed internally by the `boost::concurrent_flat_set`.
|
||||
When executed internally by a `boost::concurrent_flat_set`, the following operations by a
|
||||
user-provided visitation function on the element passed do not introduce data races:
|
||||
|
||||
* Read access to the element.
|
||||
* Non-mutable modification of the element.
|
||||
* Mutable modification of the element:
|
||||
** Within a container function accepting two visitation functions, always for the first function.
|
||||
** Within a non-const container function whose name does not contain `cvisit`, for the last (or only) visitation function.
|
||||
|
||||
Any `boost::concurrent_flat_set operation` that inserts or modifies an element `e`
|
||||
synchronizes with the internal invocation of a visitation function on `e`.
|
||||
@@ -708,8 +742,10 @@ Concurrency:;; Blocking on `*this`.
|
||||
==== [c]visit
|
||||
|
||||
```c++
|
||||
template<class F> size_t visit(const key_type& k, F f);
|
||||
template<class F> size_t visit(const key_type& k, F f) const;
|
||||
template<class F> size_t cvisit(const key_type& k, F f) const;
|
||||
template<class K, class F> size_t visit(const K& k, F f);
|
||||
template<class K, class F> size_t visit(const K& k, F f) const;
|
||||
template<class K, class F> size_t cvisit(const K& k, F f) const;
|
||||
```
|
||||
@@ -725,6 +761,8 @@ 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>
|
||||
@@ -758,6 +796,7 @@ Returns:;; The number of elements visited.
|
||||
==== [c]visit_all
|
||||
|
||||
```c++
|
||||
template<class F> size_t visit_all(F f);
|
||||
template<class F> size_t visit_all(F f) const;
|
||||
template<class F> size_t cvisit_all(F f) const;
|
||||
```
|
||||
@@ -772,6 +811,7 @@ Returns:;; The number of elements visited.
|
||||
==== Parallel [c]visit_all
|
||||
|
||||
```c++
|
||||
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const;
|
||||
```
|
||||
@@ -792,6 +832,7 @@ 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;
|
||||
```
|
||||
@@ -807,6 +848,7 @@ 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;
|
||||
```
|
||||
@@ -1064,7 +1106,123 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
|
||||
Equivalent to
|
||||
[listing,subs="+macros,+quotes"]
|
||||
-----
|
||||
this->xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f);
|
||||
this->xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), std::ref(f));
|
||||
-----
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The number of elements inserted.
|
||||
|
||||
---
|
||||
|
||||
==== emplace_and_[c]visit
|
||||
```c++
|
||||
template<class... Args, class F1, class F2>
|
||||
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
|
||||
template<class... Args, class F1, class F2>
|
||||
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
|
||||
```
|
||||
|
||||
Inserts an object, constructed with the arguments `args`, in the table if there is no element in the table with an equivalent key,
|
||||
and then invokes `f1` with a const reference to the newly created element.
|
||||
Otherwise, invokes `f2` with a const reference to the equivalent element.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is constructible from `args`.
|
||||
Returns:;; `true` if an insert took place.
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
|
||||
+
|
||||
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
|
||||
|
||||
---
|
||||
|
||||
==== Copy insert_and_[c]visit
|
||||
```c++
|
||||
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
|
||||
and then invokes `f1` with a const reference to the newly created element.
|
||||
Otherwise, invokes `f2` with a const reference to the equivalent element.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^].
|
||||
Returns:;; `true` if an insert took place. +
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued.
|
||||
|
||||
---
|
||||
|
||||
==== Move insert_and_[c]visit
|
||||
```c++
|
||||
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
|
||||
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
|
||||
and then invokes `f1` with a const reference to the newly created element.
|
||||
Otherwise, invokes `f2` with a const reference to the equivalent element.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
|
||||
Returns:;; `true` if an insert took place. +
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued.
|
||||
|
||||
---
|
||||
|
||||
==== Transparent insert_and_[c]visit
|
||||
```c++
|
||||
template<class K, class F1, class F2> bool insert_and_visit(K&& k, F1 f1, F2 f2);
|
||||
template<class K, class F1, class F2> bool insert_and_cvisit(K&& k, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Inserts an element constructed from `std::forward<K>(k)` in the container if and only if there is no element in the container with an equivalent key,
|
||||
and then invokes `f1` with a const reference to the newly created element.
|
||||
Otherwise, invokes `f2` with a const reference to the equivalent element.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] from `k`.
|
||||
Returns:;; `true` if an insert took place.
|
||||
Concurrency:;; Blocking on rehashing of `*this`.
|
||||
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
|
||||
+
|
||||
These overloads only participate in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. 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.
|
||||
|
||||
---
|
||||
|
||||
==== Insert Iterator Range and Visit
|
||||
```c++
|
||||
template<class InputIterator,class F1, class F2>
|
||||
size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
|
||||
template<class InputIterator,class F1, class F2>
|
||||
size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Equivalent to
|
||||
[listing,subs="+macros,+quotes"]
|
||||
-----
|
||||
while(first != last) this->xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2);
|
||||
-----
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The number of elements inserted.
|
||||
|
||||
---
|
||||
|
||||
==== Insert Initializer List and Visit
|
||||
```c++
|
||||
template<class F1, class F2>
|
||||
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
|
||||
template<class F1, class F2>
|
||||
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
|
||||
```
|
||||
|
||||
Equivalent to
|
||||
[listing,subs="+macros,+quotes"]
|
||||
-----
|
||||
this->xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), std::ref(f1), std::ref(f2));
|
||||
-----
|
||||
|
||||
[horizontal]
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
|
||||
== Visual Studio Natvis
|
||||
|
||||
All containers and iterators have custom visualizations in the Natvis framework, as long as their allocator uses regular raw pointers. Any container or iterator with an allocator using fancy pointers does not have a custom visualization right now.
|
||||
All containers and iterators have custom visualizations in the Natvis framework.
|
||||
|
||||
=== Using in your project
|
||||
|
||||
To visualize Boost.Unordered containers in the Natvis framework in your project, simply add the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost_unordered.natvis] to your Visual Studio project as an "Existing Item".
|
||||
|
||||
=== Visualization structure
|
||||
|
||||
The visualizations mirror those for the standard unordered containers. A container has a maximum of 100 elements displayed at once. Each set element has its item name listed as `[i]`, where `i` is the index in the display, starting at `0`. Each map element has its item name listed as `[\{key-display}]` by default. For example, if the first element is the pair `("abc", 1)`, the item name will be `["abc"]`. This behaviour can be overridden by using the view "ShowElementsByIndex", which switches the map display behaviour to name the elements by index. This same view name is used in the standard unordered containers.
|
||||
|
||||
@@ -13,4 +19,72 @@ By default, the closed-addressing containers will show the `[hash_function]` and
|
||||
|
||||
By default, the open-addressing containers will show the `[hash_function]`, `[key_eq]`, `[allocator]`, and the elements. Using the view "simple" shows only the elements, with no other items present. Both the SIMD and the non-SIMD implementations are viewable through the Natvis framework.
|
||||
|
||||
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `\{ end iterator }`.
|
||||
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `{ end iterator }`.
|
||||
|
||||
=== Fancy pointers
|
||||
|
||||
The container visualizations also work if you are using fancy pointers in your allocator, such as `boost::interprocess::offset_ptr`. While this is rare, Boost.Unordered has natvis customization points to support any type of fancy pointer. `boost::interprocess::offset_ptr` has support already defined in the Boost.Interprocess library, and you can add support to your own type by following the instructions contained in a comment near the end of the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost_unordered.natvis].
|
||||
|
||||
== GDB Pretty-Printers
|
||||
|
||||
All containers and iterators have a custom GDB pretty-printer.
|
||||
|
||||
=== Using in your project
|
||||
|
||||
Always, when using pretty-printers, you must enable pretty-printing like below. This is typically a one-time setup.
|
||||
|
||||
```plaintext
|
||||
(gdb) set print pretty on
|
||||
```
|
||||
|
||||
By default, if you compile into an ELF binary format, your binary will contain the Boost.Unordered pretty-printers. To use the embedded pretty-printers, ensure you allow auto-loading like below. This must be done every time you load GDB, or add it to a ".gdbinit" file.
|
||||
|
||||
```plaintext
|
||||
(gdb) add-auto-load-safe-path [/path/to/executable]
|
||||
```
|
||||
|
||||
You can choose to compile your binary _without_ embedding the pretty-printers by defining `BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS`, which disables the embedded GDB pretty-printers for all Boost libraries that have this feature.
|
||||
|
||||
You can load the pretty-printers externally from the non-embedded Python script. Add the script, link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered_printers.py[/extra/boost_unordered_printers.py], using the `source` command as shown below.
|
||||
|
||||
```plaintext
|
||||
(gdb) source [/path/to/boost]/libs/unordered/extra/boost_unordered_printers.py
|
||||
```
|
||||
|
||||
=== Visualization structure
|
||||
|
||||
The visualizations mirror the standard unordered containers. The map containers display an association from key to mapped value. The set containers display an association from index to value. An iterator is either displayed with its item, or as an end iterator. Here is what may be shown for an example `boost::unordered_map`, an example `boost::unordered_set`, and their respective begin and end iterators.
|
||||
|
||||
```plaintext
|
||||
(gdb) print example_unordered_map
|
||||
$1 = boost::unordered_map with 3 elements = {["C"] = "c", ["B"] = "b", ["A"] = "a"}
|
||||
(gdb) print example_unordered_map_begin
|
||||
$2 = iterator = { {first = "C", second = "c"} }
|
||||
(gdb) print example_unordered_map_end
|
||||
$3 = iterator = { end iterator }
|
||||
(gdb) print example_unordered_set
|
||||
$4 = boost::unordered_set with 3 elements = {[0] = "c", [1] = "b", [2] = "a"}
|
||||
(gdb) print example_unordered_set_begin
|
||||
$5 = iterator = { "c" }
|
||||
(gdb) print example_unordered_set_end
|
||||
$6 = iterator = { end iterator }
|
||||
```
|
||||
|
||||
The other containers are identical other than replacing "`boost::unordered_{map|set}`" with the appropriate template name when displaying the container itself. Note that each sub-element (i.e. the key, the mapped value, or the value) is displayed based on its own printing settings which may include its own pretty-printer.
|
||||
|
||||
Both the SIMD and the non-SIMD implementations are viewable through the GDB pretty-printers.
|
||||
|
||||
For open-addressing containers where xref:#hash_quality_container_statistics[container statistics] are enabled, you can obtain these statistics by calling `get_stats()` on the container, from within GDB. This is overridden in GDB as an link:https://sourceware.org/gdb/current/onlinedocs/gdb.html/Xmethod-API.html[xmethod], so it will not invoke any C++ synchronization code. See the following printout as an example for the expected format.
|
||||
|
||||
```plaintext
|
||||
(gdb) print example_flat_map.get_stats()
|
||||
$1 = [stats] = {[insertion] = {[count] = 5, [probe_length] = {avg = 1.0, var = 0.0, dev = 0.0}},
|
||||
[successful_lookup] = {[count] = 0, [probe_length] = {avg = 0.0, var = 0.0, dev = 0.0},
|
||||
[num_comparisons] = {avg = 0.0, var = 0.0, dev = 0.0}}, [unsuccessful_lookup] = {[count] = 5,
|
||||
[probe_length] = {avg = 1.0, var = 0.0, dev = 0.0},
|
||||
[num_comparisons] = {avg = 0.0, var = 0.0, dev = 0.0}}}
|
||||
```
|
||||
|
||||
=== Fancy pointers
|
||||
|
||||
The pretty-printers also work if you are using fancy pointers in your allocator, such as `boost::interprocess::offset_ptr`. While this is rare, Boost.Unordered has GDB pretty-printer customization points to support any type of fancy pointer. `boost::interprocess::offset_ptr` has support already defined in the Boost.Interprocess library, and you can add support to your own type by following the instructions contained in a comment near the end of the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered_printers.py[/extra/boost_unordered_printers.py].
|
||||
|
||||
@@ -43,7 +43,8 @@ boost::unordered_node_map
|
||||
boost::unordered_flat_map
|
||||
|
||||
^.^h|*Concurrent*
|
||||
^|
|
||||
^| `boost::concurrent_node_set` +
|
||||
`boost::concurrent_node_map`
|
||||
^| `boost::concurrent_flat_set` +
|
||||
`boost::concurrent_flat_map`
|
||||
|
||||
@@ -59,6 +60,7 @@ 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.
|
||||
Flat and node-based variants are provided.
|
||||
|
||||
All sets and maps in Boost.Unordered are instantiatied similarly as
|
||||
`std::unordered_set` and `std::unordered_map`, respectively:
|
||||
@@ -72,8 +74,8 @@ namespace boost {
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_set;
|
||||
// same for unordered_multiset, unordered_flat_set, unordered_node_set
|
||||
// and concurrent_flat_set
|
||||
// same for unordered_multiset, unordered_flat_set, unordered_node_set,
|
||||
// concurrent_flat_set and concurrent_node_set
|
||||
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
@@ -81,8 +83,8 @@ namespace boost {
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class unordered_map;
|
||||
// same for unordered_multimap, unordered_flat_map, unordered_node_map
|
||||
// and concurrent_flat_map
|
||||
// same for unordered_multimap, unordered_flat_map, unordered_node_map,
|
||||
// concurrent_flat_map and concurrent_node_map
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
@@ -121,7 +121,8 @@ 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_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map`:
|
||||
|
||||
* Open-addressing is faster than closed-addressing alternatives, both in non-concurrent and
|
||||
concurrent scenarios.
|
||||
@@ -130,7 +131,7 @@ with minimal locking. In particular, the metadata array can be used for implemen
|
||||
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`,
|
||||
of all elements between a concurrent container and its non-concurrent counterpart,
|
||||
and vice versa.
|
||||
|
||||
=== Hash Function and Platform Interoperability
|
||||
|
||||
@@ -13,3 +13,5 @@ include::unordered_node_map.adoc[]
|
||||
include::unordered_node_set.adoc[]
|
||||
include::concurrent_flat_map.adoc[]
|
||||
include::concurrent_flat_set.adoc[]
|
||||
include::concurrent_node_map.adoc[]
|
||||
include::concurrent_node_set.adoc[]
|
||||
|
||||
@@ -129,7 +129,8 @@ xref:#rationale_open_addresing_containers[corresponding section].
|
||||
|
||||
== Concurrent Containers
|
||||
|
||||
`boost::concurrent_flat_set` and `boost::concurrent_flat_map` use the basic
|
||||
`boost::concurrent_flat_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map` use the basic
|
||||
xref:#structures_open_addressing_containers[open-addressing layout] described above
|
||||
augmented with synchronization mechanisms.
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace boost {
|
||||
explicit xref:#unordered_node_map_allocator_constructor[unordered_node_map](const Allocator& a);
|
||||
xref:#unordered_node_map_copy_constructor_with_allocator[unordered_node_map](const unordered_node_map& other, const Allocator& a);
|
||||
xref:#unordered_node_map_move_constructor_with_allocator[unordered_node_map](unordered_node_map&& other, const Allocator& a);
|
||||
xref:#unordered_node_map_move_constructor_from_concurrent_node_map[unordered_node_map](concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_node_map_initializer_list_constructor[unordered_node_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -537,6 +538,24 @@ and always calls `other.reset_stats()`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_node_map
|
||||
|
||||
```c++
|
||||
unordered_node_map(concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_node_map[`concurrent_node_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`.
|
||||
If statistics are xref:unordered_node_map_boost_unordered_enable_stats[enabled],
|
||||
transfers the internal statistical information from `other` and calls `other.reset_stats()`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -1219,8 +1238,8 @@ Throws:;; Nothing.
|
||||
|
||||
==== Extract by Key
|
||||
```c++
|
||||
node_type erase(const key_type& k);
|
||||
template<class K> node_type erase(K&& k);
|
||||
node_type extract(const key_type& k);
|
||||
template<class K> node_type extract(K&& k);
|
||||
```
|
||||
|
||||
Extracts the element with key equivalent to `k`, if it exists.
|
||||
@@ -1228,7 +1247,7 @@ Extracts the element with key equivalent to `k`, if it exists.
|
||||
[horizontal]
|
||||
Returns:;; A `node_type` object holding the extracted element, or empty if no element was extracted.
|
||||
Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.
|
||||
Notes:;; The `template<class K>` 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.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. 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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace boost {
|
||||
explicit xref:#unordered_node_set_allocator_constructor[unordered_node_set](const Allocator& a);
|
||||
xref:#unordered_node_set_copy_constructor_with_allocator[unordered_node_set](const unordered_node_set& other, const Allocator& a);
|
||||
xref:#unordered_node_set_move_constructor_with_allocator[unordered_node_set](unordered_node_set&& other, const Allocator& a);
|
||||
xref:#unordered_node_set_move_constructor_from_concurrent_node_set[unordered_node_set](concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_node_set_initializer_list_constructor[unordered_node_set](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -489,6 +490,24 @@ and always calls `other.reset_stats()`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_node_set
|
||||
|
||||
```c++
|
||||
unordered_node_set(concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_node_set[`concurrent_node_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`.
|
||||
If statistics are xref:unordered_node_set_boost_unordered_enable_stats[enabled],
|
||||
transfers the internal statistical information from `other` and calls `other.reset_stats()`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -1028,8 +1047,8 @@ Throws:;; Nothing.
|
||||
|
||||
==== Extract by Key
|
||||
```c++
|
||||
node_type erase(const key_type& k);
|
||||
template<class K> node_type erase(K&& k);
|
||||
node_type extract(const key_type& k);
|
||||
template<class K> node_type extract(K&& k);
|
||||
```
|
||||
|
||||
Extracts the element with key equivalent to `k`, if it exists.
|
||||
@@ -1037,7 +1056,7 @@ Extracts the element with key equivalent to `k`, if it exists.
|
||||
[horizontal]
|
||||
Returns:;; A `node_type` object holding the extracted element, or empty if no element was extracted.
|
||||
Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.
|
||||
Notes:;; The `template<class K>` 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.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. 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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -30,18 +30,50 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::grouped_bucket_array<*>" Inheritable="false">
|
||||
<!--
|
||||
The expression `&**p` is used so the Intrinsic fails to parse for a fancy pointer type.
|
||||
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
|
||||
For a raw pointer, this expression is exactly equivalent to `*p`.
|
||||
For a fancy pointer, this expression will try to call a user-defined `operator*()`, which is not allowed in Natvis, and it will fail.
|
||||
-->
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<!--
|
||||
The casting expression `(xyz_pointer)p` is used so the Intrinsic fails to parse for a fancy pointer type.
|
||||
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
|
||||
In this case, `(xyz_pointer)p` is either a no-op for a raw pointer type, or it's an invalid expression.
|
||||
-->
|
||||
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer)p) + n">
|
||||
<Parameter Name="p" Type="bucket_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="bucket_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
|
||||
<Expand>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="size" InitialValue="size_" />
|
||||
<Variable Name="bucket_index" InitialValue="0" />
|
||||
<Variable Name="current_bucket" InitialValue="&buckets[bucket_index]" />
|
||||
<Variable Name="node" InitialValue="current_bucket->next" />
|
||||
<Variable Name="current_bucket" InitialValue="to_address(&buckets)" />
|
||||
<Variable Name="node" InitialValue="to_address(&current_bucket->next)" />
|
||||
<Loop Condition="bucket_index != size">
|
||||
<Exec>current_bucket = &buckets[bucket_index]</Exec>
|
||||
<Exec>node = current_bucket->next</Exec>
|
||||
<Exec>current_bucket = next(to_address(&buckets), bucket_index)</Exec>
|
||||
<Exec>node = to_address(&current_bucket->next)</Exec>
|
||||
<Loop Condition="node != nullptr">
|
||||
<Item>node->buf.t_</Item>
|
||||
<Exec>node = node->next</Exec>
|
||||
<Exec>node = to_address(&node->next)</Exec>
|
||||
</Loop>
|
||||
<Exec>++bucket_index</Exec>
|
||||
</Loop>
|
||||
@@ -54,14 +86,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="size" InitialValue="size_" />
|
||||
<Variable Name="bucket_index" InitialValue="0" />
|
||||
<Variable Name="current_bucket" InitialValue="&buckets[bucket_index]" />
|
||||
<Variable Name="node" InitialValue="current_bucket->next" />
|
||||
<Variable Name="current_bucket" InitialValue="to_address(&buckets)" />
|
||||
<Variable Name="node" InitialValue="to_address(&current_bucket->next)" />
|
||||
<Loop Condition="bucket_index != size">
|
||||
<Exec>current_bucket = &buckets[bucket_index]</Exec>
|
||||
<Exec>node = current_bucket->next</Exec>
|
||||
<Exec>current_bucket = next(to_address(&buckets), bucket_index)</Exec>
|
||||
<Exec>node = to_address(&current_bucket->next)</Exec>
|
||||
<Loop Condition="node != nullptr">
|
||||
<Item Name="[{node->buf.t_.first}]">node->buf.t_</Item>
|
||||
<Exec>node = node->next</Exec>
|
||||
<Exec>node = to_address(&node->next)</Exec>
|
||||
</Loop>
|
||||
<Exec>++bucket_index</Exec>
|
||||
</Loop>
|
||||
@@ -99,29 +131,46 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
<Type Name="boost::unordered::detail::iterator_detail::iterator<*>" Inheritable="false">
|
||||
<AlternativeType Name="boost::unordered::detail::iterator_detail::c_iterator<*>" />
|
||||
<Intrinsic Name="valid" Expression="p != nullptr && itb.p != nullptr && itb.pbg != nullptr" />
|
||||
<DisplayString Condition="valid()">{p->buf.t_}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="valid" Expression="to_address(&p) && to_address(&itb.p) && to_address(&itb.pbg)" />
|
||||
<DisplayString Condition="valid()">{to_address(&p)->buf.t_}</DisplayString>
|
||||
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valid()">p->buf.t_</ExpandedItem>
|
||||
<ExpandedItem Condition="valid()">to_address(&p)->buf.t_</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- FOA and CFOA helpers -->
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::element_type<*>" Priority="Medium" Inheritable="false">
|
||||
<DisplayString>{*p}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="get" Expression="to_address(&p)" />
|
||||
|
||||
<DisplayString>{*get()}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>*p</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::element_type<std::pair<*>,*>" Priority="MediumHigh" Inheritable="false">
|
||||
<!-- Manually expand when holding a `std::pair`, otherwise the debugger complains about recursion-->
|
||||
<DisplayString>({p->first}, {p->second})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="first">p->first</Item>
|
||||
<Item Name="second">p->second</Item>
|
||||
<ExpandedItem>*get()</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
@@ -227,10 +276,39 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Type Name="boost::unordered::detail::foa::table<*>" Inheritable="false">
|
||||
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table<*>" />
|
||||
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer)p) + n">
|
||||
<Parameter Name="p" Type="arrays_type::value_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer)p) + n">
|
||||
<Parameter Name="p" Type="unsigned char*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="arrays_type::value_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="unsigned char*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e">
|
||||
<Parameter Name="e" Type="value_type*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e->p">
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e->get()">
|
||||
<Parameter Name="e" Type="element_type*" />
|
||||
</Intrinsic>
|
||||
|
||||
@@ -254,8 +332,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Expand>
|
||||
<Item Name="[stats]" Optional="true">cstats</Item>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(arrays.groups_)" />
|
||||
<Variable Name="p_" InitialValue="arrays.elements_" />
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(to_address(&arrays.groups_))" />
|
||||
<Variable Name="p_" InitialValue="to_address(&arrays.elements_)" />
|
||||
<Variable Name="first_time" InitialValue="true" />
|
||||
<Variable Name="mask" InitialValue="(int)0" />
|
||||
<Variable Name="n0" InitialValue="(size_t)0" />
|
||||
@@ -264,18 +342,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Loop Condition="p_ != nullptr">
|
||||
|
||||
<!-- This if block mirrors the condition in the begin() call -->
|
||||
<If Condition="!first_time || !(arrays.elements_ && !(arrays.groups_[0].match_occupied() & 0x1))">
|
||||
<If Condition="!first_time || !(p_ && !(to_address(&arrays.groups_)[0].match_occupied() & 0x1))">
|
||||
<Item>*p_</Item>
|
||||
</If>
|
||||
<Exec>first_time = false</Exec>
|
||||
|
||||
<Exec>n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type)</Exec>
|
||||
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
|
||||
|
||||
<Exec>mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1)</Exec>
|
||||
<Loop Condition="mask == 0">
|
||||
<Exec>pc_ += sizeof(group_type)</Exec>
|
||||
<Exec>p_ += group_type::N</Exec>
|
||||
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
|
||||
<Exec>p_ = next(p_, group_type::N)</Exec>
|
||||
<Exec>mask = reinterpret_cast<group_type*>(pc_)->match_occupied()</Exec>
|
||||
</Loop>
|
||||
|
||||
@@ -284,9 +362,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Exec>p_ = nullptr</Exec>
|
||||
</If>
|
||||
<Else>
|
||||
<Exec>pc_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>p_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>p_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
|
||||
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
|
||||
</Else>
|
||||
|
||||
</Loop>
|
||||
@@ -299,8 +376,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Expand>
|
||||
<Item Name="[stats]" Optional="true">cstats</Item>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(arrays.groups_)" />
|
||||
<Variable Name="p_" InitialValue="arrays.elements_" />
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(to_address(&arrays.groups_))" />
|
||||
<Variable Name="p_" InitialValue="to_address(&arrays.elements_)" />
|
||||
<Variable Name="first_time" InitialValue="true" />
|
||||
<Variable Name="mask" InitialValue="(int)0" />
|
||||
<Variable Name="n0" InitialValue="(size_t)0" />
|
||||
@@ -309,18 +386,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Loop Condition="p_ != nullptr">
|
||||
|
||||
<!-- This if block mirrors the condition in the begin() call -->
|
||||
<If Condition="!first_time || !(arrays.elements_ && !(arrays.groups_[0].match_occupied() & 0x1))">
|
||||
<If Condition="!first_time || !(p_ && !(to_address(&arrays.groups_)[0].match_occupied() & 0x1))">
|
||||
<Item Name="[{get_value(p_)->first}]">*p_</Item>
|
||||
</If>
|
||||
<Exec>first_time = false</Exec>
|
||||
|
||||
<Exec>n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type)</Exec>
|
||||
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
|
||||
|
||||
<Exec>mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1)</Exec>
|
||||
<Loop Condition="mask == 0">
|
||||
<Exec>pc_ += sizeof(group_type)</Exec>
|
||||
<Exec>p_ += group_type::N</Exec>
|
||||
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
|
||||
<Exec>p_ = next(p_, group_type::N)</Exec>
|
||||
<Exec>mask = reinterpret_cast<group_type*>(pc_)->match_occupied()</Exec>
|
||||
</Loop>
|
||||
|
||||
@@ -329,9 +406,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Exec>p_ = nullptr</Exec>
|
||||
</If>
|
||||
<Else>
|
||||
<Exec>pc_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>p_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>p_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
|
||||
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
|
||||
</Else>
|
||||
|
||||
</Loop>
|
||||
@@ -345,6 +421,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<AlternativeType Name="boost::unordered::unordered_node_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_set<*>" />
|
||||
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast<hasher*>(static_cast<table_type::super::hash_base*>(&table_))</Item>
|
||||
@@ -357,6 +435,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Type Name="boost::unordered::unordered_flat_map<*>" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
|
||||
<AlternativeType Name="boost::unordered::unordered_node_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_map<*>" />
|
||||
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast<hasher*>(static_cast<table_type::super::hash_base*>(&table_))</Item>
|
||||
@@ -369,12 +448,62 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<!-- FOA iterators -->
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::table_iterator<*>" Inheritable="false">
|
||||
<Intrinsic Name="valid" Expression="p_ != nullptr && pc_ != nullptr" />
|
||||
<DisplayString Condition="valid()">{*p_}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="table_element_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="table_element_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="char_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="char_pointer*" />
|
||||
</Intrinsic>
|
||||
|
||||
<Intrinsic Name="valid" Expression="to_address(&p_) != nullptr && to_address(&pc_) != nullptr" />
|
||||
<DisplayString Condition="valid()">{*to_address(&p_)}</DisplayString>
|
||||
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valid()">*p_</ExpandedItem>
|
||||
<ExpandedItem Condition="valid()">*to_address(&p_)</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- Fancy pointer support -->
|
||||
|
||||
<!--
|
||||
To allow your own fancy pointer type to interact with Boost.Unordered Natvis,
|
||||
add the following intrinsics to your type, with the following conditions.
|
||||
|
||||
(Note, this is assuming the presence of a type alias `pointer` for the underlying
|
||||
raw pointer type, and a type alias `difference_type` for your fancy pointer
|
||||
difference type. Substitute whichever names are applicable in your case.)
|
||||
|
||||
`boost_to_address`
|
||||
* Takes no parameters
|
||||
* Returns the raw pointer equivalent to your fancy pointer
|
||||
|
||||
`boost_next`
|
||||
* Parameter 1, an underlying raw pointer of type `pointer`
|
||||
* Parameter 2, an offset of type `difference_type`
|
||||
* Returns the raw pointer equivalent to your fancy pointer, as if you did the following operations
|
||||
1. Convert the incoming raw pointer to your fancy pointer
|
||||
2. Use operator+= to add the offset to the fancy pointer
|
||||
3. Convert back to the raw pointer
|
||||
* Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above.
|
||||
|
||||
Example
|
||||
```
|
||||
<Type Name="my_fancy_ptr<*>">
|
||||
...
|
||||
<Intrinsic Name="boost_to_address" ReturnType="pointer" Expression="..." />
|
||||
<Intrinsic Name="boost_next" ReturnType="pointer" Expression="...">
|
||||
<Parameter Name="ptr" Type="pointer" />
|
||||
<Parameter Name="offset" Type="difference_type" />
|
||||
</Intrinsic>
|
||||
...
|
||||
</Type>
|
||||
```
|
||||
-->
|
||||
|
||||
</AutoVisualizer>
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
# Copyright 2024 Braden Ganetsky
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
import gdb.printing
|
||||
import gdb.xmethod
|
||||
import re
|
||||
import math
|
||||
|
||||
class BoostUnorderedHelpers:
|
||||
def maybe_unwrap_atomic(n):
|
||||
if f"{n.type.strip_typedefs()}".startswith("std::atomic<"):
|
||||
underlying_type = n.type.template_argument(0)
|
||||
return n.cast(underlying_type)
|
||||
else:
|
||||
return n
|
||||
|
||||
def maybe_unwrap_foa_element(e):
|
||||
if f"{e.type.strip_typedefs()}".startswith("boost::unordered::detail::foa::element_type<"):
|
||||
return e["p"]
|
||||
else:
|
||||
return e
|
||||
|
||||
def maybe_unwrap_reference(value):
|
||||
if value.type.code == gdb.TYPE_CODE_REF:
|
||||
return value.referenced_value()
|
||||
else:
|
||||
return value
|
||||
|
||||
def countr_zero(n):
|
||||
for i in range(32):
|
||||
if (n & (1 << i)) != 0:
|
||||
return i
|
||||
return 32
|
||||
|
||||
class BoostUnorderedPointerCustomizationPoint:
|
||||
def __init__(self, any_ptr):
|
||||
vis = gdb.default_visualizer(any_ptr)
|
||||
if vis is None:
|
||||
self.to_address = lambda ptr: ptr
|
||||
self.next = lambda ptr, offset: ptr + offset
|
||||
else:
|
||||
self.to_address = lambda ptr: ptr if (ptr.type.code == gdb.TYPE_CODE_PTR) else type(vis).boost_to_address(ptr)
|
||||
self.next = lambda ptr, offset: type(vis).boost_next(ptr, offset)
|
||||
|
||||
class BoostUnorderedFcaPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)
|
||||
self.name = f"{self.val.type.strip_typedefs()}".split("<")[0]
|
||||
self.name = self.name.replace("boost::unordered::", "boost::")
|
||||
self.is_map = self.name.endswith("map")
|
||||
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["table_"]["buckets_"]["buckets"])
|
||||
|
||||
def to_string(self):
|
||||
size = self.val["table_"]["size_"]
|
||||
return f"{self.name} with {size} elements"
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
def children(self):
|
||||
def generator():
|
||||
grouped_buckets = self.val["table_"]["buckets_"]
|
||||
|
||||
size = grouped_buckets["size_"]
|
||||
buckets = grouped_buckets["buckets"]
|
||||
bucket_index = 0
|
||||
|
||||
count = 0
|
||||
while bucket_index != size:
|
||||
current_bucket = self.cpo.next(self.cpo.to_address(buckets), bucket_index)
|
||||
node = self.cpo.to_address(current_bucket.dereference()["next"])
|
||||
while node != 0:
|
||||
value = node.dereference()["buf"]["t_"]
|
||||
if self.is_map:
|
||||
first = value["first"]
|
||||
second = value["second"]
|
||||
yield "", first
|
||||
yield "", second
|
||||
else:
|
||||
yield "", count
|
||||
yield "", value
|
||||
count += 1
|
||||
node = self.cpo.to_address(node.dereference()["next"])
|
||||
bucket_index += 1
|
||||
|
||||
return generator()
|
||||
|
||||
class BoostUnorderedFcaIteratorPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["p"])
|
||||
|
||||
def to_string(self):
|
||||
if self.valid():
|
||||
value = self.cpo.to_address(self.val["p"]).dereference()["buf"]["t_"]
|
||||
return f"iterator = {{ {value} }}"
|
||||
else:
|
||||
return "iterator = { end iterator }"
|
||||
|
||||
def valid(self):
|
||||
return (self.cpo.to_address(self.val["p"]) != 0) and (self.cpo.to_address(self.val["itb"]["p"]) != 0)
|
||||
|
||||
class BoostUnorderedFoaTableCoreCumulativeStatsPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "[stats]"
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
def children(self):
|
||||
def generator():
|
||||
members = ["insertion", "successful_lookup", "unsuccessful_lookup"]
|
||||
for member in members:
|
||||
yield "", member
|
||||
yield "", self.val[member]
|
||||
return generator()
|
||||
|
||||
class BoostUnorderedFoaCumulativeStatsPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.n = self.val["n"]
|
||||
self.N = self.val.type.template_argument(0)
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
def children(self):
|
||||
def generator():
|
||||
yield "", "count"
|
||||
yield "", self.n
|
||||
|
||||
sequence_stats_data = gdb.lookup_type("boost::unordered::detail::foa::sequence_stats_data")
|
||||
data = self.val["data"]
|
||||
arr = data.address.reinterpret_cast(sequence_stats_data.pointer())
|
||||
def build_string(idx):
|
||||
entry = arr[idx]
|
||||
avg = float(entry["m"])
|
||||
var = float(entry["s"] / self.n) if (self.n != 0) else 0.0
|
||||
dev = math.sqrt(var)
|
||||
return f"{{avg = {avg}, var = {var}, dev = {dev}}}"
|
||||
|
||||
if self.N > 0:
|
||||
yield "", "probe_length"
|
||||
yield "", build_string(0)
|
||||
if self.N > 1:
|
||||
yield "", "num_comparisons"
|
||||
yield "", build_string(1)
|
||||
|
||||
return generator()
|
||||
|
||||
class BoostUnorderedFoaPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)
|
||||
self.name = f"{self.val.type.strip_typedefs()}".split("<")[0]
|
||||
self.name = self.name.replace("boost::unordered::", "boost::")
|
||||
self.is_map = self.name.endswith("map")
|
||||
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["table_"]["arrays"]["groups_"])
|
||||
|
||||
def to_string(self):
|
||||
size = BoostUnorderedHelpers.maybe_unwrap_atomic(self.val["table_"]["size_ctrl"]["size"])
|
||||
return f"{self.name} with {size} elements"
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
def is_regular_layout(self, group):
|
||||
typename = group["m"].type.strip_typedefs()
|
||||
array_size = typename.sizeof // typename.target().sizeof
|
||||
if array_size == 16:
|
||||
return True
|
||||
elif array_size == 2:
|
||||
return False
|
||||
|
||||
def match_occupied(self, group):
|
||||
m = group["m"]
|
||||
at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b]["n"])
|
||||
|
||||
if self.is_regular_layout(group):
|
||||
bits = [1 << b for b in range(16) if at(b) == 0]
|
||||
return 0x7FFF & ~sum(bits)
|
||||
else:
|
||||
xx = at(0) | at(1)
|
||||
yy = xx | (xx >> 32)
|
||||
return 0x7FFF & (yy | (yy >> 16))
|
||||
|
||||
def is_sentinel(self, group, pos):
|
||||
m = group["m"]
|
||||
at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b]["n"])
|
||||
|
||||
N = group["N"]
|
||||
sentinel_ = group["sentinel_"]
|
||||
if self.is_regular_layout(group):
|
||||
return pos == N-1 and at(N-1) == sentinel_
|
||||
else:
|
||||
return pos == N-1 and (at(0) & 0x4000400040004000) == 0x4000 and (at(1) & 0x4000400040004000) == 0
|
||||
|
||||
def children(self):
|
||||
def generator():
|
||||
table = self.val["table_"]
|
||||
groups = self.cpo.to_address(table["arrays"]["groups_"])
|
||||
elements = self.cpo.to_address(table["arrays"]["elements_"])
|
||||
|
||||
pc_ = groups.cast(gdb.lookup_type("unsigned char").pointer())
|
||||
p_ = elements
|
||||
first_time = True
|
||||
mask = 0
|
||||
n0 = 0
|
||||
n = 0
|
||||
|
||||
count = 0
|
||||
while p_ != 0:
|
||||
# This if block mirrors the condition in the begin() call
|
||||
if (not first_time) or (self.match_occupied(groups.dereference()) & 1):
|
||||
pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(p_)
|
||||
value = self.cpo.to_address(pointer).dereference()
|
||||
if self.is_map:
|
||||
first = value["first"]
|
||||
second = value["second"]
|
||||
yield "", first
|
||||
yield "", second
|
||||
else:
|
||||
yield "", count
|
||||
yield "", value
|
||||
count += 1
|
||||
first_time = False
|
||||
|
||||
n0 = pc_.cast(gdb.lookup_type("uintptr_t")) % groups.dereference().type.sizeof
|
||||
pc_ = self.cpo.next(pc_, -n0)
|
||||
|
||||
mask = (self.match_occupied(pc_.cast(groups.type).dereference()) >> (n0+1)) << (n0+1)
|
||||
while mask == 0:
|
||||
pc_ = self.cpo.next(pc_, groups.dereference().type.sizeof)
|
||||
p_ = self.cpo.next(p_, groups.dereference()["N"])
|
||||
mask = self.match_occupied(pc_.cast(groups.type).dereference())
|
||||
|
||||
n = BoostUnorderedHelpers.countr_zero(mask)
|
||||
if self.is_sentinel(pc_.cast(groups.type).dereference(), n):
|
||||
p_ = 0
|
||||
else:
|
||||
pc_ = self.cpo.next(pc_, n)
|
||||
p_ = self.cpo.next(p_, n - n0)
|
||||
|
||||
return generator()
|
||||
|
||||
class BoostUnorderedFoaIteratorPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["p_"])
|
||||
|
||||
def to_string(self):
|
||||
if self.valid():
|
||||
element = self.cpo.to_address(self.val["p_"])
|
||||
pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(element)
|
||||
value = self.cpo.to_address(pointer).dereference()
|
||||
return f"iterator = {{ {value} }}"
|
||||
else:
|
||||
return "iterator = { end iterator }"
|
||||
|
||||
def valid(self):
|
||||
return (self.cpo.to_address(self.val["p_"]) != 0) and (self.cpo.to_address(self.val["pc_"]) != 0)
|
||||
|
||||
def boost_unordered_build_pretty_printer():
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("boost_unordered")
|
||||
add_template_printer = lambda name, printer: pp.add_printer(name, f"^{name}<.*>$", printer)
|
||||
add_concrete_printer = lambda name, printer: pp.add_printer(name, f"^{name}$", printer)
|
||||
|
||||
add_template_printer("boost::unordered::unordered_map", BoostUnorderedFcaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_multimap", BoostUnorderedFcaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_set", BoostUnorderedFcaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_multiset", BoostUnorderedFcaPrinter)
|
||||
|
||||
add_template_printer("boost::unordered::detail::iterator_detail::iterator", BoostUnorderedFcaIteratorPrinter)
|
||||
add_template_printer("boost::unordered::detail::iterator_detail::c_iterator", BoostUnorderedFcaIteratorPrinter)
|
||||
|
||||
add_template_printer("boost::unordered::unordered_flat_map", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_flat_set", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_node_map", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_node_set", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::concurrent_flat_map", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::concurrent_flat_set", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::concurrent_node_map", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::concurrent_node_set", BoostUnorderedFoaPrinter)
|
||||
|
||||
add_template_printer("boost::unordered::detail::foa::table_iterator", BoostUnorderedFoaIteratorPrinter)
|
||||
|
||||
add_concrete_printer("boost::unordered::detail::foa::table_core_cumulative_stats", BoostUnorderedFoaTableCoreCumulativeStatsPrinter)
|
||||
add_template_printer("boost::unordered::detail::foa::cumulative_stats", BoostUnorderedFoaCumulativeStatsPrinter)
|
||||
add_template_printer("boost::unordered::detail::foa::concurrent_cumulative_stats", BoostUnorderedFoaCumulativeStatsPrinter)
|
||||
|
||||
return pp
|
||||
|
||||
gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_unordered_build_pretty_printer())
|
||||
|
||||
|
||||
|
||||
# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html
|
||||
class BoostUnorderedFoaGetStatsMethod(gdb.xmethod.XMethod):
|
||||
def __init__(self):
|
||||
gdb.xmethod.XMethod.__init__(self, "get_stats")
|
||||
|
||||
def get_worker(self, method_name):
|
||||
if method_name == "get_stats":
|
||||
return BoostUnorderedFoaGetStatsWorker()
|
||||
|
||||
class BoostUnorderedFoaGetStatsWorker(gdb.xmethod.XMethodWorker):
|
||||
def get_arg_types(self):
|
||||
return None
|
||||
|
||||
def get_result_type(self, obj):
|
||||
return gdb.lookup_type("boost::unordered::detail::foa::table_core_cumulative_stats")
|
||||
|
||||
def __call__(self, obj):
|
||||
try:
|
||||
return obj["table_"]["cstats"]
|
||||
except gdb.error:
|
||||
print("Error: Binary was compiled without stats. Recompile with `BOOST_UNORDERED_ENABLE_STATS` defined.")
|
||||
return
|
||||
|
||||
class BoostUnorderedFoaMatcher(gdb.xmethod.XMethodMatcher):
|
||||
def __init__(self):
|
||||
gdb.xmethod.XMethodMatcher.__init__(self, 'BoostUnorderedFoaMatcher')
|
||||
self.methods = [BoostUnorderedFoaGetStatsMethod()]
|
||||
|
||||
def match(self, class_type, method_name):
|
||||
template_name = f"{class_type.strip_typedefs()}".split("<")[0]
|
||||
regex = "^boost::unordered::(unordered|concurrent)_(flat|node)_(map|set)$"
|
||||
if not re.match(regex, template_name):
|
||||
return None
|
||||
|
||||
workers = []
|
||||
for method in self.methods:
|
||||
if method.enabled:
|
||||
worker = method.get_worker(method_name)
|
||||
if worker:
|
||||
workers.append(worker)
|
||||
return workers
|
||||
|
||||
gdb.xmethod.register_xmethod_matcher(None, BoostUnorderedFoaMatcher())
|
||||
|
||||
|
||||
|
||||
""" Fancy pointer support """
|
||||
|
||||
"""
|
||||
To allow your own fancy pointer type to interact with Boost.Unordered GDB pretty-printers,
|
||||
create a pretty-printer for your own type with the following additional methods.
|
||||
|
||||
(Note, this is assuming the presence of a type alias `pointer` for the underlying
|
||||
raw pointer type, Substitute whichever name is applicable in your case.)
|
||||
|
||||
`boost_to_address(fancy_ptr)`
|
||||
* A static method, but `@staticmethod` is not required
|
||||
* Parameter `fancy_ptr` of type `gdb.Value`
|
||||
* Its `.type` will be your fancy pointer type
|
||||
* Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer
|
||||
* This method should be equivalent to calling `operator->()` on your fancy pointer in C++
|
||||
|
||||
`boost_next(raw_ptr, offset)`
|
||||
* Parameter `raw_ptr` of type `gdb.Value`
|
||||
* Its `.type` will be `pointer`
|
||||
* Parameter `offset`
|
||||
* Either has integer type, or is of type `gdb.Value` with an underlying integer
|
||||
* Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer, as if you did the following operations
|
||||
1. Convert the incoming raw pointer to your fancy pointer
|
||||
2. Use operator+= to add the offset to the fancy pointer
|
||||
3. Convert back to the raw pointer
|
||||
* Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above
|
||||
* Ultimately, it will be as if you called `operator+()` on your fancy pointer in C++, but using only raw pointers
|
||||
|
||||
Example
|
||||
```
|
||||
class MyFancyPtrPrinter:
|
||||
...
|
||||
|
||||
# Equivalent to `operator->()`
|
||||
def boost_to_address(fancy_ptr):
|
||||
...
|
||||
return ...
|
||||
|
||||
# Equivalent to `operator+()`
|
||||
def boost_next(raw_ptr, offset):
|
||||
...
|
||||
return ...
|
||||
|
||||
...
|
||||
```
|
||||
"""
|
||||
@@ -190,6 +190,7 @@ namespace boost {
|
||||
}
|
||||
|
||||
|
||||
template <bool avoid_explicit_instantiation = true>
|
||||
concurrent_flat_map(
|
||||
unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
@@ -419,16 +420,18 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator begin, InputIterator end)
|
||||
size_type insert(InputIterator begin, InputIterator end)
|
||||
{
|
||||
for (auto pos = begin; pos != end; ++pos) {
|
||||
size_type count_elements = 0;
|
||||
for (auto pos = begin; pos != end; ++pos, ++count_elements) {
|
||||
table_.emplace(*pos);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist)
|
||||
size_type insert(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
this->insert(ilist.begin(), ilist.end());
|
||||
return this->insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
template <class M>
|
||||
@@ -471,19 +474,21 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_visit(InputIterator first, InputIterator last, F f)
|
||||
size_type insert_or_visit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
size_type count_elements = 0;
|
||||
for (; first != last; ++first, ++count_elements) {
|
||||
table_.emplace_or_visit(*first, f);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
size_type insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
return this->insert_or_visit(ilist.begin(), ilist.end(), std::ref(f));
|
||||
}
|
||||
|
||||
template <class Ty, class F>
|
||||
@@ -502,19 +507,101 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
|
||||
size_type insert_or_cvisit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
size_type count_elements = 0;
|
||||
for (; first != last; ++first, ++count_elements) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
|
||||
size_type 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);
|
||||
return this->insert_or_cvisit(ilist.begin(), ilist.end(), std::ref(f));
|
||||
}
|
||||
|
||||
template <class Ty, class F1, class F2>
|
||||
BOOST_FORCEINLINE auto insert_and_visit(Ty&& value, F1 f1, F2 f2)
|
||||
-> decltype(table_.insert_and_visit(std::forward<Ty>(value), f1, f2))
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
|
||||
return table_.insert_and_visit(std::forward<Ty>(value), f1, f2);
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
BOOST_FORCEINLINE bool insert_and_visit(init_type&& obj, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
|
||||
return table_.insert_and_visit(std::move(obj), f1, f2);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F1, class F2>
|
||||
size_type insert_and_visit(
|
||||
InputIterator first, InputIterator last, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
|
||||
size_type count_elements = 0;
|
||||
for (; first != last; ++first, ++count_elements) {
|
||||
table_.emplace_and_visit(*first, f1, f2);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
size_type insert_and_visit(
|
||||
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
|
||||
return this->insert_and_visit(
|
||||
ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
|
||||
}
|
||||
|
||||
template <class Ty, class F1, class F2>
|
||||
BOOST_FORCEINLINE auto insert_and_cvisit(Ty&& value, F1 f1, F2 f2)
|
||||
-> decltype(table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2))
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2);
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return table_.insert_and_cvisit(std::move(obj), f1, f2);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F1, class F2>
|
||||
size_type insert_and_cvisit(
|
||||
InputIterator first, InputIterator last, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
size_type count_elements = 0;
|
||||
for (; first != last; ++first, ++count_elements) {
|
||||
table_.emplace_and_cvisit(*first, f1, f2);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
size_type insert_and_cvisit(
|
||||
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return this->insert_and_cvisit(
|
||||
ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
@@ -538,6 +625,30 @@ namespace boost {
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_and_visit(
|
||||
Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
|
||||
return table_.emplace_and_visit(
|
||||
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_and_cvisit(
|
||||
Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
|
||||
return table_.emplace_and_cvisit(
|
||||
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
|
||||
{
|
||||
@@ -613,6 +724,78 @@ namespace boost {
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_and_visit(
|
||||
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
|
||||
return table_.try_emplace_and_visit(
|
||||
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
|
||||
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
|
||||
return table_.try_emplace_and_cvisit(
|
||||
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_and_visit(
|
||||
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
|
||||
return table_.try_emplace_and_visit(
|
||||
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
|
||||
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
|
||||
return table_.try_emplace_and_cvisit(
|
||||
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class K, class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_and_visit(
|
||||
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
|
||||
return table_.try_emplace_and_visit(std::forward<K>(k),
|
||||
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class K, class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
|
||||
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
|
||||
return table_.try_emplace_and_cvisit(std::forward<K>(k),
|
||||
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type erase(key_type const& k)
|
||||
{
|
||||
return table_.erase(k);
|
||||
|
||||
@@ -187,6 +187,7 @@ namespace boost {
|
||||
}
|
||||
|
||||
|
||||
template <bool avoid_explicit_instantiation = true>
|
||||
concurrent_flat_set(
|
||||
unordered_flat_set<Key, Hash, Pred, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
@@ -227,6 +228,13 @@ namespace boost {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
|
||||
{
|
||||
@@ -241,6 +249,15 @@ namespace boost {
|
||||
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)
|
||||
{
|
||||
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
|
||||
@@ -259,6 +276,15 @@ 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_CONST_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
|
||||
@@ -277,6 +303,12 @@ namespace boost {
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
@@ -290,6 +322,16 @@ namespace boost {
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
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
|
||||
@@ -311,6 +353,12 @@ namespace boost {
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> bool visit_while(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool visit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
@@ -324,6 +372,16 @@ namespace boost {
|
||||
}
|
||||
|
||||
#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_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
|
||||
@@ -368,30 +426,32 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator begin, InputIterator end)
|
||||
size_type insert(InputIterator begin, InputIterator end)
|
||||
{
|
||||
for (auto pos = begin; pos != end; ++pos) {
|
||||
size_type count_elements = 0;
|
||||
for (auto pos = begin; pos != end; ++pos, ++count_elements) {
|
||||
table_.emplace(*pos);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist)
|
||||
size_type insert(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
this->insert(ilist.begin(), ilist.end());
|
||||
return 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);
|
||||
return table_.insert_or_visit(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);
|
||||
return table_.insert_or_visit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
@@ -401,23 +461,25 @@ namespace boost {
|
||||
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);
|
||||
return table_.try_emplace_or_visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_visit(InputIterator first, InputIterator last, F f)
|
||||
size_type 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);
|
||||
size_type count_elements = 0;
|
||||
for (; first != last; ++first, ++count_elements) {
|
||||
table_.emplace_or_visit(*first, f);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
size_type 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);
|
||||
return this->insert_or_visit(ilist.begin(), ilist.end(), std::ref(f));
|
||||
}
|
||||
|
||||
template <class F>
|
||||
@@ -445,19 +507,122 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
|
||||
size_type insert_or_cvisit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
size_type count_elements = 0;
|
||||
for (; first != last; ++first, ++count_elements) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
|
||||
size_type 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);
|
||||
return this->insert_or_cvisit(ilist.begin(), ilist.end(), std::ref(f));
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
BOOST_FORCEINLINE bool insert_and_visit(
|
||||
value_type const& obj, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return table_.insert_and_visit(obj, f1, f2);
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
BOOST_FORCEINLINE bool insert_and_visit(value_type&& obj, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return table_.insert_and_visit(std::move(obj), f1, f2);
|
||||
}
|
||||
|
||||
template <class K, class F1, class F2>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert_and_visit(K&& k, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return table_.try_emplace_and_visit(std::forward<K>(k), f1, f2);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F1, class F2>
|
||||
size_type insert_and_visit(
|
||||
InputIterator first, InputIterator last, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
size_type count_elements = 0;
|
||||
for (; first != last; ++first, ++count_elements) {
|
||||
table_.emplace_and_visit(*first, f1, f2);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
size_type insert_and_visit(std::initializer_list<value_type> ilist, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return this->insert_and_visit(
|
||||
ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
BOOST_FORCEINLINE bool insert_and_cvisit(
|
||||
value_type const& obj, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return table_.insert_and_cvisit(obj, f1, f2);
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
BOOST_FORCEINLINE bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return table_.insert_and_cvisit(std::move(obj), f1, f2);
|
||||
}
|
||||
|
||||
template <class K, class F1, class F2>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert_and_cvisit(K&& k, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return table_.try_emplace_and_cvisit(std::forward<K>(k), f1, f2);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F1, class F2>
|
||||
size_type insert_and_cvisit(
|
||||
InputIterator first, InputIterator last, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
size_type count_elements = 0;
|
||||
for (; first != last; ++first, ++count_elements) {
|
||||
table_.emplace_and_cvisit(*first, f1, f2);
|
||||
}
|
||||
return count_elements;
|
||||
}
|
||||
|
||||
template <class F1, class F2>
|
||||
size_type insert_and_cvisit(
|
||||
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
|
||||
return this->insert_and_cvisit(
|
||||
ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
@@ -469,7 +634,7 @@ namespace boost {
|
||||
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(
|
||||
return table_.emplace_or_visit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -481,6 +646,30 @@ namespace boost {
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_and_visit(
|
||||
Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
|
||||
return table_.emplace_and_visit(
|
||||
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_and_cvisit(
|
||||
Arg1&& arg1, Arg2&& arg2, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
|
||||
Arg1, Arg2, Args...)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
|
||||
return table_.emplace_and_cvisit(
|
||||
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type erase(key_type const& k)
|
||||
{
|
||||
return table_.erase(k);
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/* Fast open-addressing, node-based concurrent hashmap.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 Braden Ganetsky.
|
||||
* Copyright 2024 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_NODE_MAP_FWD_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
|
||||
template <class Key, class T, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class concurrent_node_map;
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class T, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
|
||||
concurrent_node_map<Key, T, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename concurrent_node_map<K, T, H, P, A>::size_type erase_if(
|
||||
concurrent_node_map<K, T, H, P, A>& c, Predicate pred);
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
namespace pmr {
|
||||
template <class Key, class T, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key> >
|
||||
using concurrent_node_map = boost::unordered::concurrent_node_map<Key, T,
|
||||
Hash, Pred, std::pmr::polymorphic_allocator<std::pair<Key const, T> > >;
|
||||
} // namespace pmr
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::concurrent_node_map;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP
|
||||
@@ -0,0 +1,67 @@
|
||||
/* Fast open-addressing, node-based concurrent hashset.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023-2024 Joaquin M Lopez Munoz.
|
||||
* Copyright 2024 Braden Ganetsky.
|
||||
* 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_NODE_SET_FWD_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
|
||||
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_node_set;
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x,
|
||||
concurrent_node_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename concurrent_node_set<K, H, P, A>::size_type erase_if(
|
||||
concurrent_node_set<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
namespace pmr {
|
||||
template <class Key, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key> >
|
||||
using concurrent_node_set = boost::unordered::concurrent_node_set<Key,
|
||||
Hash, Pred, std::pmr::polymorphic_allocator<Key> >;
|
||||
} // namespace pmr
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::concurrent_node_set;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
* Copyright 2023-2024 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,10 +13,7 @@
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
|
||||
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
|
||||
@@ -58,16 +55,41 @@
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
|
||||
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
|
||||
|
||||
#define BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args) \
|
||||
mp11::mp_at_c<mp11::mp_list< \
|
||||
Arg1 BOOST_UNORDERED_DETAIL_COMMA Arg2 BOOST_UNORDERED_DETAIL_COMMA Args \
|
||||
>, \
|
||||
mp11::mp_size<mp11::mp_list< \
|
||||
Arg1 BOOST_UNORDERED_DETAIL_COMMA Arg2 BOOST_UNORDERED_DETAIL_COMMA Args \
|
||||
>>::value - 2>
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE( \
|
||||
Arg1, Arg2, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE( \
|
||||
BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args))
|
||||
|
||||
#define BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE( \
|
||||
Arg1, Arg2, Args) \
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
|
||||
BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, 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> >
|
||||
template <class...> struct is_invocable_helper : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class F, class... Args>
|
||||
struct is_invocable_helper<
|
||||
void_t<decltype(std::declval<F>()(std::declval<Args>()...))>, F,
|
||||
Args...> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class F, class... Args>
|
||||
using is_invocable = is_invocable_helper<void, F, Args...>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
@@ -215,7 +215,7 @@ struct atomic_integral
|
||||
|
||||
/* Group-level concurrency protection. It provides a rw mutex plus an
|
||||
* atomic insertion counter for optimistic insertion (see
|
||||
* unprotected_norehash_emplace_or_visit).
|
||||
* unprotected_norehash_emplace_and_visit).
|
||||
*/
|
||||
|
||||
struct group_access
|
||||
@@ -397,10 +397,10 @@ 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.
|
||||
* Heterogeneous lookup is suported by default, that is, without checking for
|
||||
* any ::is_transparent typedefs --this checking is done by the wrapping
|
||||
* containers.
|
||||
* Consult boost::concurrent_(flat|node)_(map|set) 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.
|
||||
*
|
||||
* Thread-safe concurrency is implemented using a two-level lock system:
|
||||
*
|
||||
@@ -724,6 +724,14 @@ public:
|
||||
BOOST_FORCEINLINE bool
|
||||
insert(value_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
template<typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert(element_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace(Key&& x,Args&&... args)
|
||||
{
|
||||
@@ -747,6 +755,22 @@ public:
|
||||
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_and_visit(Key&& x,Args&&... args)
|
||||
{
|
||||
return emplace_and_visit_flast(
|
||||
group_exclusive{},
|
||||
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_and_cvisit(Key&& x,Args&&... args)
|
||||
{
|
||||
return emplace_and_visit_flast(
|
||||
group_shared{},
|
||||
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Args&&... args)
|
||||
{
|
||||
@@ -761,62 +785,121 @@ public:
|
||||
group_shared{},std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(const init_type& x,F&& f)
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE bool emplace_and_visit(Args&&... args)
|
||||
{
|
||||
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x);
|
||||
return construct_and_emplace_and_visit_flast(
|
||||
group_exclusive{},std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(const init_type& x,F&& f)
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE bool emplace_and_cvisit(Args&&... args)
|
||||
{
|
||||
return emplace_or_visit_impl(group_shared{},std::forward<F>(f),x);
|
||||
return construct_and_emplace_and_visit_flast(
|
||||
group_shared{},std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(init_type&& x,F&& f)
|
||||
template<typename Value,typename F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(Value&& x,F&& f)
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_exclusive{},std::forward<F>(f),std::move(x));
|
||||
return insert_and_visit(
|
||||
std::forward<Value>(x),[](const value_type&){},std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(init_type&& x,F&& f)
|
||||
template<typename Value,typename F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(Value&& x,F&& f)
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_shared{},std::forward<F>(f),std::move(x));
|
||||
return insert_and_cvisit(
|
||||
std::forward<Value>(x),[](const value_type&){},std::forward<F>(f));
|
||||
}
|
||||
|
||||
template<typename F1,typename F2>
|
||||
BOOST_FORCEINLINE bool insert_and_visit(const init_type& x,F1&& f1,F2&& f2)
|
||||
{
|
||||
return emplace_and_visit_impl(
|
||||
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),x);
|
||||
}
|
||||
|
||||
template<typename F1,typename F2>
|
||||
BOOST_FORCEINLINE bool insert_and_cvisit(const init_type& x,F1&& f1,F2&& f2)
|
||||
{
|
||||
return emplace_and_visit_impl(
|
||||
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),x);
|
||||
}
|
||||
|
||||
template<typename F1,typename F2>
|
||||
BOOST_FORCEINLINE bool insert_and_visit(init_type&& x,F1&& f1,F2&& f2)
|
||||
{
|
||||
return emplace_and_visit_impl(
|
||||
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
|
||||
std::move(x));
|
||||
}
|
||||
|
||||
template<typename F1,typename F2>
|
||||
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& x,F1&& f1,F2&& f2)
|
||||
{
|
||||
return emplace_and_visit_impl(
|
||||
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
|
||||
}
|
||||
|
||||
/* SFINAE tilts call ambiguities in favor of init_type */
|
||||
|
||||
template<typename Value,typename F>
|
||||
BOOST_FORCEINLINE auto insert_or_visit(const Value& x,F&& f)
|
||||
template<typename Value,typename F1,typename F2>
|
||||
BOOST_FORCEINLINE auto insert_and_visit(const Value& x,F1&& f1,F2&& f2)
|
||||
->enable_if_is_value_type<Value,bool>
|
||||
{
|
||||
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x);
|
||||
return emplace_and_visit_impl(
|
||||
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),x);
|
||||
}
|
||||
|
||||
template<typename Value,typename F>
|
||||
BOOST_FORCEINLINE auto insert_or_cvisit(const Value& x,F&& f)
|
||||
template<typename Value,typename F1,typename F2>
|
||||
BOOST_FORCEINLINE auto insert_and_cvisit(const Value& x,F1&& f1,F2&& f2)
|
||||
->enable_if_is_value_type<Value,bool>
|
||||
{
|
||||
return emplace_or_visit_impl(group_shared{},std::forward<F>(f),x);
|
||||
return emplace_and_visit_impl(
|
||||
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),x);
|
||||
}
|
||||
|
||||
template<typename Value,typename F>
|
||||
BOOST_FORCEINLINE auto insert_or_visit(Value&& x,F&& f)
|
||||
template<typename Value,typename F1,typename F2>
|
||||
BOOST_FORCEINLINE auto insert_and_visit(Value&& x,F1&& f1,F2&& f2)
|
||||
->enable_if_is_value_type<Value,bool>
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_exclusive{},std::forward<F>(f),std::move(x));
|
||||
return emplace_and_visit_impl(
|
||||
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
|
||||
std::move(x));
|
||||
}
|
||||
|
||||
template<typename Value,typename F>
|
||||
BOOST_FORCEINLINE auto insert_or_cvisit(Value&& x,F&& f)
|
||||
template<typename Value,typename F1,typename F2>
|
||||
BOOST_FORCEINLINE auto insert_and_cvisit(Value&& x,F1&& f1,F2&& f2)
|
||||
->enable_if_is_value_type<Value,bool>
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_shared{},std::forward<F>(f),std::move(x));
|
||||
return emplace_and_visit_impl(
|
||||
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
|
||||
}
|
||||
|
||||
template<typename F1,typename F2,typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert_and_visit(element_type&& x,F1&& f1,F2&& f2)
|
||||
{
|
||||
return emplace_and_visit_impl(
|
||||
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
|
||||
std::move(x));
|
||||
}
|
||||
|
||||
template<typename F1,typename F2,typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert_and_cvisit(element_type&& x,F1&& f1,F2&& f2)
|
||||
{
|
||||
return emplace_and_visit_impl(
|
||||
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
@@ -889,6 +972,29 @@ public:
|
||||
super::clear();
|
||||
}
|
||||
|
||||
template<typename Key,typename Extractor>
|
||||
BOOST_FORCEINLINE void extract(const Key& x,Extractor&& ext)
|
||||
{
|
||||
extract_if(
|
||||
x,[](const value_type&){return true;},std::forward<Extractor>(ext));
|
||||
}
|
||||
|
||||
template<typename Key,typename F,typename Extractor>
|
||||
BOOST_FORCEINLINE void extract_if(const Key& x,F&& f,Extractor&& ext)
|
||||
{
|
||||
auto lck=shared_access();
|
||||
auto hash=this->hash_for(x);
|
||||
unprotected_internal_visit(
|
||||
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)))){
|
||||
ext(std::move(*p),this->al());
|
||||
super::erase(pg,n,p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: should we accept different allocator too?
|
||||
template<typename Hash2,typename Pred2>
|
||||
size_type merge(concurrent_table<TypePolicy,Hash2,Pred2,Allocator>& x)
|
||||
@@ -1353,23 +1459,59 @@ private:
|
||||
);
|
||||
}
|
||||
|
||||
struct call_construct_and_emplace_and_visit
|
||||
{
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE bool operator()(
|
||||
concurrent_table* this_,Args&&... args)const
|
||||
{
|
||||
return this_->construct_and_emplace_and_visit(
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename GroupAccessMode,typename... Args>
|
||||
BOOST_FORCEINLINE bool construct_and_emplace_and_visit_flast(
|
||||
GroupAccessMode access_mode,Args&&... args)
|
||||
{
|
||||
return mp11::tuple_apply(
|
||||
call_construct_and_emplace_and_visit{},
|
||||
std::tuple_cat(
|
||||
std::make_tuple(this,access_mode),
|
||||
tuple_rotate_right<2>(
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F,typename... Args>
|
||||
BOOST_FORCEINLINE bool construct_and_emplace_or_visit(
|
||||
GroupAccessMode access_mode,F&& f,Args&&... args)
|
||||
{
|
||||
return construct_and_emplace_and_visit(
|
||||
access_mode,[](const value_type&){},std::forward<F>(f),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
|
||||
BOOST_FORCEINLINE bool construct_and_emplace_and_visit(
|
||||
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
|
||||
{
|
||||
auto lck=shared_access();
|
||||
|
||||
alloc_cted_insert_type<type_policy,Allocator,Args...> x(
|
||||
this->al(),std::forward<Args>(args)...);
|
||||
int res=unprotected_norehash_emplace_or_visit(
|
||||
access_mode,std::forward<F>(f),type_policy::move(x.value()));
|
||||
int res=unprotected_norehash_emplace_and_visit(
|
||||
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
|
||||
type_policy::move(x.value()));
|
||||
if(BOOST_LIKELY(res>=0))return res!=0;
|
||||
|
||||
lck.unlock();
|
||||
|
||||
rehash_if_full();
|
||||
return noinline_emplace_or_visit(
|
||||
access_mode,std::forward<F>(f),type_policy::move(x.value()));
|
||||
return noinline_emplace_and_visit(
|
||||
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
|
||||
type_policy::move(x.value()));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
@@ -1387,6 +1529,15 @@ private:
|
||||
access_mode,std::forward<F>(f),std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
|
||||
BOOST_NOINLINE bool noinline_emplace_and_visit(
|
||||
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
|
||||
{
|
||||
return emplace_and_visit_impl(
|
||||
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
struct call_emplace_or_visit_impl
|
||||
{
|
||||
template<typename... Args>
|
||||
@@ -1410,15 +1561,49 @@ private:
|
||||
);
|
||||
}
|
||||
|
||||
struct call_emplace_and_visit_impl
|
||||
{
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE bool operator()(
|
||||
concurrent_table* this_,Args&&... args)const
|
||||
{
|
||||
return this_->emplace_and_visit_impl(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename GroupAccessMode,typename... Args>
|
||||
BOOST_FORCEINLINE bool emplace_and_visit_flast(
|
||||
GroupAccessMode access_mode,Args&&... args)
|
||||
{
|
||||
return mp11::tuple_apply(
|
||||
call_emplace_and_visit_impl{},
|
||||
std::tuple_cat(
|
||||
std::make_tuple(this,access_mode),
|
||||
tuple_rotate_right<2>(
|
||||
std::forward_as_tuple(std::forward<Args>(args)...))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F,typename... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_visit_impl(
|
||||
GroupAccessMode access_mode,F&& f,Args&&... args)
|
||||
{
|
||||
return emplace_and_visit_impl(
|
||||
access_mode,[](const value_type&){},std::forward<F>(f),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
|
||||
BOOST_FORCEINLINE bool emplace_and_visit_impl(
|
||||
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
|
||||
{
|
||||
for(;;){
|
||||
{
|
||||
auto lck=shared_access();
|
||||
int res=unprotected_norehash_emplace_or_visit(
|
||||
access_mode,std::forward<F>(f),std::forward<Args>(args)...);
|
||||
int res=unprotected_norehash_emplace_and_visit(
|
||||
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
|
||||
std::forward<Args>(args)...);
|
||||
if(BOOST_LIKELY(res>=0))return res!=0;
|
||||
}
|
||||
rehash_if_full();
|
||||
@@ -1443,6 +1628,16 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename GroupAccessMode,typename F,typename... Args>
|
||||
BOOST_FORCEINLINE int
|
||||
unprotected_norehash_emplace_or_visit(
|
||||
GroupAccessMode access_mode,F&& f,Args&&... args)
|
||||
{
|
||||
return unprotected_norehash_emplace_and_visit(
|
||||
access_mode,[&](const value_type&){},
|
||||
std::forward<F>(f),std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
struct reserve_size
|
||||
{
|
||||
reserve_size(concurrent_table& x_):x(x_)
|
||||
@@ -1484,10 +1679,10 @@ private:
|
||||
bool commit_=false;
|
||||
};
|
||||
|
||||
template<typename GroupAccessMode,typename F,typename... Args>
|
||||
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
|
||||
BOOST_FORCEINLINE int
|
||||
unprotected_norehash_emplace_or_visit(
|
||||
GroupAccessMode access_mode,F&& f,Args&&... args)
|
||||
unprotected_norehash_emplace_and_visit(
|
||||
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
|
||||
{
|
||||
const auto &k=this->key_from(std::forward<Args>(args)...);
|
||||
auto hash=this->hash_for(k);
|
||||
@@ -1497,7 +1692,7 @@ private:
|
||||
startover:
|
||||
boost::uint32_t counter=insert_counter(pos0);
|
||||
if(unprotected_visit(
|
||||
access_mode,k,pos0,hash,std::forward<F>(f)))return 0;
|
||||
access_mode,k,pos0,hash,std::forward<F2>(f2)))return 0;
|
||||
|
||||
reserve_size rsize(*this);
|
||||
if(BOOST_LIKELY(rsize.succeeded())){
|
||||
@@ -1517,6 +1712,7 @@ private:
|
||||
this->construct_element(p,std::forward<Args>(args)...);
|
||||
rslot.commit();
|
||||
rsize.commit();
|
||||
f1(cast_for(group_exclusive{},type_policy::value_from(*p)));
|
||||
BOOST_UNORDERED_ADD_STATS(this->cstats.insertion,(pb.length()));
|
||||
return 1;
|
||||
}
|
||||
@@ -1733,7 +1929,8 @@ private:
|
||||
|
||||
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));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p)),std::addressof(x));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1742,7 +1939,7 @@ private:
|
||||
{
|
||||
using raw_key_type=typename std::remove_const<key_type>::type;
|
||||
using raw_mapped_type=typename std::remove_const<
|
||||
typename TypePolicy::mapped_type>::type;
|
||||
typename type_policy::mapped_type>::type;
|
||||
|
||||
auto lck=exclusive_access();
|
||||
std::size_t s;
|
||||
@@ -1766,8 +1963,12 @@ private:
|
||||
|
||||
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));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p).first),
|
||||
std::addressof(k));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p).second),
|
||||
std::addressof(m));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <boost/unordered/detail/static_assert.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/hash_traits.hpp>
|
||||
#include <boost/unordered/unordered_printers.hpp>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
@@ -1017,6 +1018,11 @@ struct table_arrays
|
||||
rebind<group_type>;
|
||||
using group_type_pointer_traits=boost::pointer_traits<group_type_pointer>;
|
||||
|
||||
// For natvis purposes
|
||||
using char_pointer=
|
||||
typename boost::pointer_traits<value_type_pointer>::template
|
||||
rebind<unsigned char>;
|
||||
|
||||
table_arrays(
|
||||
std::size_t gsi,std::size_t gsm,
|
||||
group_type_pointer pg,value_type_pointer pe):
|
||||
@@ -1453,6 +1459,11 @@ public:
|
||||
using stats=table_core_stats;
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
table_core(
|
||||
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
|
||||
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
|
||||
@@ -1461,6 +1472,10 @@ public:
|
||||
size_ctrl{initial_max_load(),0}
|
||||
{}
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
@@ -2075,6 +2090,11 @@ private:
|
||||
using pred_base=empty_value<Pred,1>;
|
||||
using allocator_base=empty_value<Allocator,2>;
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
/* used by allocator-extended move ctor */
|
||||
|
||||
table_core(Hash&& h_,Pred&& pred_,const Allocator& al_):
|
||||
@@ -2085,6 +2105,10 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
arrays_type new_arrays(std::size_t n)const
|
||||
{
|
||||
return arrays_type::new_(typename arrays_type::allocator_type(al()),n);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/types_constructibility.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
|
||||
namespace boost {
|
||||
@@ -25,6 +27,9 @@ namespace boost {
|
||||
|
||||
using element_type = value_type;
|
||||
|
||||
using types = flat_map_types<Key, T>;
|
||||
using constructibility_checker = map_types_constructibility<types>;
|
||||
|
||||
static value_type& value_from(element_type& x) { return x; }
|
||||
|
||||
template <class K, class V>
|
||||
@@ -48,18 +53,21 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, init_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, key_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -79,8 +87,8 @@ namespace boost {
|
||||
}
|
||||
};
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/types_constructibility.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
|
||||
namespace boost {
|
||||
@@ -21,6 +23,9 @@ namespace boost {
|
||||
|
||||
using element_type = value_type;
|
||||
|
||||
using types = flat_set_types<Key>;
|
||||
using constructibility_checker = set_types_constructibility<types>;
|
||||
|
||||
static Key& value_from(element_type& x) { return x; }
|
||||
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
@@ -28,6 +33,7 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -37,8 +43,8 @@ namespace boost {
|
||||
}
|
||||
};
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 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)
|
||||
@@ -11,8 +12,11 @@
|
||||
|
||||
#include <boost/unordered/detail/opt_storage.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
@@ -27,6 +31,13 @@ struct insert_return_type
|
||||
NodeType node;
|
||||
};
|
||||
|
||||
template <class NodeType>
|
||||
struct iteratorless_insert_return_type
|
||||
{
|
||||
bool inserted;
|
||||
NodeType node;
|
||||
};
|
||||
|
||||
template <class TypePolicy,class Allocator>
|
||||
struct node_handle_base
|
||||
{
|
||||
@@ -42,7 +53,27 @@ struct node_handle_base
|
||||
element_type p_;
|
||||
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage<Allocator> a_;
|
||||
|
||||
protected:
|
||||
friend struct node_handle_access;
|
||||
|
||||
template<bool B>
|
||||
void move_assign_allocator_if(node_handle_base&& nh)noexcept
|
||||
{
|
||||
move_assign_allocator_if(
|
||||
std::integral_constant<bool,B>{}, std::move(nh));
|
||||
}
|
||||
|
||||
void move_assign_allocator_if(
|
||||
std::true_type, node_handle_base&& nh)noexcept
|
||||
{
|
||||
al()=std::move(nh.al());
|
||||
}
|
||||
|
||||
void move_assign_allocator_if(
|
||||
std::false_type, node_handle_base&&)noexcept
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
node_value_type& data()noexcept
|
||||
{
|
||||
return *(p_.p);
|
||||
@@ -126,9 +157,7 @@ struct node_handle_base
|
||||
BOOST_ASSERT(pocma||al()==nh.al());
|
||||
|
||||
type_policy::destroy(al(),&p_);
|
||||
if(pocma){
|
||||
al()=std::move(nh.al());
|
||||
}
|
||||
move_assign_allocator_if<pocma>(std::move(nh));
|
||||
|
||||
p_=std::move(nh.p_);
|
||||
nh.reset();
|
||||
@@ -153,7 +182,17 @@ struct node_handle_base
|
||||
}
|
||||
}
|
||||
|
||||
allocator_type get_allocator()const noexcept{return al();}
|
||||
allocator_type get_allocator()const
|
||||
{
|
||||
#if defined(BOOST_GCC)
|
||||
/* GCC lifetime analysis incorrectly warns about uninitialized
|
||||
* allocator object under some circumstances.
|
||||
*/
|
||||
if(empty())__builtin_unreachable();
|
||||
#endif
|
||||
return al();
|
||||
}
|
||||
|
||||
explicit operator bool()const noexcept{ return !empty();}
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_.p==nullptr;}
|
||||
|
||||
@@ -196,6 +235,82 @@ struct node_handle_base
|
||||
}
|
||||
};
|
||||
|
||||
// Internal usage of node_handle_base protected API
|
||||
|
||||
struct node_handle_access
|
||||
{
|
||||
template <class TypePolicy, class Allocator>
|
||||
using node_type = node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_CLANG_VERSION,<190000)
|
||||
// https://github.com/llvm/llvm-project/issues/25708
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct element_type_impl
|
||||
{
|
||||
using type = typename node_type<TypePolicy, Allocator>::element_type;
|
||||
};
|
||||
template <class TypePolicy, class Allocator>
|
||||
using element_type = typename element_type_impl<TypePolicy, Allocator>::type;
|
||||
#else
|
||||
template <class TypePolicy, class Allocator>
|
||||
using element_type = typename node_type<TypePolicy, Allocator>::element_type;
|
||||
#endif
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
static element_type<TypePolicy, Allocator>&
|
||||
element(node_type<TypePolicy, Allocator>& nh)noexcept
|
||||
{
|
||||
return nh.element();
|
||||
}
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
static element_type<TypePolicy, Allocator>
|
||||
const& element(node_type<TypePolicy, Allocator> const& nh)noexcept
|
||||
{
|
||||
return nh.element();
|
||||
}
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
static void emplace(
|
||||
node_type<TypePolicy, Allocator>& nh,
|
||||
element_type<TypePolicy, Allocator>&& x, Allocator a)
|
||||
{
|
||||
nh.emplace(std::move(x), a);
|
||||
}
|
||||
|
||||
template <class TypePolicy,class Allocator>
|
||||
static void reset(node_type<TypePolicy, Allocator>& nh)
|
||||
{
|
||||
nh.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
class node_handle_emplacer_class
|
||||
{
|
||||
using access = node_handle_access;
|
||||
using node_type = access::node_type<TypePolicy, Allocator>;
|
||||
using element_type = access::element_type<TypePolicy, Allocator>;
|
||||
|
||||
node_type & nh;
|
||||
|
||||
public:
|
||||
node_handle_emplacer_class(node_type& nh_): nh(nh_) {}
|
||||
|
||||
void operator()(element_type&& x,Allocator a)
|
||||
{
|
||||
access::emplace(nh, std::move(x), a);
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
node_handle_emplacer_class<TypePolicy, Allocator>
|
||||
node_handle_emplacer(node_handle_base<TypePolicy, Allocator>& nh)
|
||||
{
|
||||
return {nh};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 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_NODE_MAP_HANDLE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_map_handle
|
||||
: public node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
public:
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using mapped_type = typename TypePolicy::mapped_type;
|
||||
|
||||
constexpr node_map_handle() noexcept = default;
|
||||
node_map_handle(node_map_handle&& nh) noexcept = default;
|
||||
|
||||
node_map_handle& operator=(node_map_handle&&) noexcept = default;
|
||||
|
||||
key_type& key() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<key_type&>(this->data().first);
|
||||
}
|
||||
|
||||
mapped_type& mapped() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<mapped_type&>(this->data().second);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP
|
||||
@@ -7,6 +7,8 @@
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/types_constructibility.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
@@ -29,6 +31,9 @@ namespace boost {
|
||||
|
||||
using element_type = foa::element_type<value_type, VoidPtr>;
|
||||
|
||||
using types = node_map_types<Key, T, VoidPtr>;
|
||||
using constructibility_checker = map_types_constructibility<types>;
|
||||
|
||||
static value_type& value_from(element_type const& x)
|
||||
{
|
||||
return *(x.p);
|
||||
@@ -68,24 +73,27 @@ namespace boost {
|
||||
static void construct(
|
||||
A& al, element_type* p, element_type const& copy)
|
||||
{
|
||||
construct(al, p, *copy.p);
|
||||
construct(al, p, detail::as_const(*copy.p));
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, init_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, key_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -95,8 +103,11 @@ namespace boost {
|
||||
p->p = boost::allocator_allocate(al, 1);
|
||||
BOOST_TRY
|
||||
{
|
||||
auto address = boost::to_address(p->p);
|
||||
constructibility_checker::check(
|
||||
al, address, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(
|
||||
al, boost::to_address(p->p), std::forward<Args>(args)...);
|
||||
al, address, std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
@@ -132,8 +143,8 @@ namespace boost {
|
||||
};
|
||||
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 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_NODE_SET_HANDLE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_set_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
public:
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
|
||||
constexpr node_set_handle() noexcept = default;
|
||||
node_set_handle(node_set_handle&& nh) noexcept = default;
|
||||
node_set_handle& operator=(node_set_handle&&) noexcept = default;
|
||||
|
||||
value_type& value() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<value_type&>(this->data());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP
|
||||
@@ -6,6 +6,8 @@
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/types_constructibility.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
@@ -26,6 +28,9 @@ namespace boost {
|
||||
|
||||
using element_type = foa::element_type<value_type, VoidPtr>;
|
||||
|
||||
using types = node_set_types<Key, VoidPtr>;
|
||||
using constructibility_checker = set_types_constructibility<types>;
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *x.p; }
|
||||
static Key const& extract(element_type const& k) { return *k.p; }
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
@@ -35,7 +40,7 @@ namespace boost {
|
||||
static void construct(
|
||||
A& al, element_type* p, element_type const& copy)
|
||||
{
|
||||
construct(al, p, *copy.p);
|
||||
construct(al, p, detail::as_const(*copy.p));
|
||||
}
|
||||
|
||||
template <typename Allocator>
|
||||
@@ -49,6 +54,7 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -58,8 +64,11 @@ namespace boost {
|
||||
p->p = boost::allocator_allocate(al, 1);
|
||||
BOOST_TRY
|
||||
{
|
||||
auto address = boost::to_address(p->p);
|
||||
constructibility_checker::check(
|
||||
al, address, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(
|
||||
al, boost::to_address(p->p), std::forward<Args>(args)...);
|
||||
al, address, std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
@@ -85,8 +94,8 @@ namespace boost {
|
||||
};
|
||||
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2023 Joaquin M Lopez Munoz.
|
||||
/* Copyright 2023-2024 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)
|
||||
@@ -19,27 +19,28 @@ namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template<typename Tuple>
|
||||
template<std::size_t Offset,typename Tuple>
|
||||
using tuple_rotate_right_return_type=mp11::mp_rotate_right_c<
|
||||
typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type,
|
||||
1
|
||||
Offset
|
||||
>;
|
||||
|
||||
template<std::size_t... Is,typename Tuple>
|
||||
tuple_rotate_right_return_type<Tuple>
|
||||
template<std::size_t Offset,std::size_t... Is,typename Tuple>
|
||||
tuple_rotate_right_return_type<Offset,Tuple>
|
||||
tuple_rotate_right_aux(mp11::index_sequence<Is...>,Tuple&& x)
|
||||
{
|
||||
return tuple_rotate_right_return_type<Tuple>{
|
||||
std::get<(Is+sizeof...(Is)-1)%sizeof...(Is)>(std::forward<Tuple>(x))...};
|
||||
return tuple_rotate_right_return_type<Offset,Tuple>{
|
||||
std::get<(Is+sizeof...(Is)-Offset)%sizeof...(Is)>(
|
||||
std::forward<Tuple>(x))...};
|
||||
}
|
||||
|
||||
template<typename Tuple>
|
||||
tuple_rotate_right_return_type<Tuple> tuple_rotate_right(Tuple&& x)
|
||||
template<std::size_t Offset=1,typename Tuple>
|
||||
tuple_rotate_right_return_type<Offset,Tuple> tuple_rotate_right(Tuple&& x)
|
||||
{
|
||||
using RawTuple=typename std::remove_cv<
|
||||
typename std::remove_reference<Tuple>::type>::type;
|
||||
|
||||
return tuple_rotate_right_aux(
|
||||
return tuple_rotate_right_aux<Offset>(
|
||||
mp11::make_index_sequence<std::tuple_size<RawTuple>::value>{},
|
||||
std::forward<Tuple>(x));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
// 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_FOA_TYPES_CONSTRUCTIBILITY_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
namespace foa {
|
||||
template <class Key, class... Args> struct check_key_type_t
|
||||
{
|
||||
static_assert(std::is_constructible<Key, Args...>::value,
|
||||
"key_type must be constructible from Args");
|
||||
};
|
||||
template <class Key> struct check_key_type_t<Key>
|
||||
{
|
||||
static_assert(std::is_constructible<Key>::value,
|
||||
"key_type must be default constructible");
|
||||
};
|
||||
template <class Key> struct check_key_type_t<Key, const Key&>
|
||||
{
|
||||
static_assert(std::is_constructible<Key, const Key&>::value,
|
||||
"key_type must be copy constructible");
|
||||
};
|
||||
template <class Key> struct check_key_type_t<Key, Key&&>
|
||||
{
|
||||
static_assert(std::is_constructible<Key, Key&&>::value,
|
||||
"key_type must be move constructible");
|
||||
};
|
||||
|
||||
template <class Mapped, class... Args> struct check_mapped_type_t
|
||||
{
|
||||
static_assert(std::is_constructible<Mapped, Args...>::value,
|
||||
"mapped_type must be constructible from Args");
|
||||
};
|
||||
template <class Mapped> struct check_mapped_type_t<Mapped>
|
||||
{
|
||||
static_assert(std::is_constructible<Mapped>::value,
|
||||
"mapped_type must be default constructible");
|
||||
};
|
||||
template <class Mapped>
|
||||
struct check_mapped_type_t<Mapped, const Mapped&>
|
||||
{
|
||||
static_assert(std::is_constructible<Mapped, const Mapped&>::value,
|
||||
"mapped_type must be copy constructible");
|
||||
};
|
||||
template <class Mapped> struct check_mapped_type_t<Mapped, Mapped&&>
|
||||
{
|
||||
static_assert(std::is_constructible<Mapped, Mapped&&>::value,
|
||||
"mapped_type must be move constructible");
|
||||
};
|
||||
|
||||
template <class TypePolicy> struct map_types_constructibility
|
||||
{
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using mapped_type = typename TypePolicy::mapped_type;
|
||||
using init_type = typename TypePolicy::init_type;
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
|
||||
template <class A, class X, class... Args>
|
||||
static void check(A&, X*, Args&&...)
|
||||
{
|
||||
// Pass through, as we cannot say anything about a general allocator
|
||||
}
|
||||
|
||||
template <class... Args> static void check_key_type()
|
||||
{
|
||||
(void)check_key_type_t<key_type, Args...>{};
|
||||
}
|
||||
template <class... Args> static void check_mapped_type()
|
||||
{
|
||||
(void)check_mapped_type_t<mapped_type, Args...>{};
|
||||
}
|
||||
|
||||
template <class Arg>
|
||||
static void check(std::allocator<value_type>&, key_type*, Arg&&)
|
||||
{
|
||||
check_key_type<Arg&&>();
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(
|
||||
std::allocator<value_type>&, value_type*, Arg1&&, Arg2&&)
|
||||
{
|
||||
check_key_type<Arg1&&>();
|
||||
check_mapped_type<Arg2&&>();
|
||||
}
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(std::allocator<value_type>&, value_type*,
|
||||
const std::pair<Arg1, Arg2>&)
|
||||
{
|
||||
check_key_type<const Arg1&>();
|
||||
check_mapped_type<const Arg2&>();
|
||||
}
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(
|
||||
std::allocator<value_type>&, value_type*, std::pair<Arg1, Arg2>&&)
|
||||
{
|
||||
check_key_type<Arg1&&>();
|
||||
check_mapped_type<Arg2&&>();
|
||||
}
|
||||
template <class... Args1, class... Args2>
|
||||
static void check(std::allocator<value_type>&, value_type*,
|
||||
std::piecewise_construct_t, std::tuple<Args1...>&&,
|
||||
std::tuple<Args2...>&&)
|
||||
{
|
||||
check_key_type<Args1&&...>();
|
||||
check_mapped_type<Args2&&...>();
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(
|
||||
std::allocator<value_type>&, init_type*, Arg1&&, Arg2&&)
|
||||
{
|
||||
check_key_type<Arg1&&>();
|
||||
check_mapped_type<Arg2&&>();
|
||||
}
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(std::allocator<value_type>&, init_type*,
|
||||
const std::pair<Arg1, Arg2>&)
|
||||
{
|
||||
check_key_type<const Arg1&>();
|
||||
check_mapped_type<const Arg2&>();
|
||||
}
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(
|
||||
std::allocator<value_type>&, init_type*, std::pair<Arg1, Arg2>&&)
|
||||
{
|
||||
check_key_type<Arg1&&>();
|
||||
check_mapped_type<Arg2&&>();
|
||||
}
|
||||
template <class... Args1, class... Args2>
|
||||
static void check(std::allocator<value_type>&, init_type*,
|
||||
std::piecewise_construct_t, std::tuple<Args1...>&&,
|
||||
std::tuple<Args2...>&&)
|
||||
{
|
||||
check_key_type<Args1&&...>();
|
||||
check_mapped_type<Args2&&...>();
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypePolicy> struct set_types_constructibility
|
||||
{
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
static_assert(std::is_same<key_type, value_type>::value, "");
|
||||
|
||||
template <class A, class X, class... Args>
|
||||
static void check(A&, X*, Args&&...)
|
||||
{
|
||||
// Pass through, as we cannot say anything about a general allocator
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
static void check(std::allocator<value_type>&, key_type*, Args&&...)
|
||||
{
|
||||
(void)check_key_type_t<key_type, Args&&...>{};
|
||||
}
|
||||
};
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <boost/unordered/detail/serialize_tracked_address.hpp>
|
||||
#include <boost/unordered/detail/static_assert.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_printers.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/allocator_traits.hpp>
|
||||
|
||||
@@ -41,23 +41,6 @@ namespace boost {
|
||||
typedef typename table::iterator iterator;
|
||||
typedef boost::unordered::insert_return_type_map<iterator, node_type> insert_return_type;
|
||||
};
|
||||
|
||||
template <typename K, typename M, typename H, typename P, typename A>
|
||||
class instantiate_map
|
||||
{
|
||||
typedef boost::unordered_map<K, M, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
typename container::insert_return_type insert_return_type;
|
||||
};
|
||||
|
||||
template <typename K, typename M, typename H, typename P, typename A>
|
||||
class instantiate_multimap
|
||||
{
|
||||
typedef boost::unordered_multimap<K, M, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,23 +39,6 @@ namespace boost {
|
||||
typedef boost::unordered::insert_return_type_set<iterator, node_type>
|
||||
insert_return_type;
|
||||
};
|
||||
|
||||
template <typename T, typename H, typename P, typename A>
|
||||
class instantiate_set
|
||||
{
|
||||
typedef boost::unordered_set<T, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
typename container::insert_return_type insert_return_type;
|
||||
};
|
||||
|
||||
template <typename T, typename H, typename P, typename A>
|
||||
class instantiate_multiset
|
||||
{
|
||||
typedef boost::unordered_multiset<T, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +219,17 @@ namespace boost {
|
||||
using iter_to_alloc_t =
|
||||
typename std::pair<iter_key_t<T> const, iter_val_t<T> >;
|
||||
#endif
|
||||
|
||||
#if BOOST_CXX_VERSION < 201703L
|
||||
template <class T>
|
||||
constexpr typename std::add_const<T>::type& as_const(T& t) noexcept
|
||||
{
|
||||
return t;
|
||||
}
|
||||
template <class T> void as_const(const T&&) = delete;
|
||||
#else
|
||||
using std::as_const;
|
||||
#endif
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 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)
|
||||
|
||||
@@ -181,6 +182,7 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
template <bool avoid_explicit_instantiation = true>
|
||||
unordered_flat_map(
|
||||
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
@@ -202,6 +204,13 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_flat_map& operator=(std::initializer_list<value_type> il)
|
||||
{
|
||||
this->clear();
|
||||
this->insert(il.begin(), il.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 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)
|
||||
|
||||
@@ -177,6 +178,7 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
template <bool avoid_explicit_instantiation = true>
|
||||
unordered_flat_set(
|
||||
concurrent_flat_set<Key, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
@@ -198,6 +200,13 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_flat_set& operator=(std::initializer_list<value_type> il)
|
||||
{
|
||||
this->clear();
|
||||
this->insert(il.begin(), il.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace boost {
|
||||
template <class... Args> std::pair<iterator, bool> emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace_unique(
|
||||
table::extractor::extract(std::forward<Args>(args)...),
|
||||
table::extractor::extract(detail::as_const(args)...),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace boost {
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args)
|
||||
{
|
||||
return table_.emplace_hint_unique(hint,
|
||||
table::extractor::extract(std::forward<Args>(args)...),
|
||||
table::extractor::extract(detail::as_const(args)...),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 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)
|
||||
|
||||
@@ -10,8 +11,8 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/concurrent_node_map_fwd.hpp>
|
||||
#include <boost/unordered/detail/foa/node_map_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_map_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
@@ -36,45 +37,13 @@ namespace boost {
|
||||
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_map_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
template <class Key, class T, class Hash, class Pred, class Alloc>
|
||||
friend class boost::unordered::unordered_node_map;
|
||||
|
||||
public:
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using mapped_type = typename TypePolicy::mapped_type;
|
||||
|
||||
constexpr node_map_handle() noexcept = default;
|
||||
node_map_handle(node_map_handle&& nh) noexcept = default;
|
||||
|
||||
node_map_handle& operator=(node_map_handle&&) noexcept = default;
|
||||
|
||||
key_type& key() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<key_type&>(this->data().first);
|
||||
}
|
||||
|
||||
mapped_type& mapped() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<mapped_type&>(this->data().second);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_node_map
|
||||
{
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class concurrent_node_map;
|
||||
|
||||
using map_types = detail::foa::node_map_types<Key, T,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
@@ -109,7 +78,7 @@ namespace boost {
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
using iterator = typename table_type::iterator;
|
||||
using const_iterator = typename table_type::const_iterator;
|
||||
using node_type = detail::node_map_handle<map_types,
|
||||
using node_type = detail::foa::node_map_handle<map_types,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename map_types::value_type>::type>;
|
||||
using insert_return_type =
|
||||
@@ -220,6 +189,13 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
template <bool avoid_explicit_instantiation = true>
|
||||
unordered_node_map(
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~unordered_node_map() = default;
|
||||
|
||||
unordered_node_map& operator=(unordered_node_map const& other)
|
||||
@@ -235,6 +211,13 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_node_map& operator=(std::initializer_list<value_type> il)
|
||||
{
|
||||
this->clear();
|
||||
this->insert(il.begin(), il.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
@@ -307,15 +290,17 @@ namespace boost {
|
||||
|
||||
insert_return_type insert(node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {end(), false, node_type{}};
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
auto itp = table_.insert(std::move(access::element(nh)));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
access::reset(nh);
|
||||
return {itp.first, true, node_type{}};
|
||||
} else {
|
||||
return {itp.first, false, std::move(nh)};
|
||||
@@ -324,15 +309,17 @@ namespace boost {
|
||||
|
||||
iterator insert(const_iterator, node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return end();
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
auto itp = table_.insert(std::move(access::element(nh)));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
access::reset(nh);
|
||||
return itp.first;
|
||||
} else {
|
||||
return itp.first;
|
||||
@@ -507,7 +494,8 @@ namespace boost {
|
||||
BOOST_ASSERT(pos != end());
|
||||
node_type nh;
|
||||
auto elem = table_.extract(pos);
|
||||
nh.emplace(std::move(elem), get_allocator());
|
||||
detail::foa::node_handle_emplacer(nh)(
|
||||
std::move(elem), get_allocator());
|
||||
return nh;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 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)
|
||||
|
||||
@@ -10,8 +11,9 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/concurrent_node_set_fwd.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_set_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_set_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
@@ -35,37 +37,12 @@ namespace boost {
|
||||
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_set_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
friend class boost::unordered::unordered_node_set;
|
||||
|
||||
public:
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
|
||||
constexpr node_set_handle() noexcept = default;
|
||||
node_set_handle(node_set_handle&& nh) noexcept = default;
|
||||
node_set_handle& operator=(node_set_handle&&) noexcept = default;
|
||||
|
||||
value_type& value() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<value_type&>(this->data());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_node_set
|
||||
{
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class concurrent_node_set;
|
||||
|
||||
using set_types = detail::foa::node_set_types<Key,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
@@ -99,7 +76,7 @@ namespace boost {
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
using iterator = typename table_type::iterator;
|
||||
using const_iterator = typename table_type::const_iterator;
|
||||
using node_type = detail::node_set_handle<set_types,
|
||||
using node_type = detail::foa::node_set_handle<set_types,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename set_types::value_type>::type>;
|
||||
using insert_return_type =
|
||||
@@ -210,6 +187,13 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
template <bool avoid_explicit_instantiation = true>
|
||||
unordered_node_set(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~unordered_node_set() = default;
|
||||
|
||||
unordered_node_set& operator=(unordered_node_set const& other)
|
||||
@@ -225,6 +209,13 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_node_set& operator=(std::initializer_list<value_type> il)
|
||||
{
|
||||
this->clear();
|
||||
this->insert(il.begin(), il.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
@@ -312,15 +303,17 @@ namespace boost {
|
||||
|
||||
insert_return_type insert(node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {end(), false, node_type{}};
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
auto itp = table_.insert(std::move(access::element(nh)));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
access::reset(nh);
|
||||
return {itp.first, true, node_type{}};
|
||||
} else {
|
||||
return {itp.first, false, std::move(nh)};
|
||||
@@ -329,15 +322,17 @@ namespace boost {
|
||||
|
||||
iterator insert(const_iterator, node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return end();
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
auto itp = table_.insert(std::move(access::element(nh)));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
access::reset(nh);
|
||||
return itp.first;
|
||||
} else {
|
||||
return itp.first;
|
||||
@@ -395,7 +390,8 @@ namespace boost {
|
||||
BOOST_ASSERT(pos != end());
|
||||
node_type nh;
|
||||
auto elem = table_.extract(pos);
|
||||
nh.emplace(std::move(elem), get_allocator());
|
||||
detail::foa::node_handle_emplacer(nh)(
|
||||
std::move(elem), get_allocator());
|
||||
return nh;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
// Copyright 2024 Braden Ganetsky
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
// Generated on 2024-08-25T17:48:54
|
||||
|
||||
#ifndef BOOST_UNORDERED_UNORDERED_PRINTERS_HPP
|
||||
#define BOOST_UNORDERED_UNORDERED_PRINTERS_HPP
|
||||
|
||||
#ifndef BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS
|
||||
#if defined(__ELF__)
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Woverlength-strings"
|
||||
#endif
|
||||
__asm__(".pushsection \".debug_gdb_scripts\", \"MS\",%progbits,1\n"
|
||||
".ascii \"\\4gdb.inlined-script.BOOST_UNORDERED_UNORDERED_PRINTERS_HPP\\n\"\n"
|
||||
".ascii \"import gdb.printing\\n\"\n"
|
||||
".ascii \"import gdb.xmethod\\n\"\n"
|
||||
".ascii \"import re\\n\"\n"
|
||||
".ascii \"import math\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedHelpers:\\n\"\n"
|
||||
".ascii \" def maybe_unwrap_atomic(n):\\n\"\n"
|
||||
".ascii \" if f\\\"{n.type.strip_typedefs()}\\\".startswith(\\\"std::atomic<\\\"):\\n\"\n"
|
||||
".ascii \" underlying_type = n.type.template_argument(0)\\n\"\n"
|
||||
".ascii \" return n.cast(underlying_type)\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return n\\n\"\n"
|
||||
|
||||
".ascii \" def maybe_unwrap_foa_element(e):\\n\"\n"
|
||||
".ascii \" if f\\\"{e.type.strip_typedefs()}\\\".startswith(\\\"boost::unordered::detail::foa::element_type<\\\"):\\n\"\n"
|
||||
".ascii \" return e[\\\"p\\\"]\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return e\\n\"\n"
|
||||
|
||||
".ascii \" def maybe_unwrap_reference(value):\\n\"\n"
|
||||
".ascii \" if value.type.code == gdb.TYPE_CODE_REF:\\n\"\n"
|
||||
".ascii \" return value.referenced_value()\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return value\\n\"\n"
|
||||
|
||||
".ascii \" def countr_zero(n):\\n\"\n"
|
||||
".ascii \" for i in range(32):\\n\"\n"
|
||||
".ascii \" if (n & (1 << i)) != 0:\\n\"\n"
|
||||
".ascii \" return i\\n\"\n"
|
||||
".ascii \" return 32\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedPointerCustomizationPoint:\\n\"\n"
|
||||
".ascii \" def __init__(self, any_ptr):\\n\"\n"
|
||||
".ascii \" vis = gdb.default_visualizer(any_ptr)\\n\"\n"
|
||||
".ascii \" if vis is None:\\n\"\n"
|
||||
".ascii \" self.to_address = lambda ptr: ptr\\n\"\n"
|
||||
".ascii \" self.next = lambda ptr, offset: ptr + offset\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" self.to_address = lambda ptr: ptr if (ptr.type.code == gdb.TYPE_CODE_PTR) else type(vis).boost_to_address(ptr)\\n\"\n"
|
||||
".ascii \" self.next = lambda ptr, offset: type(vis).boost_next(ptr, offset)\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFcaPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n"
|
||||
".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
|
||||
".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n"
|
||||
".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n"
|
||||
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"buckets_\\\"][\\\"buckets\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" size = self.val[\\\"table_\\\"][\\\"size_\\\"]\\n\"\n"
|
||||
".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def display_hint(self):\\n\"\n"
|
||||
".ascii \" return \\\"map\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def children(self):\\n\"\n"
|
||||
".ascii \" def generator():\\n\"\n"
|
||||
".ascii \" grouped_buckets = self.val[\\\"table_\\\"][\\\"buckets_\\\"]\\n\"\n"
|
||||
|
||||
".ascii \" size = grouped_buckets[\\\"size_\\\"]\\n\"\n"
|
||||
".ascii \" buckets = grouped_buckets[\\\"buckets\\\"]\\n\"\n"
|
||||
".ascii \" bucket_index = 0\\n\"\n"
|
||||
|
||||
".ascii \" count = 0\\n\"\n"
|
||||
".ascii \" while bucket_index != size:\\n\"\n"
|
||||
".ascii \" current_bucket = self.cpo.next(self.cpo.to_address(buckets), bucket_index)\\n\"\n"
|
||||
".ascii \" node = self.cpo.to_address(current_bucket.dereference()[\\\"next\\\"])\\n\"\n"
|
||||
".ascii \" while node != 0:\\n\"\n"
|
||||
".ascii \" value = node.dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n"
|
||||
".ascii \" if self.is_map:\\n\"\n"
|
||||
".ascii \" first = value[\\\"first\\\"]\\n\"\n"
|
||||
".ascii \" second = value[\\\"second\\\"]\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", first\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", second\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", count\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", value\\n\"\n"
|
||||
".ascii \" count += 1\\n\"\n"
|
||||
".ascii \" node = self.cpo.to_address(node.dereference()[\\\"next\\\"])\\n\"\n"
|
||||
".ascii \" bucket_index += 1\\n\"\n"
|
||||
|
||||
".ascii \" return generator()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFcaIteratorPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = val\\n\"\n"
|
||||
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" if self.valid():\\n\"\n"
|
||||
".ascii \" value = self.cpo.to_address(self.val[\\\"p\\\"]).dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n"
|
||||
".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def valid(self):\\n\"\n"
|
||||
".ascii \" return (self.cpo.to_address(self.val[\\\"p\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"itb\\\"][\\\"p\\\"]) != 0)\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaTableCoreCumulativeStatsPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = val\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" return \\\"[stats]\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def display_hint(self):\\n\"\n"
|
||||
".ascii \" return \\\"map\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def children(self):\\n\"\n"
|
||||
".ascii \" def generator():\\n\"\n"
|
||||
".ascii \" members = [\\\"insertion\\\", \\\"successful_lookup\\\", \\\"unsuccessful_lookup\\\"]\\n\"\n"
|
||||
".ascii \" for member in members:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", member\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", self.val[member]\\n\"\n"
|
||||
".ascii \" return generator()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaCumulativeStatsPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = val\\n\"\n"
|
||||
".ascii \" self.n = self.val[\\\"n\\\"]\\n\"\n"
|
||||
".ascii \" self.N = self.val.type.template_argument(0)\\n\"\n"
|
||||
|
||||
".ascii \" def display_hint(self):\\n\"\n"
|
||||
".ascii \" return \\\"map\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def children(self):\\n\"\n"
|
||||
".ascii \" def generator():\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", \\\"count\\\"\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", self.n\\n\"\n"
|
||||
|
||||
".ascii \" sequence_stats_data = gdb.lookup_type(\\\"boost::unordered::detail::foa::sequence_stats_data\\\")\\n\"\n"
|
||||
".ascii \" data = self.val[\\\"data\\\"]\\n\"\n"
|
||||
".ascii \" arr = data.address.reinterpret_cast(sequence_stats_data.pointer())\\n\"\n"
|
||||
".ascii \" def build_string(idx):\\n\"\n"
|
||||
".ascii \" entry = arr[idx]\\n\"\n"
|
||||
".ascii \" avg = float(entry[\\\"m\\\"])\\n\"\n"
|
||||
".ascii \" var = float(entry[\\\"s\\\"] / self.n) if (self.n != 0) else 0.0\\n\"\n"
|
||||
".ascii \" dev = math.sqrt(var)\\n\"\n"
|
||||
".ascii \" return f\\\"{{avg = {avg}, var = {var}, dev = {dev}}}\\\"\\n\"\n"
|
||||
|
||||
".ascii \" if self.N > 0:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", \\\"probe_length\\\"\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", build_string(0)\\n\"\n"
|
||||
".ascii \" if self.N > 1:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", \\\"num_comparisons\\\"\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", build_string(1)\\n\"\n"
|
||||
|
||||
".ascii \" return generator()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n"
|
||||
".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
|
||||
".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n"
|
||||
".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n"
|
||||
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" size = BoostUnorderedHelpers.maybe_unwrap_atomic(self.val[\\\"table_\\\"][\\\"size_ctrl\\\"][\\\"size\\\"])\\n\"\n"
|
||||
".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def display_hint(self):\\n\"\n"
|
||||
".ascii \" return \\\"map\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def is_regular_layout(self, group):\\n\"\n"
|
||||
".ascii \" typename = group[\\\"m\\\"].type.strip_typedefs()\\n\"\n"
|
||||
".ascii \" array_size = typename.sizeof // typename.target().sizeof\\n\"\n"
|
||||
".ascii \" if array_size == 16:\\n\"\n"
|
||||
".ascii \" return True\\n\"\n"
|
||||
".ascii \" elif array_size == 2:\\n\"\n"
|
||||
".ascii \" return False\\n\"\n"
|
||||
|
||||
".ascii \" def match_occupied(self, group):\\n\"\n"
|
||||
".ascii \" m = group[\\\"m\\\"]\\n\"\n"
|
||||
".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" if self.is_regular_layout(group):\\n\"\n"
|
||||
".ascii \" bits = [1 << b for b in range(16) if at(b) == 0]\\n\"\n"
|
||||
".ascii \" return 0x7FFF & ~sum(bits)\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" xx = at(0) | at(1)\\n\"\n"
|
||||
".ascii \" yy = xx | (xx >> 32)\\n\"\n"
|
||||
".ascii \" return 0x7FFF & (yy | (yy >> 16))\\n\"\n"
|
||||
|
||||
".ascii \" def is_sentinel(self, group, pos):\\n\"\n"
|
||||
".ascii \" m = group[\\\"m\\\"]\\n\"\n"
|
||||
".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" N = group[\\\"N\\\"]\\n\"\n"
|
||||
".ascii \" sentinel_ = group[\\\"sentinel_\\\"]\\n\"\n"
|
||||
".ascii \" if self.is_regular_layout(group):\\n\"\n"
|
||||
".ascii \" return pos == N-1 and at(N-1) == sentinel_\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return pos == N-1 and (at(0) & 0x4000400040004000) == 0x4000 and (at(1) & 0x4000400040004000) == 0\\n\"\n"
|
||||
|
||||
".ascii \" def children(self):\\n\"\n"
|
||||
".ascii \" def generator():\\n\"\n"
|
||||
".ascii \" table = self.val[\\\"table_\\\"]\\n\"\n"
|
||||
".ascii \" groups = self.cpo.to_address(table[\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n"
|
||||
".ascii \" elements = self.cpo.to_address(table[\\\"arrays\\\"][\\\"elements_\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" pc_ = groups.cast(gdb.lookup_type(\\\"unsigned char\\\").pointer())\\n\"\n"
|
||||
".ascii \" p_ = elements\\n\"\n"
|
||||
".ascii \" first_time = True\\n\"\n"
|
||||
".ascii \" mask = 0\\n\"\n"
|
||||
".ascii \" n0 = 0\\n\"\n"
|
||||
".ascii \" n = 0\\n\"\n"
|
||||
|
||||
".ascii \" count = 0\\n\"\n"
|
||||
".ascii \" while p_ != 0:\\n\"\n"
|
||||
".ascii \" # This if block mirrors the condition in the begin() call\\n\"\n"
|
||||
".ascii \" if (not first_time) or (self.match_occupied(groups.dereference()) & 1):\\n\"\n"
|
||||
".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(p_)\\n\"\n"
|
||||
".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n"
|
||||
".ascii \" if self.is_map:\\n\"\n"
|
||||
".ascii \" first = value[\\\"first\\\"]\\n\"\n"
|
||||
".ascii \" second = value[\\\"second\\\"]\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", first\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", second\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", count\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", value\\n\"\n"
|
||||
".ascii \" count += 1\\n\"\n"
|
||||
".ascii \" first_time = False\\n\"\n"
|
||||
|
||||
".ascii \" n0 = pc_.cast(gdb.lookup_type(\\\"uintptr_t\\\")) % groups.dereference().type.sizeof\\n\"\n"
|
||||
".ascii \" pc_ = self.cpo.next(pc_, -n0)\\n\"\n"
|
||||
|
||||
".ascii \" mask = (self.match_occupied(pc_.cast(groups.type).dereference()) >> (n0+1)) << (n0+1)\\n\"\n"
|
||||
".ascii \" while mask == 0:\\n\"\n"
|
||||
".ascii \" pc_ = self.cpo.next(pc_, groups.dereference().type.sizeof)\\n\"\n"
|
||||
".ascii \" p_ = self.cpo.next(p_, groups.dereference()[\\\"N\\\"])\\n\"\n"
|
||||
".ascii \" mask = self.match_occupied(pc_.cast(groups.type).dereference())\\n\"\n"
|
||||
|
||||
".ascii \" n = BoostUnorderedHelpers.countr_zero(mask)\\n\"\n"
|
||||
".ascii \" if self.is_sentinel(pc_.cast(groups.type).dereference(), n):\\n\"\n"
|
||||
".ascii \" p_ = 0\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" pc_ = self.cpo.next(pc_, n)\\n\"\n"
|
||||
".ascii \" p_ = self.cpo.next(p_, n - n0)\\n\"\n"
|
||||
|
||||
".ascii \" return generator()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaIteratorPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = val\\n\"\n"
|
||||
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p_\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" if self.valid():\\n\"\n"
|
||||
".ascii \" element = self.cpo.to_address(self.val[\\\"p_\\\"])\\n\"\n"
|
||||
".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(element)\\n\"\n"
|
||||
".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n"
|
||||
".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def valid(self):\\n\"\n"
|
||||
".ascii \" return (self.cpo.to_address(self.val[\\\"p_\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"pc_\\\"]) != 0)\\n\"\n"
|
||||
|
||||
".ascii \"def boost_unordered_build_pretty_printer():\\n\"\n"
|
||||
".ascii \" pp = gdb.printing.RegexpCollectionPrettyPrinter(\\\"boost_unordered\\\")\\n\"\n"
|
||||
".ascii \" add_template_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}<.*>$\\\", printer)\\n\"\n"
|
||||
".ascii \" add_concrete_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}$\\\", printer)\\n\"\n"
|
||||
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_map\\\", BoostUnorderedFcaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_multimap\\\", BoostUnorderedFcaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_set\\\", BoostUnorderedFcaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_multiset\\\", BoostUnorderedFcaPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::c_iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::table_iterator\\\", BoostUnorderedFoaIteratorPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" add_concrete_printer(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\", BoostUnorderedFoaTableCoreCumulativeStatsPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::concurrent_cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" return pp\\n\"\n"
|
||||
|
||||
".ascii \"gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_unordered_build_pretty_printer())\\n\"\n"
|
||||
|
||||
|
||||
|
||||
".ascii \"# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html\\n\"\n"
|
||||
".ascii \"class BoostUnorderedFoaGetStatsMethod(gdb.xmethod.XMethod):\\n\"\n"
|
||||
".ascii \" def __init__(self):\\n\"\n"
|
||||
".ascii \" gdb.xmethod.XMethod.__init__(self, \\\"get_stats\\\")\\n\"\n"
|
||||
|
||||
".ascii \" def get_worker(self, method_name):\\n\"\n"
|
||||
".ascii \" if method_name == \\\"get_stats\\\":\\n\"\n"
|
||||
".ascii \" return BoostUnorderedFoaGetStatsWorker()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaGetStatsWorker(gdb.xmethod.XMethodWorker):\\n\"\n"
|
||||
".ascii \" def get_arg_types(self):\\n\"\n"
|
||||
".ascii \" return None\\n\"\n"
|
||||
|
||||
".ascii \" def get_result_type(self, obj):\\n\"\n"
|
||||
".ascii \" return gdb.lookup_type(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\")\\n\"\n"
|
||||
|
||||
".ascii \" def __call__(self, obj):\\n\"\n"
|
||||
".ascii \" try:\\n\"\n"
|
||||
".ascii \" return obj[\\\"table_\\\"][\\\"cstats\\\"]\\n\"\n"
|
||||
".ascii \" except gdb.error:\\n\"\n"
|
||||
".ascii \" print(\\\"Error: Binary was compiled without stats. Recompile with `BOOST_UNORDERED_ENABLE_STATS` defined.\\\")\\n\"\n"
|
||||
".ascii \" return\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaMatcher(gdb.xmethod.XMethodMatcher):\\n\"\n"
|
||||
".ascii \" def __init__(self):\\n\"\n"
|
||||
".ascii \" gdb.xmethod.XMethodMatcher.__init__(self, 'BoostUnorderedFoaMatcher')\\n\"\n"
|
||||
".ascii \" self.methods = [BoostUnorderedFoaGetStatsMethod()]\\n\"\n"
|
||||
|
||||
".ascii \" def match(self, class_type, method_name):\\n\"\n"
|
||||
".ascii \" template_name = f\\\"{class_type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
|
||||
".ascii \" regex = \\\"^boost::unordered::(unordered|concurrent)_(flat|node)_(map|set)$\\\"\\n\"\n"
|
||||
".ascii \" if not re.match(regex, template_name):\\n\"\n"
|
||||
".ascii \" return None\\n\"\n"
|
||||
|
||||
".ascii \" workers = []\\n\"\n"
|
||||
".ascii \" for method in self.methods:\\n\"\n"
|
||||
".ascii \" if method.enabled:\\n\"\n"
|
||||
".ascii \" worker = method.get_worker(method_name)\\n\"\n"
|
||||
".ascii \" if worker:\\n\"\n"
|
||||
".ascii \" workers.append(worker)\\n\"\n"
|
||||
".ascii \" return workers\\n\"\n"
|
||||
|
||||
".ascii \"gdb.xmethod.register_xmethod_matcher(None, BoostUnorderedFoaMatcher())\\n\"\n"
|
||||
|
||||
|
||||
|
||||
".ascii \"\\\"\\\"\\\" Fancy pointer support \\\"\\\"\\\"\\n\"\n"
|
||||
|
||||
".ascii \"\\\"\\\"\\\"\\n\"\n"
|
||||
".ascii \"To allow your own fancy pointer type to interact with Boost.Unordered GDB pretty-printers,\\n\"\n"
|
||||
".ascii \"create a pretty-printer for your own type with the following additional methods.\\n\"\n"
|
||||
|
||||
".ascii \"(Note, this is assuming the presence of a type alias `pointer` for the underlying\\n\"\n"
|
||||
".ascii \"raw pointer type, Substitute whichever name is applicable in your case.)\\n\"\n"
|
||||
|
||||
".ascii \"`boost_to_address(fancy_ptr)`\\n\"\n"
|
||||
".ascii \" * A static method, but `@staticmethod` is not required\\n\"\n"
|
||||
".ascii \" * Parameter `fancy_ptr` of type `gdb.Value`\\n\"\n"
|
||||
".ascii \" * Its `.type` will be your fancy pointer type\\n\"\n"
|
||||
".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer\\n\"\n"
|
||||
".ascii \" * This method should be equivalent to calling `operator->()` on your fancy pointer in C++\\n\"\n"
|
||||
|
||||
".ascii \"`boost_next(raw_ptr, offset)`\\n\"\n"
|
||||
".ascii \" * Parameter `raw_ptr` of type `gdb.Value`\\n\"\n"
|
||||
".ascii \" * Its `.type` will be `pointer`\\n\"\n"
|
||||
".ascii \" * Parameter `offset`\\n\"\n"
|
||||
".ascii \" * Either has integer type, or is of type `gdb.Value` with an underlying integer\\n\"\n"
|
||||
".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer, as if you did the following operations\\n\"\n"
|
||||
".ascii \" 1. Convert the incoming raw pointer to your fancy pointer\\n\"\n"
|
||||
".ascii \" 2. Use operator+= to add the offset to the fancy pointer\\n\"\n"
|
||||
".ascii \" 3. Convert back to the raw pointer\\n\"\n"
|
||||
".ascii \" * Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above\\n\"\n"
|
||||
".ascii \" * Ultimately, it will be as if you called `operator+()` on your fancy pointer in C++, but using only raw pointers\\n\"\n"
|
||||
|
||||
".ascii \"Example\\n\"\n"
|
||||
".ascii \"```\\n\"\n"
|
||||
".ascii \"class MyFancyPtrPrinter:\\n\"\n"
|
||||
".ascii \" ...\\n\"\n"
|
||||
|
||||
".ascii \" # Equivalent to `operator->()`\\n\"\n"
|
||||
".ascii \" def boost_to_address(fancy_ptr):\\n\"\n"
|
||||
".ascii \" ...\\n\"\n"
|
||||
".ascii \" return ...\\n\"\n"
|
||||
|
||||
".ascii \" # Equivalent to `operator+()`\\n\"\n"
|
||||
".ascii \" def boost_next(raw_ptr, offset):\\n\"\n"
|
||||
".ascii \" ...\\n\"\n"
|
||||
".ascii \" return ...\\n\"\n"
|
||||
|
||||
".ascii \" ...\\n\"\n"
|
||||
".ascii \"```\\n\"\n"
|
||||
".ascii \"\\\"\\\"\\\"\\n\"\n"
|
||||
|
||||
".byte 0\n"
|
||||
".popsection\n");
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#endif // defined(__ELF__)
|
||||
#endif // !defined(BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS)
|
||||
|
||||
#endif // !defined(BOOST_UNORDERED_UNORDERED_PRINTERS_HPP)
|
||||
@@ -194,7 +194,7 @@ namespace boost {
|
||||
template <class... Args> std::pair<iterator, bool> emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace_unique(
|
||||
table::extractor::extract(std::forward<Args>(args)...),
|
||||
table::extractor::extract(detail::as_const(args)...),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace boost {
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args)
|
||||
{
|
||||
return table_.emplace_hint_unique(hint,
|
||||
table::extractor::extract(std::forward<Args>(args)...),
|
||||
table::extractor::extract(detail::as_const(args)...),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ foa_tests(SOURCES exception/merge_exception_tests.cpp)
|
||||
|
||||
# CFOA tests
|
||||
|
||||
cfoa_tests(SOURCES cfoa/insert_tests.cpp)
|
||||
cfoa_tests(SOURCES cfoa/insert_tests.cpp COMPILE_OPTIONS $<$<CXX_COMPILER_ID:MSVC>:/bigobj>)
|
||||
cfoa_tests(SOURCES cfoa/erase_tests.cpp)
|
||||
cfoa_tests(SOURCES cfoa/try_emplace_tests.cpp)
|
||||
cfoa_tests(SOURCES cfoa/emplace_tests.cpp)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import path ;
|
||||
import regex ;
|
||||
import testing ;
|
||||
import ../../config/checks/config : requires ;
|
||||
import config : requires ;
|
||||
|
||||
path-constant TOP : . ;
|
||||
|
||||
@@ -21,6 +21,9 @@ local msvc-flags = /wd4494 ;
|
||||
|
||||
project
|
||||
: requirements
|
||||
<library>/boost/concept_check//boost_concept_check
|
||||
<library>/boost/compat//boost_compat
|
||||
<library>/boost/iterator//boost_iterator
|
||||
<warnings>pedantic
|
||||
<toolset>intel:<warnings>on
|
||||
|
||||
@@ -42,6 +45,10 @@ path-constant BOOST_UNORDERED_TEST_DIR : . ;
|
||||
|
||||
run quick.cpp ;
|
||||
|
||||
compile debuggability/visualization_tests.cpp
|
||||
: <target-os>cygwin:<define>_XOPEN_SOURCE=600
|
||||
;
|
||||
|
||||
compile unordered/self_include_tests_obj.cpp
|
||||
: <define>BOOST_UNORDERED_HEADER="boost/unordered_map.hpp"
|
||||
: tl_unordered_map_hpp ;
|
||||
@@ -108,6 +115,7 @@ local FCA_TESTS =
|
||||
move_tests
|
||||
narrow_cast_tests
|
||||
node_handle_tests
|
||||
node_handle_allocator_tests
|
||||
noexcept_tests
|
||||
post_move_tests
|
||||
prime_fmod_tests
|
||||
@@ -148,13 +156,17 @@ run unordered/serialization_tests.cpp
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
<library>/boost//serialization/<warnings>off ;
|
||||
<library>/boost/serialization//boost_serialization/<warnings>off ;
|
||||
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ;
|
||||
|
||||
compile unordered/explicit_instantiation_tests.cpp : : fca_explicit_instantiation_tests ;
|
||||
compile unordered/explicit_instantiation_tests.cpp : <define>BOOST_UNORDERED_FOA_TESTS : foa_explicit_instantiation_tests ;
|
||||
compile cfoa/explicit_instantiation_tests.cpp : : cfoa_explicit_instantiation_tests ;
|
||||
|
||||
local FCA_EXCEPTION_TESTS =
|
||||
constructor_exception_tests
|
||||
copy_exception_tests
|
||||
@@ -230,6 +242,7 @@ local FOA_TESTS =
|
||||
fancy_pointer_noleak
|
||||
pmr_allocator_tests
|
||||
stats_tests
|
||||
node_handle_allocator_tests
|
||||
;
|
||||
|
||||
for local test in $(FOA_TESTS)
|
||||
@@ -251,7 +264,7 @@ run unordered/serialization_tests.cpp
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
<library>/boost//serialization/<warnings>off
|
||||
<library>/boost/serialization//boost_serialization/<warnings>off
|
||||
: foa_serialization_tests ;
|
||||
|
||||
local FOA_EXCEPTION_TESTS =
|
||||
@@ -286,11 +299,19 @@ local MMAP_CONTAINERS =
|
||||
|
||||
for local container in $(MMAP_CONTAINERS)
|
||||
{
|
||||
run unordered/mmap_tests.cpp /boost/filesystem//boost_filesystem : :
|
||||
run unordered/mmap_tests.cpp
|
||||
/boost/filesystem//boost_filesystem
|
||||
/boost/interprocess//boost_interprocess
|
||||
/boost/process//boost_process
|
||||
/boost/uuid//boost_uuid
|
||||
: :
|
||||
: <define>BOOST_UNORDERED_FOA_MMAP_MAP_TYPE="boost::$(container)"
|
||||
<warnings>off
|
||||
<link>static
|
||||
<toolset>gcc-4.8:<build>no # Boost.Process does not compile
|
||||
<toolset>gcc-4.9:<build>no # ditto
|
||||
<target-os>cygwin:<build>no
|
||||
<toolset>gcc,<target-os>windows:<build>no # Process on MinGW on Appveyor doesn't compile
|
||||
: foa_mmap_$(container)_tests ;
|
||||
}
|
||||
|
||||
@@ -306,11 +327,10 @@ alias foa_tests :
|
||||
;
|
||||
|
||||
local CFOA_TESTS =
|
||||
insert_tests
|
||||
erase_tests
|
||||
try_emplace_tests
|
||||
emplace_tests
|
||||
visit_tests
|
||||
extract_insert_tests
|
||||
constructor_tests
|
||||
assign_tests
|
||||
clear_tests
|
||||
@@ -336,6 +356,7 @@ local CFOA_TESTS =
|
||||
explicit_alloc_ctor_tests
|
||||
pmr_allocator_tests
|
||||
stats_tests
|
||||
node_handle_allocator_tests
|
||||
;
|
||||
|
||||
for local test in $(CFOA_TESTS)
|
||||
@@ -346,6 +367,28 @@ for local test in $(CFOA_TESTS)
|
||||
;
|
||||
}
|
||||
|
||||
run cfoa/insert_tests.cpp
|
||||
:
|
||||
:
|
||||
: $(CPP11) <threading>multi
|
||||
<toolset>msvc:<cxxflags>/bigobj
|
||||
<toolset>gcc:<inlining>on
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
: cfoa_insert_tests ;
|
||||
|
||||
run cfoa/visit_tests.cpp
|
||||
:
|
||||
:
|
||||
: $(CPP11) <threading>multi
|
||||
<toolset>msvc:<cxxflags>/bigobj
|
||||
<toolset>gcc:<inlining>on
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
: cfoa_visit_tests ;
|
||||
|
||||
run cfoa/serialization_tests.cpp
|
||||
:
|
||||
:
|
||||
@@ -357,12 +400,12 @@ run cfoa/serialization_tests.cpp
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
<library>/boost//serialization/<warnings>off
|
||||
<library>/boost/serialization//boost_serialization/<warnings>off
|
||||
: cfoa_serialization_tests ;
|
||||
|
||||
rule make_cfoa_interprocess_concurrency_tests ( name : defines ? )
|
||||
{
|
||||
run cfoa/interprocess_concurrency_tests.cpp /boost/filesystem//boost_filesystem : :
|
||||
run cfoa/interprocess_concurrency_tests.cpp : :
|
||||
: <define>$(defines)
|
||||
<warnings>off
|
||||
<link>static
|
||||
@@ -370,7 +413,14 @@ rule make_cfoa_interprocess_concurrency_tests ( name : defines ? )
|
||||
<toolset>clang-3.6:<build>no # idem
|
||||
<toolset>clang-3.7:<build>no # idem
|
||||
<toolset>clang-3.8:<build>no # idem
|
||||
<toolset>gcc-4.8:<build>no # idem
|
||||
<toolset>gcc-4.9:<build>no # idem
|
||||
<target-os>cygwin:<build>no
|
||||
<toolset>gcc,<target-os>windows:<build>no # Process on MinGW on Appveyor doesn't compile
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/interprocess//boost_interprocess
|
||||
<library>/boost/process//boost_process
|
||||
<library>/boost/uuid//boost_uuid
|
||||
: $(name) ;
|
||||
}
|
||||
|
||||
@@ -381,6 +431,8 @@ make_cfoa_interprocess_concurrency_tests cfoa_interproc_conc_tests_stats
|
||||
|
||||
alias cfoa_tests :
|
||||
cfoa_$(CFOA_TESTS)
|
||||
cfoa_insert_tests
|
||||
cfoa_visit_tests
|
||||
cfoa_serialization_tests
|
||||
cfoa_interproc_conc_tests
|
||||
cfoa_interproc_conc_tests_stats ;
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 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)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include "../helpers/replace_allocator.hpp"
|
||||
#include "../objects/non_default_ctble_allocator.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#if defined(__clang__) && defined(__has_warning)
|
||||
|
||||
@@ -37,19 +44,35 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using fancy_map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator2<std::pair<raii const, raii> > >;
|
||||
|
||||
using fancy_node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator2<std::pair<raii const, raii> > >;
|
||||
|
||||
using fancy_set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator2<raii> >;
|
||||
|
||||
using fancy_node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator2<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
fancy_map_type* fancy_test_map;
|
||||
fancy_node_map_type* fancy_test_node_map;
|
||||
fancy_set_type* fancy_test_set;
|
||||
fancy_node_set_type* fancy_test_node_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> map_init_list{
|
||||
{raii{0}, raii{0}},
|
||||
@@ -102,7 +125,9 @@ std::initializer_list<set_type::value_type> set_init_list{
|
||||
};
|
||||
|
||||
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
|
||||
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
|
||||
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
|
||||
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
|
||||
|
||||
template <class T,bool POCCA, bool POCMA>
|
||||
struct poca_allocator: fancy_allocator<T>
|
||||
@@ -862,6 +887,28 @@ namespace {
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class X, class GF>
|
||||
void initializer_list_assign_gh276(
|
||||
X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
// https://github.com/boostorg/unordered/issues/276
|
||||
|
||||
using replaced_allocator_container = test::replace_allocator<
|
||||
X, test::non_default_ctble_allocator<int> >;
|
||||
using replaced_allocator_type =
|
||||
typename replaced_allocator_container::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(4, [&] { return gen(rg); });
|
||||
|
||||
replaced_allocator_container
|
||||
x(replaced_allocator_type(0)),
|
||||
y(values.begin(), values.end(), replaced_allocator_type(0));
|
||||
|
||||
x = {values[0], values[1], values[2], values[3]};
|
||||
BOOST_TEST(x == y);
|
||||
}
|
||||
|
||||
template <class X, class GF>
|
||||
void insert_and_assign(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
@@ -928,7 +975,7 @@ namespace {
|
||||
}
|
||||
|
||||
template <class X, class GF>
|
||||
void flat_move_assign(X*, GF gen_factory, test::random_generator rg)
|
||||
void nonconcurrent_move_assign(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
@@ -950,16 +997,17 @@ namespace {
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
flat_container<X> flat(values.begin(), values.end(), values.size(),
|
||||
nonconcurrent_container<X> nonc(
|
||||
values.begin(), values.end(), values.size(),
|
||||
hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
X x(0, hasher(2), key_equal(1), allocator_type(3));
|
||||
|
||||
BOOST_TEST(flat.get_allocator() == x.get_allocator());
|
||||
BOOST_TEST(nonc.get_allocator() == x.get_allocator());
|
||||
|
||||
x = std::move(flat);
|
||||
x = std::move(nonc);
|
||||
|
||||
BOOST_TEST(flat.empty());
|
||||
BOOST_TEST(nonc.empty());
|
||||
BOOST_TEST_EQ(x.size(), reference_cont.size());
|
||||
|
||||
test_fuzzy_matches_reference(x, reference_cont, rg);
|
||||
@@ -983,17 +1031,18 @@ namespace {
|
||||
X x(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
|
||||
flat_container<X> flat(0, hasher(2), key_equal(1), allocator_type(3));
|
||||
nonconcurrent_container<X> nonc(
|
||||
0, hasher(2), key_equal(1), allocator_type(3));
|
||||
|
||||
BOOST_TEST(flat.get_allocator() == x.get_allocator());
|
||||
BOOST_TEST(nonc.get_allocator() == x.get_allocator());
|
||||
|
||||
flat = std::move(x);
|
||||
nonc = std::move(x);
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(flat.size(), reference_cont.size());
|
||||
BOOST_TEST_EQ(nonc.size(), reference_cont.size());
|
||||
|
||||
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||
@@ -1008,16 +1057,17 @@ namespace {
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
flat_container<X> flat(values.begin(), values.end(), values.size(),
|
||||
nonconcurrent_container<X> nonc(
|
||||
values.begin(), values.end(), values.size(),
|
||||
hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
X x(0, hasher(2), key_equal(1), allocator_type(4));
|
||||
|
||||
BOOST_TEST(flat.get_allocator() != x.get_allocator());
|
||||
BOOST_TEST(nonc.get_allocator() != x.get_allocator());
|
||||
|
||||
x = std::move(flat);
|
||||
x = std::move(nonc);
|
||||
|
||||
BOOST_TEST(flat.empty());
|
||||
BOOST_TEST(nonc.empty());
|
||||
BOOST_TEST_EQ(x.size(), reference_cont.size());
|
||||
|
||||
test_fuzzy_matches_reference(x, reference_cont, rg);
|
||||
@@ -1043,17 +1093,18 @@ namespace {
|
||||
X x(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
|
||||
flat_container<X> flat(0, hasher(2), key_equal(1), allocator_type(4));
|
||||
nonconcurrent_container<X> nonc(
|
||||
0, hasher(2), key_equal(1), allocator_type(4));
|
||||
|
||||
BOOST_TEST(flat.get_allocator() != x.get_allocator());
|
||||
BOOST_TEST(nonc.get_allocator() != x.get_allocator());
|
||||
|
||||
flat = std::move(x);
|
||||
nonc = std::move(x);
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(flat.size(), reference_cont.size());
|
||||
BOOST_TEST_EQ(nonc.size(), reference_cont.size());
|
||||
|
||||
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||
@@ -1073,29 +1124,37 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
copy_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
initializer_list_assign,
|
||||
((test_map_and_init_list)(test_set_and_init_list)))
|
||||
((test_map_and_init_list)(test_node_map_and_init_list)
|
||||
(test_set_and_init_list)(test_node_set_and_init_list)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
initializer_list_assign_gh276,
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((init_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
flat_move_assign,
|
||||
((test_map)(test_set)(fancy_test_map)(fancy_test_set))
|
||||
nonconcurrent_move_assign,
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)
|
||||
(fancy_test_map)(fancy_test_node_map)(fancy_test_set)(fancy_test_node_set))
|
||||
((init_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 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)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{674140082};
|
||||
|
||||
@@ -20,11 +22,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
template <class X, class GF>
|
||||
@@ -130,12 +140,12 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
clear_tests,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(insert_and_clear,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 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)
|
||||
|
||||
|
||||
#ifndef BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_HPP
|
||||
#define BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_HPP
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_node_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_node_set_fwd.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
@@ -27,6 +31,31 @@ struct value_cardinality<std::pair<K, V> >
|
||||
static constexpr std::size_t value=2;
|
||||
};
|
||||
|
||||
template <typename K>
|
||||
struct value_nonconst_cardinality
|
||||
{
|
||||
static constexpr std::size_t value=1;
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct value_nonconst_cardinality<std::pair<K, V> >
|
||||
{
|
||||
static constexpr std::size_t value=
|
||||
1 * !std::is_const<K>::value +
|
||||
1 * !std::is_const<V>::value ;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct is_container_node_based: std::false_type {};
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct is_container_node_based<boost::concurrent_node_map<K, V, H, P, A> >
|
||||
: std::true_type {};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct is_container_node_based<boost::concurrent_node_set<K, H, P, A> >
|
||||
: std::true_type {};
|
||||
|
||||
template <class Container>
|
||||
struct reference_container_impl;
|
||||
|
||||
@@ -39,30 +68,55 @@ struct reference_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
|
||||
using type = boost::unordered_flat_map<K, V>;
|
||||
};
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct reference_container_impl<boost::concurrent_node_map<K, V, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_node_map<K, V>;
|
||||
};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct reference_container_impl<boost::concurrent_flat_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_set<K>;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct flat_container_impl;
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct reference_container_impl<boost::concurrent_node_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_node_set<K>;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
using flat_container = typename flat_container_impl<Container>::type;
|
||||
struct nonconcurrent_container_impl;
|
||||
|
||||
template <class Container>
|
||||
using nonconcurrent_container =
|
||||
typename nonconcurrent_container_impl<Container>::type;
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct flat_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
|
||||
struct nonconcurrent_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_map<K, V, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct nonconcurrent_container_impl<boost::concurrent_node_map<K, V, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_node_map<K, V, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct flat_container_impl<boost::concurrent_flat_set<K, H, P, A> >
|
||||
struct nonconcurrent_container_impl<boost::concurrent_flat_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_set<K, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct nonconcurrent_container_impl<boost::concurrent_node_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_node_set<K, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename Container, template <typename> class Allocator>
|
||||
struct replace_allocator_impl;
|
||||
|
||||
@@ -95,7 +149,33 @@ struct replace_allocator_impl<
|
||||
using type =
|
||||
boost::concurrent_flat_set<K, H, P, Allocator<value_type> >;
|
||||
};
|
||||
|
||||
|
||||
template <
|
||||
typename K, typename V, typename H, typename P, typename A,
|
||||
template <typename> class Allocator
|
||||
>
|
||||
struct replace_allocator_impl<
|
||||
boost::concurrent_node_map<K, V, H, P, A>, Allocator>
|
||||
{
|
||||
using value_type =
|
||||
typename boost::concurrent_node_map<K, V, H, P, A>::value_type;
|
||||
using type =
|
||||
boost::concurrent_node_map<K, V, H, P, Allocator<value_type> >;
|
||||
};
|
||||
|
||||
template <
|
||||
typename K, typename H, typename P, typename A,
|
||||
template <typename> class Allocator
|
||||
>
|
||||
struct replace_allocator_impl<
|
||||
boost::concurrent_node_set<K, H, P, A>, Allocator>
|
||||
{
|
||||
using value_type =
|
||||
typename boost::concurrent_node_set<K, H, P, A>::value_type;
|
||||
using type =
|
||||
boost::concurrent_node_set<K, H, P, Allocator<value_type> >;
|
||||
};
|
||||
|
||||
template <typename K>
|
||||
K const& get_key(K const& x) { return x; }
|
||||
|
||||
|
||||