Compare commits
110 Commits
boost-1.79
...
esp-idf-co
Author | SHA1 | Date | |
---|---|---|---|
016d255851 | |||
6b87a43162 | |||
a4c6bf90aa | |||
a31e894411 | |||
91e78fd746 | |||
3abe5de533 | |||
dfa3c7311f | |||
2c5b8577aa | |||
4e804a9d4d | |||
0ca8c5f56f | |||
912798e5cb | |||
5bcdd7fdf0 | |||
78ffc4c192 | |||
966b76182f | |||
b7101494f2 | |||
be467b3dc4 | |||
ee70d96c75 | |||
8fbd380879 | |||
7746518c0a | |||
c8a98e27e0 | |||
3df902af23 | |||
45542e26cb | |||
49f73b118c | |||
6e3dcfddb0 | |||
09088045ac | |||
e466232757 | |||
2ccd6654c1 | |||
7d7a6b881e | |||
9661227d00 | |||
5855c67d4d | |||
3edfe2b76f | |||
f36bfe24f6 | |||
9da4b3a45a | |||
95524a6af4 | |||
d4b61541b5 | |||
143c378ba6 | |||
dfac93aebb | |||
b6daca37d5 | |||
4937619ea0 | |||
6d34532301 | |||
fb733483c6 | |||
2670bb149d | |||
d49eda63f8 | |||
08e0fee141 | |||
d204b9b408 | |||
c53e0228c5 | |||
31cffd8412 | |||
0f71fe28a2 | |||
f00a29d3df | |||
e111389d6c | |||
7079341416 | |||
7fdbfc0c1a | |||
e1dff1c931 | |||
90b2536a99 | |||
97f54318e3 | |||
f1481f0deb | |||
b1a9cde690 | |||
1ed2a0a0f1 | |||
759645cab6 | |||
3203251539 | |||
b84b94b4db | |||
3dd77edd16 | |||
a24165083f | |||
8878482ca1 | |||
19c7bbf97d | |||
23f15947d3 | |||
152129bf70 | |||
20ac32c34c | |||
707b40e8c2 | |||
0d1d9f4634 | |||
5a64ca48ad | |||
f4ddf18124 | |||
657cf68e55 | |||
f0013a4d65 | |||
83fe0249b3 | |||
3701199cfa | |||
4b56bfac05 | |||
613a997694 | |||
e690e8910c | |||
34b9a8d21f | |||
0106ed3d91 | |||
f8342e4b04 | |||
8a74b192b0 | |||
d0ac539d09 | |||
0be4856144 | |||
7503b85f6a | |||
4a9abf20b3 | |||
2836bb5c41 | |||
e2925ba01b | |||
f2724b745b | |||
712d20079a | |||
99a5409b39 | |||
72fe06aa00 | |||
8081a3f9ff | |||
f218f9b5a2 | |||
4e38751187 | |||
e36e3bcf96 | |||
18503e5eb8 | |||
110c5dcf10 | |||
37f5a462e4 | |||
a1fb756831 | |||
5a456eb295 | |||
ad639ffb61 | |||
2ae686c366 | |||
641c9fba9c | |||
8473d8120f | |||
954db4e246 | |||
4f43bc5ec7 | |||
0bcc79baab | |||
e7d34a5ab1 |
131
.appveyor.yml
@ -1,4 +1,6 @@
|
||||
# Copyright 2017 Daniel James
|
||||
# Copyright 2016, 2017 Peter Dimov
|
||||
# Copyright 2017 - 2019 James E. King III
|
||||
# Copyright 2019 - 2021 Alexander Grund
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -10,48 +12,103 @@ branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
- /bugfix\/.*/
|
||||
- /feature\/.*/
|
||||
- /fix\/.*/
|
||||
- /pr\/.*/
|
||||
|
||||
matrix:
|
||||
fast_finish: false
|
||||
# Adding MAYFAIL to any matrix job allows it to fail but the build stays green:
|
||||
allow_failures:
|
||||
- MAYFAIL: true
|
||||
|
||||
environment:
|
||||
global:
|
||||
B2_CI_VERSION: 1
|
||||
GIT_FETCH_JOBS: 4
|
||||
B2_ADDRESS_MODEL: 32,64
|
||||
B2_VARIANT: debug,release
|
||||
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
TOOLSET: msvc-9.0,msvc-10.0,msvc-11.0
|
||||
ADDRMD: 32
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
TOOLSET: msvc-12.0,msvc-14.0
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
TOOLSET: msvc-14.1
|
||||
CXXSTD: 14,17
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: clang-win
|
||||
CXXSTD: 14
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: clang-win
|
||||
CXXSTD: 17
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: clang-win
|
||||
CXXSTD: latest
|
||||
ADDRMD: 32,64
|
||||
- FLAVOR: Visual Studio 2008, 2010, 2012
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-9.0,msvc-10.0,msvc-11.0
|
||||
B2_ADDRESS_MODEL: 32 # No 64bit support
|
||||
|
||||
- FLAVOR: Visual Studio 2013, 2015
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
B2_TOOLSET: msvc-12.0,msvc-14.0
|
||||
|
||||
- FLAVOR: Visual Studio 2017
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
B2_CXXSTD: 14,17,latest
|
||||
B2_TOOLSET: msvc-14.1
|
||||
|
||||
- FLAVOR: cygwin (32-bit)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
ADDPATH: C:\cygwin\bin;
|
||||
B2_ADDRESS_MODEL: 32
|
||||
B2_CXXSTD: 03,11,14,1z
|
||||
B2_TOOLSET: gcc
|
||||
|
||||
- FLAVOR: cygwin (64-bit)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 03,11,14,1z
|
||||
B2_TOOLSET: gcc
|
||||
|
||||
- FLAVOR: cygwin (64-bit, latest)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 03,11
|
||||
B2_TOOLSET: gcc
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
|
||||
- FLAVOR: cygwin (64-bit, latest)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 14,1z
|
||||
B2_TOOLSET: gcc
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
|
||||
- FLAVOR: mingw-w64, 32 bit
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
|
||||
B2_CXXSTD: 03,11,14,17,2a
|
||||
B2_TOOLSET: gcc
|
||||
B2_ADDRESS_MODEL: 32
|
||||
|
||||
- FLAVOR: mingw-w64, 64 bit
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
|
||||
B2_CXXSTD: 03,11,14,17,2a
|
||||
B2_TOOLSET: gcc
|
||||
B2_ADDRESS_MODEL: 64
|
||||
|
||||
#- FLAVOR: CodeCov (VS 2019)
|
||||
# APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
# B2_CXXFLAGS: -permissive-
|
||||
# B2_CXXSTD: 14
|
||||
# B2_TOOLSET: msvc-14.2
|
||||
# COVERAGE: true
|
||||
|
||||
install:
|
||||
- set BOOST_BRANCH=develop
|
||||
- if "%APPVEYOR_REPO_BRANCH%" == "master" set BOOST_BRANCH=master
|
||||
- cd ..
|
||||
- git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
- cd boost-root
|
||||
- git submodule update --init tools/boostdep
|
||||
- xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\unordered\
|
||||
- python tools/boostdep/depinst/depinst.py unordered
|
||||
- cmd /c bootstrap
|
||||
- b2 -d0 headers
|
||||
- git clone --depth 1 https://github.com/boostorg/boost-ci.git C:\boost-ci-cloned
|
||||
# Copy ci folder if not testing Boost.CI
|
||||
- if NOT "%APPVEYOR_PROJECT_NAME%" == "boost-ci" xcopy /s /e /q /i /y C:\boost-ci-cloned\ci .\ci
|
||||
- rmdir /s /q C:\boost-ci-cloned
|
||||
- ci\appveyor\install.bat
|
||||
|
||||
build: off
|
||||
|
||||
test_script:
|
||||
- if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
|
||||
- if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
|
||||
- b2 -j3 libs/unordered/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
test_script: ci\build.bat
|
||||
|
||||
for:
|
||||
# CodeCov coverage build
|
||||
- matrix:
|
||||
only: [COVERAGE: true]
|
||||
test_script: [ps: ci\codecov.ps1]
|
||||
|
23
.codecov.yml
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright 2019 - 2021 Alexander Grund
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# Sample codecov configuration file. Edit as required
|
||||
|
||||
codecov:
|
||||
max_report_age: off
|
||||
require_ci_to_pass: yes
|
||||
notify:
|
||||
# Increase this if you have multiple coverage collection jobs
|
||||
after_n_builds: 1
|
||||
wait_for_ci: yes
|
||||
|
||||
# Change how pull request comments look
|
||||
comment:
|
||||
layout: "reach,diff,flags,files,footer"
|
||||
|
||||
# Ignore specific files or folders. Glob patterns are supported.
|
||||
# See https://docs.codecov.com/docs/ignoring-paths
|
||||
ignore:
|
||||
- extra/**/*
|
||||
# - test/**/*
|
180
.drone.jsonnet
Normal file
@ -0,0 +1,180 @@
|
||||
# Copyright 2022 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
local library = "unordered";
|
||||
|
||||
local triggers =
|
||||
{
|
||||
branch: [ "master", "develop", "feature/*", "bugfix/*" ]
|
||||
};
|
||||
|
||||
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
|
||||
local asan = { ASAN: '1' };
|
||||
|
||||
local linux_pipeline(name, image, environment, packages = "", sources = [], arch = "amd64") =
|
||||
{
|
||||
name: name,
|
||||
kind: "pipeline",
|
||||
type: "docker",
|
||||
trigger: triggers,
|
||||
platform:
|
||||
{
|
||||
os: "linux",
|
||||
arch: arch
|
||||
},
|
||||
steps:
|
||||
[
|
||||
{
|
||||
name: "everything",
|
||||
image: image,
|
||||
environment: environment,
|
||||
commands:
|
||||
[
|
||||
'set -e',
|
||||
'wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -',
|
||||
] +
|
||||
(if sources != [] then [ ('apt-add-repository "' + source + '"') for source in sources ] else []) +
|
||||
(if packages != "" then [ 'apt-get update', 'apt-get -y install ' + packages ] else []) +
|
||||
[
|
||||
'export LIBRARY=' + library,
|
||||
'./.drone/drone.sh',
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
local macos_pipeline(name, environment, xcode_version = "12.2", osx_version = "catalina", arch = "amd64") =
|
||||
{
|
||||
name: name,
|
||||
kind: "pipeline",
|
||||
type: "exec",
|
||||
trigger: triggers,
|
||||
platform: {
|
||||
"os": "darwin",
|
||||
"arch": arch
|
||||
},
|
||||
node: {
|
||||
"os": osx_version
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: "everything",
|
||||
environment: environment + { "DEVELOPER_DIR": "/Applications/Xcode-" + xcode_version + ".app/Contents/Developer" },
|
||||
commands:
|
||||
[
|
||||
'export LIBRARY=' + library,
|
||||
'./.drone/drone.sh',
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
{
|
||||
name: name,
|
||||
kind: "pipeline",
|
||||
type: "docker",
|
||||
trigger: triggers,
|
||||
platform:
|
||||
{
|
||||
os: "windows",
|
||||
arch: arch
|
||||
},
|
||||
"steps":
|
||||
[
|
||||
{
|
||||
name: "everything",
|
||||
image: image,
|
||||
environment: environment,
|
||||
commands:
|
||||
[
|
||||
'cmd /C .drone\\\\drone.bat ' + library,
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
[
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.4 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.4', CXXSTD: '98,0x', ADDRMD: '32,64' },
|
||||
"g++-4.4-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.6 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.6', CXXSTD: '98,0x', ADDRMD: '32,64' },
|
||||
"g++-4.6-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.7 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.7', CXXSTD: '98,0x', ADDRMD: '32,64' },
|
||||
"g++-4.7-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.8* 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.9 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '03,11', ADDRMD: '32,64' },
|
||||
"g++-4.9-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* ARM64 32",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32' },
|
||||
arch="arm64",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* ARM64 64",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '64' },
|
||||
arch="arm64",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* S390x 32",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32' },
|
||||
arch="s390x",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* S390x 64",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '64' },
|
||||
arch="s390x",
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 10.15 Xcode 12.2 UBSAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + ubsan,
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 10.15 Xcode 12.2 ASAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + asan,
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2017 msvc-14.1",
|
||||
"cppalliance/dronevs2017",
|
||||
{ TOOLSET: 'msvc-14.1', CXXSTD: '14,17,latest' },
|
||||
),
|
||||
]
|
23
.drone/drone.bat
Normal file
@ -0,0 +1,23 @@
|
||||
@REM Copyright 2022 Peter Dimov
|
||||
@REM Distributed under the Boost Software License, Version 1.0.
|
||||
@REM https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@ECHO ON
|
||||
|
||||
set LIBRARY=%1
|
||||
set DRONE_BUILD_DIR=%CD%
|
||||
|
||||
set BOOST_BRANCH=develop
|
||||
if "%DRONE_BRANCH%" == "master" set BOOST_BRANCH=master
|
||||
cd ..
|
||||
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
git submodule update --init tools/boostdep
|
||||
xcopy /s /e /q %DRONE_BUILD_DIR% libs\%LIBRARY%\
|
||||
python tools/boostdep/depinst/depinst.py %LIBRARY%
|
||||
cmd /c bootstrap
|
||||
b2 -d0 headers
|
||||
|
||||
if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
|
||||
if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
24
.drone/drone.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2022 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
set -ex
|
||||
|
||||
DRONE_BUILD_DIR=$(pwd)
|
||||
|
||||
BOOST_BRANCH=develop
|
||||
if [ "$DRONE_BRANCH" = "master" ]; then BOOST_BRANCH=master; fi
|
||||
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
git submodule update --init tools/boostdep
|
||||
cp -r $DRONE_BUILD_DIR/* libs/$LIBRARY
|
||||
python tools/boostdep/depinst/depinst.py $LIBRARY
|
||||
./bootstrap.sh
|
||||
./b2 -d0 headers
|
||||
|
||||
echo "using $TOOLSET : : $COMPILER ;" > ~/user-config.jam
|
||||
./b2 -j3 libs/$LIBRARY/test toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${ADDRMD:+address-model=$ADDRMD} ${UBSAN:+undefined-sanitizer=norecover debug-symbols=on} ${ASAN:+address-sanitizer=norecover debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS}
|
521
.github/workflows/ci.yml
vendored
@ -1,3 +1,11 @@
|
||||
# Copyright 2020-2021 Peter Dimov
|
||||
# Copyright 2021 Andrey Semashev
|
||||
# Copyright 2021 Alexander Grund
|
||||
# Copyright 2022 James E. King III
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
---
|
||||
name: CI
|
||||
|
||||
on:
|
||||
@ -6,207 +14,384 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- bugfix/**
|
||||
- feature/**
|
||||
- fix/**
|
||||
- pr/**
|
||||
|
||||
concurrency:
|
||||
group: ${{format('{0}:{1}', github.repository, github.ref)}}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
GIT_FETCH_JOBS: 8
|
||||
NET_RETRY_COUNT: 5
|
||||
B2_CI_VERSION: 1
|
||||
B2_VARIANT: debug,release
|
||||
LCOV_BRANCH_COVERAGE: 0
|
||||
CODECOV_NAME: Github Actions
|
||||
|
||||
jobs:
|
||||
posix:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: gcc-4.8
|
||||
cxxstd: "03,11"
|
||||
os: ubuntu-18.04
|
||||
install: g++-4.8
|
||||
- toolset: gcc-5
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: g++-5
|
||||
- toolset: gcc-6
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: g++-6
|
||||
- toolset: gcc-7
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
- toolset: gcc-8
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-18.04
|
||||
install: g++-8
|
||||
- toolset: gcc-9
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-18.04
|
||||
- toolset: gcc-9
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: gcc-10
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: gcc-11
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
install: g++-11
|
||||
sanitizers: true
|
||||
- toolset: clang
|
||||
compiler: clang++-3.9
|
||||
cxxstd: "03,11,14"
|
||||
os: ubuntu-18.04
|
||||
install: clang-3.9
|
||||
- toolset: clang
|
||||
compiler: clang++-4.0
|
||||
cxxstd: "03,11,14"
|
||||
os: ubuntu-18.04
|
||||
install: clang-4.0
|
||||
- toolset: clang
|
||||
compiler: clang++-5.0
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
install: clang-5.0
|
||||
- toolset: clang
|
||||
compiler: clang++-6.0
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
install: clang-6.0
|
||||
- toolset: clang
|
||||
compiler: clang++-7
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
install: clang-7
|
||||
- toolset: clang
|
||||
compiler: clang++-8
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-20.04
|
||||
install: clang-8
|
||||
- toolset: clang
|
||||
compiler: clang++-9
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-20.04
|
||||
install: clang-9
|
||||
- toolset: clang
|
||||
compiler: clang++-10
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-20.04
|
||||
- toolset: clang
|
||||
compiler: clang++-11
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
- toolset: clang
|
||||
compiler: clang++-12
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
sanitizers: true
|
||||
- toolset: clang
|
||||
cxxstd: "03,11,14,17"
|
||||
os: macos-10.15
|
||||
sanitizers: true
|
||||
# Linux, gcc
|
||||
- { compiler: gcc-4.8, cxxstd: '03,11', os: ubuntu-18.04, install: 'g++-4.8-multilib', address-model: '32,64' }
|
||||
- { compiler: gcc-4.9, cxxstd: '03,11', os: ubuntu-20.04, container: 'ubuntu:16.04' }
|
||||
- { compiler: gcc-5, cxxstd: '03,11,14,1z', os: ubuntu-18.04, install: 'g++-5-multilib', address-model: '32,64' }
|
||||
- { compiler: gcc-6, cxxstd: '03,11,14,17', os: ubuntu-18.04, install: 'g++-6-multilib', address-model: '32,64' }
|
||||
- { compiler: gcc-7, cxxstd: '03,11,14,17', os: ubuntu-18.04, install: 'g++-7-multilib', address-model: '32,64' }
|
||||
- { compiler: gcc-8, cxxstd: '03,11,14,17,2a', os: ubuntu-18.04, install: 'g++-8-multilib', address-model: '32,64' }
|
||||
- { compiler: gcc-9, cxxstd: '03,11,14,17,2a', os: ubuntu-18.04, install: 'g++-9-multilib', address-model: '32,64' }
|
||||
- { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, install: 'g++-10-multilib', address-model: '32,64' }
|
||||
- { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, install: 'g++-11-multilib', address-model: '32,64' }
|
||||
- { compiler: gcc-12, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-12-multilib', address-model: '32,64' }
|
||||
- { name: GCC w/ sanitizers, sanitize: yes,
|
||||
compiler: gcc-12, cxxstd: '03,11,14,17,20', os: ubuntu-22.04 }
|
||||
- { name: Collect coverage, coverage: yes,
|
||||
compiler: gcc-8, cxxstd: '03,11', os: ubuntu-20.04, install: 'g++-8-multilib', address-model: '32,64' }
|
||||
|
||||
# Linux, clang
|
||||
- { compiler: clang-3.7, cxxstd: '03,11,14', os: ubuntu-20.04, container: 'ubuntu:16.04' }
|
||||
- { compiler: clang-3.8, cxxstd: '03,11,14', os: ubuntu-20.04, container: 'ubuntu:16.04' }
|
||||
- { compiler: clang-3.9, cxxstd: '03,11,14', os: ubuntu-18.04 }
|
||||
- { compiler: clang-4.0, cxxstd: '03,11,14', os: ubuntu-18.04 }
|
||||
- { compiler: clang-5.0, cxxstd: '03,11,14,1z', os: ubuntu-18.04 }
|
||||
- { compiler: clang-6.0, cxxstd: '03,11,14,17', os: ubuntu-18.04 }
|
||||
- { compiler: clang-7, cxxstd: '03,11,14,17', os: ubuntu-18.04 }
|
||||
- { compiler: clang-8, cxxstd: '03,11,14,17', os: ubuntu-18.04 }
|
||||
- { compiler: clang-9, cxxstd: '03,11,14,17,2a', os: ubuntu-20.04 }
|
||||
- { compiler: clang-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04 }
|
||||
- { compiler: clang-11, cxxstd: '03,11,14,17,20', os: ubuntu-20.04 }
|
||||
- { compiler: clang-12, cxxstd: '03,11,14,17,20', os: ubuntu-20.04 }
|
||||
- { compiler: clang-13, cxxstd: '03,11,14,17,20', os: ubuntu-22.04 }
|
||||
- { compiler: clang-14, cxxstd: '03,11,14,17,20', os: ubuntu-22.04 }
|
||||
|
||||
# libc++
|
||||
- { compiler: clang-6.0, cxxstd: '03,11,14', os: ubuntu-18.04, stdlib: libc++, install: 'clang-6.0 libc++-dev libc++abi-dev' }
|
||||
- { compiler: clang-12, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev' }
|
||||
- { name: Clang w/ sanitizers, sanitize: yes,
|
||||
compiler: clang-12, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev' }
|
||||
|
||||
# OSX, clang
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, sanitize: yes }
|
||||
|
||||
timeout-minutes: 120
|
||||
runs-on: ${{matrix.os}}
|
||||
container: ${{matrix.container}}
|
||||
env: {B2_USE_CCACHE: 1}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup environment
|
||||
run: |
|
||||
if [ -f "/etc/debian_version" ]; then
|
||||
echo "DEBIAN_FRONTEND=noninteractive" >> $GITHUB_ENV
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
fi
|
||||
if [ -n "${{matrix.container}}" ] && [ -f "/etc/debian_version" ]; then
|
||||
apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
|
||||
apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y sudo software-properties-common
|
||||
# Need (newer) git, and the older Ubuntu container may require requesting the key manually using port 80
|
||||
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys E1DD270288B4E6030699E45FA1715D88E1DF1F24
|
||||
for i in {1..${NET_RETRY_COUNT:-3}}; do sudo -E add-apt-repository -y ppa:git-core/ppa && break || sleep 10; done
|
||||
apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
|
||||
apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y g++ python libpython-dev git
|
||||
fi
|
||||
# For jobs not compatible with ccache, use "ccache: no" in the matrix
|
||||
if [[ "${{ matrix.ccache }}" == "no" ]]; then
|
||||
echo "B2_USE_CCACHE=0" >> $GITHUB_ENV
|
||||
fi
|
||||
git config --global pack.threads 0
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
# For coverage builds fetch the whole history, else only 1 commit using a 'fake ternary'
|
||||
fetch-depth: ${{ matrix.coverage && '0' || '1' }}
|
||||
|
||||
- name: Cache ccache
|
||||
uses: actions/cache@v3
|
||||
if: env.B2_USE_CCACHE
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}-${{github.sha}}
|
||||
restore-keys: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}-
|
||||
|
||||
- name: Fetch Boost.CI
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: boostorg/boost-ci
|
||||
ref: master
|
||||
path: boost-ci-cloned
|
||||
|
||||
- name: Get CI scripts folder
|
||||
run: |
|
||||
# Copy ci folder if not testing Boost.CI
|
||||
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
|
||||
rm -rf boost-ci-cloned
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
SOURCE_KEYS=(${{join(matrix.source_keys, ' ')}})
|
||||
SOURCES=(${{join(matrix.sources, ' ')}})
|
||||
# Add this by default
|
||||
SOURCES+=(ppa:ubuntu-toolchain-r/test)
|
||||
for key in "${SOURCE_KEYS[@]}"; do
|
||||
for i in {1..$NET_RETRY_COUNT}; do
|
||||
wget -O - "$key" | sudo apt-key add - && break || sleep 10
|
||||
done
|
||||
done
|
||||
for source in "${SOURCES[@]}"; do
|
||||
for i in {1..$NET_RETRY_COUNT}; do
|
||||
sudo add-apt-repository $source && break || sleep 10
|
||||
done
|
||||
done
|
||||
sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
|
||||
if [[ -z "${{matrix.install}}" ]]; then
|
||||
pkgs="${{matrix.compiler}}"
|
||||
pkgs="${pkgs/gcc-/g++-}"
|
||||
else
|
||||
pkgs="${{matrix.install}}"
|
||||
fi
|
||||
sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y $pkgs
|
||||
|
||||
- name: Setup GCC Toolchain
|
||||
if: matrix.gcc_toolchain
|
||||
run: |
|
||||
GCC_TOOLCHAIN_ROOT="$HOME/gcc-toolchain"
|
||||
echo "GCC_TOOLCHAIN_ROOT=$GCC_TOOLCHAIN_ROOT" >> $GITHUB_ENV
|
||||
MULTIARCH_TRIPLET="$(dpkg-architecture -qDEB_HOST_MULTIARCH)"
|
||||
mkdir -p "$GCC_TOOLCHAIN_ROOT"
|
||||
ln -s /usr/include "$GCC_TOOLCHAIN_ROOT/include"
|
||||
ln -s /usr/bin "$GCC_TOOLCHAIN_ROOT/bin"
|
||||
mkdir -p "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET"
|
||||
ln -s "/usr/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}"
|
||||
|
||||
- name: Setup multiarch
|
||||
if: matrix.multiarch
|
||||
run: |
|
||||
sudo apt-get install --no-install-recommends -y binfmt-support qemu-user-static
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
git clone https://github.com/jeking3/bdde.git
|
||||
echo "$(pwd)/bdde/bin/linux" >> ${GITHUB_PATH}
|
||||
echo "BDDE_DISTRO=${{ matrix.distro }}" >> ${GITHUB_ENV}
|
||||
echo "BDDE_EDITION=${{ matrix.edition }}" >> ${GITHUB_ENV}
|
||||
echo "BDDE_ARCH=${{ matrix.arch }}" >> ${GITHUB_ENV}
|
||||
echo "B2_WRAPPER=bdde" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
|
||||
LIBRARY=${GITHUB_REPOSITORY#*/}
|
||||
echo LIBRARY: $LIBRARY
|
||||
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
|
||||
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
|
||||
echo GITHUB_REF: $GITHUB_REF
|
||||
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
|
||||
REF=${REF#refs/heads/}
|
||||
echo REF: $REF
|
||||
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
|
||||
echo BOOST_BRANCH: $BOOST_BRANCH
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
|
||||
./bootstrap.sh
|
||||
./b2 -d0 headers
|
||||
env:
|
||||
B2_ADDRESS_MODEL: ${{matrix.address-model}}
|
||||
B2_COMPILER: ${{matrix.compiler}}
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_SANITIZE: ${{matrix.sanitize}}
|
||||
B2_STDLIB: ${{matrix.stdlib}}
|
||||
# More entries can be added in the same way, see the B2_ARGS assignment in ci/enforce.sh for the possible keys.
|
||||
# B2_DEFINES: ${{matrix.defines}}
|
||||
# Variables set here (to non-empty) will override the top-level environment variables, e.g.
|
||||
# B2_VARIANT: ${{matrix.variant}}
|
||||
run: source ci/github/install.sh
|
||||
|
||||
- name: Create user-config.jam
|
||||
if: matrix.compiler
|
||||
run: |
|
||||
echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam
|
||||
- name: Setup coverage collection
|
||||
if: matrix.coverage
|
||||
run: ci/github/codecov.sh "setup"
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ../boost-root
|
||||
./b2 -j3 libs/$LIBRARY/test \
|
||||
toolset=${{matrix.toolset}} \
|
||||
cxxstd=${{matrix.cxxstd}} \
|
||||
variant=debug,release \
|
||||
${{(matrix.sanitizers && 'address-sanitizer=norecover undefined-sanitizer=norecover') || ''}}
|
||||
if: '!matrix.coverity'
|
||||
run: ci/build.sh
|
||||
|
||||
- name: Upload coverage
|
||||
if: matrix.coverage
|
||||
run: ci/codecov.sh "upload"
|
||||
|
||||
- name: Run coverity
|
||||
if: matrix.coverity && github.event_name == 'push' && (github.ref_name == 'develop' || github.ref_name == 'master')
|
||||
run: ci/github/coverity.sh
|
||||
env:
|
||||
COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}
|
||||
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
|
||||
windows:
|
||||
defaults:
|
||||
run:
|
||||
shell: cmd
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: msvc-14.0
|
||||
cxxstd: 14,latest
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.1
|
||||
cxxstd: "14,17,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2016
|
||||
- toolset: msvc-14.2
|
||||
cxxstd: "14,17,20,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.3
|
||||
cxxstd: "14,17,20,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2022
|
||||
- toolset: clang-win
|
||||
cxxstd: "14,17,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2022
|
||||
- toolset: gcc
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
addrmd: 64
|
||||
os: windows-2019
|
||||
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: windows-2019 }
|
||||
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2019 }
|
||||
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2022 }
|
||||
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: windows-2022 }
|
||||
- { toolset: gcc, cxxstd: '03,11,14,17,2a', addrmd: '64', os: windows-2019 }
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Fetch Boost.CI
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: boostorg/boost-ci
|
||||
ref: master
|
||||
path: boost-ci-cloned
|
||||
- name: Get CI scripts folder
|
||||
run: |
|
||||
REM Copy ci folder if not testing Boost.CI
|
||||
if "%GITHUB_REPOSITORY%" == "%GITHUB_REPOSITORY:boost-ci=%" xcopy /s /e /q /i /y boost-ci-cloned\ci .\ci
|
||||
rmdir /s /q boost-ci-cloned
|
||||
|
||||
- name: Setup Boost
|
||||
shell: cmd
|
||||
run: |
|
||||
echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
|
||||
for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
|
||||
echo LIBRARY: %LIBRARY%
|
||||
echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
|
||||
echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
|
||||
echo GITHUB_REF: %GITHUB_REF%
|
||||
if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
|
||||
set BOOST_BRANCH=develop
|
||||
for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
|
||||
echo BOOST_BRANCH: %BOOST_BRANCH%
|
||||
cd ..
|
||||
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY%
|
||||
cmd /c bootstrap
|
||||
b2 -d0 headers
|
||||
run: ci\github\install.bat
|
||||
|
||||
- name: Run tests
|
||||
shell: cmd
|
||||
if: '!matrix.coverage'
|
||||
run: ci\build.bat
|
||||
env:
|
||||
B2_TOOLSET: ${{matrix.toolset}}
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_ADDRESS_MODEL: ${{matrix.addrmd}}
|
||||
|
||||
- name: Collect coverage
|
||||
shell: powershell
|
||||
if: matrix.coverage
|
||||
run: ci\opencppcoverage.ps1
|
||||
env:
|
||||
B2_TOOLSET: ${{matrix.toolset}}
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_ADDRESS_MODEL: ${{matrix.addrmd}}
|
||||
|
||||
- name: Upload coverage
|
||||
if: matrix.coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
files: __out/cobertura.xml
|
||||
|
||||
MSYS2:
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { sys: MINGW32, compiler: gcc, cxxstd: '03,11,17,20' }
|
||||
- { sys: MINGW64, compiler: gcc, cxxstd: '03,11,17,20' }
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup MSYS2 environment
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{matrix.sys}}
|
||||
update: true
|
||||
install: git python
|
||||
pacboy: gcc:p cmake:p ninja:p
|
||||
|
||||
- name: Fetch Boost.CI
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: boostorg/boost-ci
|
||||
ref: master
|
||||
path: boost-ci-cloned
|
||||
- name: Get CI scripts folder
|
||||
run: |
|
||||
cd ../boost-root
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release embed-manifest-via=linker
|
||||
# Copy ci folder if not testing Boost.CI
|
||||
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
|
||||
rm -rf boost-ci-cloned
|
||||
|
||||
- name: Setup Boost
|
||||
env:
|
||||
B2_COMPILER: ${{matrix.compiler}}
|
||||
B2_CXXSTD: ${{matrix.cxxstd}}
|
||||
B2_SANITIZE: ${{matrix.sanitize}}
|
||||
B2_STDLIB: ${{matrix.stdlib}}
|
||||
run: ci/github/install.sh
|
||||
|
||||
- name: Run tests
|
||||
run: ci/build.sh
|
||||
|
||||
# Run also the CMake tests to avoid having to setup another matrix for CMake on MSYS
|
||||
- name: Run CMake tests
|
||||
run: |
|
||||
cd "$BOOST_ROOT"
|
||||
mkdir __build_cmake_test__ && cd __build_cmake_test__
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DBoost_VERBOSE=ON ..
|
||||
cmake --build . --target tests --config Debug -j$B2_JOBS
|
||||
ctest --output-on-failure --build-config Debug
|
||||
|
||||
CMake:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { os: ubuntu-20.04, build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
|
||||
- { os: windows-2019, build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
|
||||
|
||||
timeout-minutes: 120
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Fetch Boost.CI
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: boostorg/boost-ci
|
||||
ref: master
|
||||
path: boost-ci-cloned
|
||||
- name: Get CI scripts folder
|
||||
run: |
|
||||
# Copy ci folder if not testing Boost.CI
|
||||
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
|
||||
rm -rf boost-ci-cloned
|
||||
- name: Setup Boost
|
||||
env: {B2_DONT_BOOTSTRAP: 1}
|
||||
run: source ci/github/install.sh
|
||||
|
||||
- name: Run CMake tests
|
||||
run: |
|
||||
cd "$BOOST_ROOT"
|
||||
mkdir __build_cmake_test__ && cd __build_cmake_test__
|
||||
cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DBUILD_TESTING=ON -DBoost_VERBOSE=ON ..
|
||||
cmake --build . --target tests --config ${{matrix.build_type}} -j$B2_JOBS
|
||||
ctest --output-on-failure --build-config ${{matrix.build_type}}
|
||||
|
||||
- name: Run CMake subdir tests
|
||||
run: |
|
||||
cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder
|
||||
[ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_subdir_test"
|
||||
cd "$cmake_test_folder"
|
||||
mkdir __build_cmake_subdir_test__ && cd __build_cmake_subdir_test__
|
||||
cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=OFF -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} ..
|
||||
cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS
|
||||
ctest --output-on-failure --build-config ${{matrix.build_type}}
|
||||
|
||||
- name: Install Library
|
||||
run: |
|
||||
cd "$BOOST_ROOT"
|
||||
mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__
|
||||
cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_INSTALL_PREFIX=~/.local -DBoost_VERBOSE=ON -DBoost_DEBUG=ON ..
|
||||
cmake --build . --target install --config ${{matrix.build_type}} -j$B2_JOBS
|
||||
- name: Run CMake install tests
|
||||
run: |
|
||||
cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder
|
||||
[ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_install_test"
|
||||
cd "$cmake_test_folder"
|
||||
mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__
|
||||
cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_PREFIX_PATH=~/.local ..
|
||||
cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS
|
||||
ctest --output-on-failure --build-config ${{matrix.build_type}}
|
||||
|
@ -1,9 +1,11 @@
|
||||
# Generated by `boostdep --cmake unordered`
|
||||
# Copyright 2020 Peter Dimov
|
||||
# Copyright 2020, 2021 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.16)
|
||||
if(NOT DEFINED IDF_TARGET)
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.20)
|
||||
|
||||
project(boost_unordered VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||
|
||||
@ -16,14 +18,12 @@ target_link_libraries(boost_unordered
|
||||
INTERFACE
|
||||
Boost::assert
|
||||
Boost::config
|
||||
Boost::container
|
||||
Boost::container_hash
|
||||
Boost::core
|
||||
Boost::detail
|
||||
Boost::move
|
||||
Boost::mp11
|
||||
Boost::predef
|
||||
Boost::preprocessor
|
||||
Boost::smart_ptr
|
||||
Boost::throw_exception
|
||||
Boost::tuple
|
||||
Boost::type_traits
|
||||
@ -35,3 +35,27 @@ if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
|
||||
|
||||
endif()
|
||||
|
||||
else()
|
||||
|
||||
FILE(GLOB_RECURSE headers include/*.h include/*.hpp)
|
||||
|
||||
idf_component_register(
|
||||
SRCS
|
||||
${headers}
|
||||
INCLUDE_DIRS
|
||||
include
|
||||
REQUIRES
|
||||
boost_assert
|
||||
boost_config
|
||||
boost_container_hash
|
||||
boost_core
|
||||
boost_move
|
||||
boost_mp11
|
||||
boost_predef
|
||||
boost_preprocessor
|
||||
boost_throw_exception
|
||||
boost_tuple
|
||||
boost_type_traits
|
||||
)
|
||||
|
||||
endif()
|
||||
|
23
LICENSE
Normal file
@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
49
README.md
Normal file
@ -0,0 +1,49 @@
|
||||
# Boost.Unordered
|
||||
|
||||
Part of collection of the [Boost C++ Libraries](http://github.com/boostorg).
|
||||
|
||||
For accessing data based on key lookup, the C++ standard library offers `std::set`, `std::map`, `std::multiset` and `std::multimap`.
|
||||
These are generally implemented using balanced binary trees so that lookup time has logarithmic complexity.
|
||||
That is generally okay, but in many cases a hash table can perform better, as accessing data has constant complexity, on average.
|
||||
The worst case complexity is linear, but that occurs rarely and with some care, can be avoided.
|
||||
|
||||
Also, the existing containers require a 'less than' comparison object to order their elements.
|
||||
For some data types this is impossible to implement or isn’t practical.
|
||||
In contrast, a hash table only needs an equality function and a hash function for the key.
|
||||
|
||||
With this in mind, unordered associative containers were added to the C++ standard.
|
||||
This is an implementation of the containers described in C++11, with some deviations from the standard in order to work with non-C++11 compilers and libraries.
|
||||
|
||||
|
||||
### License
|
||||
|
||||
Distributed under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt).
|
||||
|
||||
### Properties
|
||||
|
||||
* C++03
|
||||
* Header-Only
|
||||
|
||||
### Build Status
|
||||
|
||||
Branch | GH Actions | Appveyor | codecov.io | Deps | Docs | Tests |
|
||||
:-------------: | ---------- | -------- | ---------- | ---- | ---- | ----- |
|
||||
[`master`](https://github.com/boostorg/unordered/tree/master) | [](https://github.com/boostorg/unordered/actions/workflows/ci.yml) | [](https://ci.appveyor.com/project/danieljames/unordered-qtwe6/branch/master) | [](https://codecov.io/gh/boostorg/unordered/branch/master) | [](https://pdimov.github.io/boostdep-report/master/unordered.html) | [](https://www.boost.org/doc/libs/master/libs/unordered/doc/html/unordered.html) | [](http://www.boost.org/development/tests/master/developer/unordered.html)
|
||||
[`develop`](https://github.com/boostorg/unordered/tree/develop) | [](https://github.com/boostorg/unordered/actions/workflows/ci.yml) | [](https://ci.appveyor.com/project/danieljames/unordered-qtwe6/branch/develop) | [](https://codecov.io/gh/boostorg/unordered/branch/develop) | [](https://pdimov.github.io/boostdep-report/develop/unordered.html) | [](https://www.boost.org/doc/libs/develop/libs/unordered/doc/html/unordered.html) | [](http://www.boost.org/development/tests/develop/developer/unordered.html)
|
||||
|
||||
### Directories
|
||||
|
||||
| Name | Purpose |
|
||||
| ----------- | ------------------------------ |
|
||||
| `doc` | documentation |
|
||||
| `example` | examples |
|
||||
| `include` | headers |
|
||||
| `test` | unit tests |
|
||||
|
||||
### More information
|
||||
|
||||
* [Ask questions](http://stackoverflow.com/questions/ask?tags=c%2B%2B,boost,boost-unordered)
|
||||
* [Report bugs](https://github.com/boostorg/unordered/issues): Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well.
|
||||
* Submit your patches as pull requests against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt).
|
||||
* Discussions about the library are held on the [Boost developers mailing list](http://www.boost.org/community/groups.html#main). Be sure to read the [discussion policy](http://www.boost.org/community/policy.html) before posting and add the `[unordered]` tag at the beginning of the subject line.
|
||||
|
@ -14,6 +14,12 @@
|
||||
# include "absl/container/node_hash_map.h"
|
||||
# include "absl/container/flat_hash_map.h"
|
||||
#endif
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
# include "tsl/hopscotch_map.h"
|
||||
#endif
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
# include "tsl/robin_map.h"
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@ -137,7 +143,14 @@ template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::s
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
it = map.erase( it );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -159,13 +172,9 @@ template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::stead
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
@ -295,6 +304,26 @@ template<class K, class V> using absl_flat_hash_map =
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_map =
|
||||
tsl::hopscotch_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_pg_map =
|
||||
tsl::hopscotch_pg_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
template<class K, class V> using tsl_robin_map =
|
||||
tsl::robin_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_robin_pg_map =
|
||||
tsl::robin_pg_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
// fnv1a_hash
|
||||
|
||||
template<int Bits> struct fnv1a_hash_impl;
|
||||
@ -363,6 +392,26 @@ template<class K, class V> using absl_flat_hash_map_fnv1a =
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_map_fnv1a =
|
||||
tsl::hopscotch_map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_pg_map_fnv1a =
|
||||
tsl::hopscotch_pg_map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
template<class K, class V> using tsl_robin_map_fnv1a =
|
||||
tsl::robin_map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_robin_pg_map_fnv1a =
|
||||
tsl::robin_pg_map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
int main()
|
||||
@ -382,6 +431,20 @@ int main()
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
test<tsl_hopscotch_map>( "tsl::hopscotch_map" );
|
||||
test<tsl_hopscotch_pg_map>( "tsl::hopscotch_pg_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
test<tsl_robin_map>( "tsl::robin_map" );
|
||||
test<tsl_robin_pg_map>( "tsl::robin_pg_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
@ -393,13 +456,27 @@ int main()
|
||||
test<absl_node_hash_map_fnv1a>( "absl::node_hash_map, FNV-1a" );
|
||||
test<absl_flat_hash_map_fnv1a>( "absl::flat_hash_map, FNV-1a" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
test<tsl_hopscotch_map_fnv1a>( "tsl::hopscotch_map, FNV-1a" );
|
||||
test<tsl_hopscotch_pg_map_fnv1a>( "tsl::hopscotch_pg_map, FNV-1a" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
test<tsl_robin_map_fnv1a>( "tsl::robin_map, FNV-1a" );
|
||||
test<tsl_robin_pg_map_fnv1a>( "tsl::robin_pg_map, FNV-1a" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 30 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
std::cout << std::setw( 31 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,12 @@
|
||||
# include "absl/container/node_hash_map.h"
|
||||
# include "absl/container/flat_hash_map.h"
|
||||
#endif
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
# include "tsl/hopscotch_map.h"
|
||||
#endif
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
# include "tsl/robin_map.h"
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@ -138,7 +144,14 @@ template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::s
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
it = map.erase( it );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -160,13 +173,9 @@ template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::stead
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
@ -296,6 +305,26 @@ template<class K, class V> using absl_flat_hash_map =
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_map =
|
||||
tsl::hopscotch_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_pg_map =
|
||||
tsl::hopscotch_pg_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
template<class K, class V> using tsl_robin_map =
|
||||
tsl::robin_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_robin_pg_map =
|
||||
tsl::robin_pg_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
// fnv1a_hash
|
||||
|
||||
template<int Bits> struct fnv1a_hash_impl;
|
||||
@ -364,6 +393,26 @@ template<class K, class V> using absl_flat_hash_map_fnv1a =
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_map_fnv1a =
|
||||
tsl::hopscotch_map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_pg_map_fnv1a =
|
||||
tsl::hopscotch_pg_map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
template<class K, class V> using tsl_robin_map_fnv1a =
|
||||
tsl::robin_map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_robin_pg_map_fnv1a =
|
||||
tsl::robin_pg_map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
int main()
|
||||
@ -383,6 +432,20 @@ int main()
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
test<tsl_hopscotch_map>( "tsl::hopscotch_map" );
|
||||
test<tsl_hopscotch_pg_map>( "tsl::hopscotch_pg_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
test<tsl_robin_map>( "tsl::robin_map" );
|
||||
test<tsl_robin_pg_map>( "tsl::robin_pg_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
@ -394,13 +457,27 @@ int main()
|
||||
test<absl_node_hash_map_fnv1a>( "absl::node_hash_map, FNV-1a" );
|
||||
test<absl_flat_hash_map_fnv1a>( "absl::flat_hash_map, FNV-1a" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
test<tsl_hopscotch_map_fnv1a>( "tsl::hopscotch_map, FNV-1a" );
|
||||
test<tsl_hopscotch_pg_map_fnv1a>( "tsl::hopscotch_pg_map, FNV-1a" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
test<tsl_robin_map_fnv1a>( "tsl::robin_map, FNV-1a" );
|
||||
test<tsl_robin_pg_map_fnv1a>( "tsl::robin_pg_map, FNV-1a" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 30 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
std::cout << std::setw( 31 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,19 @@
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/node_hash_map.h"
|
||||
# include "absl/container/flat_hash_map.h"
|
||||
#endif
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
# include "tsl/hopscotch_map.h"
|
||||
#endif
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
# include "tsl/robin_map.h"
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@ -62,7 +69,7 @@ static void init_indices()
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices3.push_back( (std::uint32_t)i << 11 );
|
||||
indices3.push_back( boost::endian::endian_reverse( static_cast<std::uint32_t>( i ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +94,7 @@ template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::stea
|
||||
map.insert( { indices3[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted insert", 0, map.size() );
|
||||
print_time( t1, "Consecutive reversed insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@ -133,7 +140,7 @@ template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::stea
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted lookup", s, map.size() );
|
||||
print_time( t1, "Consecutive reversed lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@ -146,7 +153,14 @@ template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::s
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
it = map.erase( it );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -168,13 +182,9 @@ template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::stead
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
@ -184,7 +194,7 @@ template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::stead
|
||||
map.erase( indices3[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted erase", 0, map.size() );
|
||||
print_time( t1, "Consecutive reversed erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@ -311,6 +321,26 @@ template<class K, class V> using absl_flat_hash_map =
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_map =
|
||||
tsl::hopscotch_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_pg_map =
|
||||
tsl::hopscotch_pg_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
template<class K, class V> using tsl_robin_map =
|
||||
tsl::robin_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_robin_pg_map =
|
||||
tsl::robin_pg_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
@ -324,6 +354,20 @@ int main()
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
test<tsl_hopscotch_map>( "tsl::hopscotch_map" );
|
||||
test<tsl_hopscotch_pg_map>( "tsl::hopscotch_pg_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
test<tsl_robin_map>( "tsl::robin_map" );
|
||||
test<tsl_robin_pg_map>( "tsl::robin_pg_map" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
@ -8,12 +8,19 @@
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/node_hash_map.h"
|
||||
# include "absl/container/flat_hash_map.h"
|
||||
#endif
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
# include "tsl/hopscotch_map.h"
|
||||
#endif
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
# include "tsl/robin_map.h"
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@ -62,7 +69,7 @@ static void init_indices()
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices3.push_back( (std::uint64_t)i << 40 );
|
||||
indices3.push_back( boost::endian::endian_reverse( static_cast<std::uint64_t>( i ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +94,7 @@ template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::stea
|
||||
map.insert( { indices3[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted insert", 0, map.size() );
|
||||
print_time( t1, "Consecutive reversed insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@ -133,7 +140,7 @@ template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::stea
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted lookup", s, map.size() );
|
||||
print_time( t1, "Consecutive reversed lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@ -146,7 +153,14 @@ template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::s
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
it = map.erase( it );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -168,13 +182,9 @@ template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::stead
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
@ -184,7 +194,7 @@ template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::stead
|
||||
map.erase( indices3[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted erase", 0, map.size() );
|
||||
print_time( t1, "Consecutive reversed erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@ -311,6 +321,26 @@ template<class K, class V> using absl_flat_hash_map =
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_map =
|
||||
tsl::hopscotch_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_hopscotch_pg_map =
|
||||
tsl::hopscotch_pg_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
template<class K, class V> using tsl_robin_map =
|
||||
tsl::robin_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
template<class K, class V> using tsl_robin_pg_map =
|
||||
tsl::robin_pg_map<K, V, std::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
|
||||
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
@ -324,6 +354,20 @@ int main()
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_HOPSCOTCH
|
||||
|
||||
test<tsl_hopscotch_map>( "tsl::hopscotch_map" );
|
||||
test<tsl_hopscotch_pg_map>( "tsl::hopscotch_pg_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TSL_ROBIN
|
||||
|
||||
test<tsl_robin_map>( "tsl::robin_map" );
|
||||
test<tsl_robin_pg_map>( "tsl::robin_pg_map" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import urllib, os, os.path, sys, json, tarfile, zipfile, tempfile
|
||||
|
||||
def download(snapshot):
|
||||
if snapshot == 'stable':
|
||||
# TODO: Default version/filename if not available?
|
||||
downloads = [
|
||||
"https://sourceforge.net/projects/boost/files/boost/%s/%s.tar.bz2/download" %
|
||||
(os.environ['BOOST_VERSION'], os.environ['BOOST_FILENAME'])]
|
||||
else:
|
||||
json_response = urllib.urlopen('https://api.bintray.com/packages/boostorg/%s/snapshot/files' % (snapshot))
|
||||
x = json.load(json_response)
|
||||
|
||||
extension_priorities = { '.bz2': 2, '.gz': 1, '.zip': 0 }
|
||||
file_list = []
|
||||
version_dates = {}
|
||||
for file in x:
|
||||
file_extension = os.path.splitext(file['path'])[1]
|
||||
if (file_extension in extension_priorities):
|
||||
file['priority'] = extension_priorities[file_extension]
|
||||
file_list.append(file)
|
||||
if not file['version'] in version_dates or file['created'] < version_dates[file['version']]:
|
||||
version_dates[file['version']] = file['created']
|
||||
file_list.sort(key=lambda x: (version_dates[x['version']], x['priority']), reverse=True)
|
||||
downloads = ['http://dl.bintray.com/boostorg/%s/%s' % (snapshot, file['path']) for file in file_list]
|
||||
|
||||
filename = ''
|
||||
for download_url in downloads:
|
||||
try:
|
||||
print "Downloading: " + download_url
|
||||
(filename, headers) = urllib.urlretrieve(download_url)
|
||||
|
||||
print "Extracting: " + filename
|
||||
dir = tempfile.mkdtemp()
|
||||
extract(filename, dir)
|
||||
os.remove(filename)
|
||||
files = os.listdir(dir)
|
||||
assert(len(files) == 1)
|
||||
os.rename(os.path.join(dir, files[0]), 'boost')
|
||||
return
|
||||
except IOError:
|
||||
print "Error opening URL: " + download_url
|
||||
|
||||
def extract(filename, path = '.'):
|
||||
if (filename.endswith(".gz")):
|
||||
tar = tarfile.open(filename, "r:gz")
|
||||
tar.extractall(path)
|
||||
tar.close
|
||||
elif (filename.endswith(".bz2")):
|
||||
tar = tarfile.open(filename, "r:bz2")
|
||||
tar.extractall(path)
|
||||
tar.close
|
||||
elif (filename.endswith(".zip")):
|
||||
zip = zipfile.ZipFile(filename, "r")
|
||||
zip.extractall(path)
|
||||
zip.close
|
||||
else:
|
||||
assert False
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
download('stable')
|
||||
elif len(sys.argv) == 2:
|
||||
download(sys.argv[1])
|
||||
else:
|
||||
print "Usage: %s [stable|branch-name]" % (sys.argv[0])
|
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 32 KiB |
BIN
doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice.png
Normal file
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 33 KiB |
BIN
doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice.png
Normal file
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 38 KiB |
BIN
doc/diagrams/benchmarks/vs/running insertion.xlsx.practice.png
Normal file
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 34 KiB |
BIN
doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice.png
Normal file
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 42 KiB |
BIN
doc/diagrams/bucket-groups.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
doc/diagrams/fca.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
doc/diagrams/singly-linked.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
47
doc/preview.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Building Boost from the Tip of Develop
|
||||
|
||||
To build Boost from the tip of the develop branch without cloning the entire
|
||||
history, use the command below:
|
||||
|
||||
Linux:
|
||||
```bash
|
||||
cwd=$(pwd) \
|
||||
&& cd ~ \
|
||||
&& git clone -b develop --depth 1 --recurse-submodules --shallow-submodules --jobs 8 https://github.com/boostorg/boost.git boost-develop-beta \
|
||||
&& cd boost-develop-beta \
|
||||
&& ./bootstrap.sh \
|
||||
&& ./b2 install --prefix=boost-install cxxstd=17 \
|
||||
&& echo "Boost successfully installed into $(realpath boost-install)!
|
||||
Add this to your CMake toolchain file.
|
||||
list(APPEND CMAKE_PREFIX_PATH $(realpath boost-install))
|
||||
" \
|
||||
&& cd $cwd
|
||||
```
|
||||
|
||||
Windows (using plain Command Prompt):
|
||||
```bat
|
||||
cmd /v
|
||||
set cwd=%cd% ^
|
||||
&& cd %homepath% ^
|
||||
&& git clone -b develop --depth 1 --recurse-submodules --shallow-submodules --jobs 8 https://github.com/boostorg/boost.git boost-develop-beta ^
|
||||
&& cd boost-develop-beta ^
|
||||
&& bootstrap.bat ^
|
||||
&& b2 install --prefix=boost-install cxxstd=17 address-model=64 ^
|
||||
&& echo Boost successfully installed into !cd!\boost-install! ^
|
||||
&& echo Add this to your CMake toolchain file. ^
|
||||
&& echo list(APPEND CMAKE_PREFIX_PATH !cd:\=/!/boost-install) ^
|
||||
&& cd !cwd! ^
|
||||
&& exit
|
||||
```
|
||||
|
||||
Note: you can build Boost with a specific compiler by setting the toolset in
|
||||
the `./b2` command above. To build with clang, specify `toolset=clang`; to build
|
||||
with a specific version of gcc, clang or msvc, specify e.g. `toolset=gcc-12` for GCC
|
||||
12, `clang-14` for Clang 14, `msvc-14.3` for MSVC 14.3. The value of `cxxstd`
|
||||
can also be set to other values such as 11 for C++11, 14 for C++14, etc.
|
||||
|
||||
For more info on what's possible, check out this link on b2:
|
||||
https://www.boost.org/doc/libs/develop/tools/build/doc/html/index.html#bbv2.overview.builtins.features
|
||||
|
||||
This should hopefully cover the two most common cases of building a dependency,
|
||||
setting the compiler and C++ standard used.
|
188
doc/roadmap.md
Normal file
@ -0,0 +1,188 @@
|
||||
# Refactoring Roadmap
|
||||
|
||||
[Proof of concept](https://github.com/joaquintides/fca_unordered) implementation for a fast closed-addressing implementation.
|
||||
|
||||
## Plan of Refactoring
|
||||
|
||||
* remove `ptr_node` and `ptr_bucket`
|
||||
* see if the code can survive a lack of the `extra_node` or maybe we hard-code it in
|
||||
* implement bucket groups as they are in `fca` but don't use them directly yet, add alongside the `buckets_` data member in `struct table`
|
||||
* try to remove `bucket_info_` from the node structure (breaks all call-sites that use `get_bucket()` and dependents)
|
||||
* make sure `fca` can successfully handle multi-variants at this stage + supports mutable iterators for `map`/`multimap`
|
||||
* do a hard-break:
|
||||
* update code to no longer use one single linked list across all buckets (each bucket contains its own unique list)
|
||||
* integrate the `bucket_group<Node>` structure into the `table` (update iterator call-sites to include `bucket_iterator`s)
|
||||
|
||||
Blockers:
|
||||
* how to handle `multi` variants with new `fca` prototype
|
||||
|
||||
## Implementation Differences
|
||||
|
||||
### Unordered
|
||||
|
||||
### Node Type
|
||||
|
||||
Bullet Points:
|
||||
* reify node type into a single one
|
||||
* come up with implementation for multi- variants
|
||||
* code that touches `get_bucket()` and `*_in_group()` member functions may need updating
|
||||
|
||||
There are two node types in Unordered, `struct node` and `struct ptr_node`, and the node type is selected conditionally based on the Allocator's pointer type:
|
||||
```c++
|
||||
template <typename A, typename T, typename NodePtr, typename BucketPtr>
|
||||
struct pick_node2
|
||||
{
|
||||
typedef boost::unordered::detail::node<A, T> node;
|
||||
// ...
|
||||
};
|
||||
|
||||
template <typename A, typename T>
|
||||
struct pick_node2<A, T, boost::unordered::detail::ptr_node<T>*,
|
||||
boost::unordered::detail::ptr_bucket*>
|
||||
{
|
||||
typedef boost::unordered::detail::ptr_node<T> node;
|
||||
// ...
|
||||
};
|
||||
|
||||
template <typename A, typename T> struct pick_node
|
||||
{
|
||||
typedef typename boost::remove_const<T>::type nonconst;
|
||||
|
||||
typedef boost::unordered::detail::allocator_traits<
|
||||
typename boost::unordered::detail::rebind_wrap<A,
|
||||
boost::unordered::detail::ptr_node<nonconst> >::type>
|
||||
tentative_node_traits;
|
||||
|
||||
typedef boost::unordered::detail::allocator_traits<
|
||||
typename boost::unordered::detail::rebind_wrap<A,
|
||||
boost::unordered::detail::ptr_bucket>::type>
|
||||
tentative_bucket_traits;
|
||||
|
||||
typedef pick_node2<A, nonconst, typename tentative_node_traits::pointer,
|
||||
typename tentative_bucket_traits::pointer>
|
||||
pick;
|
||||
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
};
|
||||
```
|
||||
|
||||
The node types are identical in terms of interface and the only difference is that `node` is chosen when the Allocator uses fancy pointers and `ptr_node` is chosen when the Allocator's pointer type is `T*`.
|
||||
|
||||
Nodes in Unorderd store `bucket_info_`:
|
||||
```cpp
|
||||
template <typename A, typename T>
|
||||
struct node : boost::unordered::detail::value_base<T>
|
||||
{
|
||||
link_pointer next_;
|
||||
std::size_t bucket_info_;
|
||||
node() : next_(), bucket_info_(0) {}
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
`bucket_info_` maps each node back to its corresponding bucket via the member function:
|
||||
```cpp
|
||||
std::size_t get_bucket() const
|
||||
{
|
||||
return bucket_info_ & ((std::size_t)-1 >> 1);
|
||||
}
|
||||
```
|
||||
|
||||
`bucket_info_` is also used to demarcate the start of equivalent nodes in the containers via:
|
||||
```cpp
|
||||
// Note that nodes start out as the first in their group, as `bucket_info_` defaults to 0.
|
||||
std::size_t is_first_in_group() const
|
||||
{ return !(bucket_info_ & ~((std::size_t)-1 >> 1)); }
|
||||
|
||||
void set_first_in_group()
|
||||
{ bucket_info_ = bucket_info_ & ((std::size_t)-1 >> 1); }
|
||||
|
||||
void reset_first_in_group()
|
||||
{ bucket_info_ = bucket_info_ | ~((std::size_t)-1 >> 1); }
|
||||
```
|
||||
|
||||
A goal of refactoring is to simply have one node type:
|
||||
```cpp
|
||||
template<class T>
|
||||
struct node {
|
||||
node *next;
|
||||
T value;
|
||||
};
|
||||
```
|
||||
that is used unconditionally. This also requires updating the code that touches the `bucket_info_` along with the code that that touches the `*_in_group()` member functions.
|
||||
|
||||
### Bucket Type
|
||||
|
||||
Bullet points:
|
||||
* reify bucket structure into a single one
|
||||
* figure out how to add `bucket_group`s to the table struct
|
||||
|
||||
Buckets are similar to nodes in that there are two variations: `template<class NodePointer> struct bucket` and `struct ptr_bucket`.
|
||||
|
||||
The buckets exist to contain a pointer to a node, however they contain an `enum { extra_node = true };` or `enum { extra_node = false }` to determine whether or not the code should explicitly allocate a default constructed node whose address assigned as the dummy node at the end of the bucket array.
|
||||
|
||||
`extra_node` is used in the creation and deletion of the bucket array but it is not inherently clear what its intended purpose is.
|
||||
|
||||
### Iterators
|
||||
|
||||
Iterators are currently templated on the type of Node they store. Because `fca` constructs iterators with two arguments, all the call-sites that instantiate iterators will need to be updated but this a straight-forward mechanical change.
|
||||
|
||||
Iterators are selected, as of now, via the `detail::map` and `detail::set` class templates.
|
||||
|
||||
For example, for `unordered_map`, `iterator` is defined as:
|
||||
```cpp
|
||||
typedef boost::unordered::detail::map<A, K, T, H, P> types;
|
||||
typedef typename types::table table;
|
||||
typedef typename table::iterator iterator;
|
||||
```
|
||||
|
||||
The iterator is a member typedef of the `table` which is `types::table`. Examining `types` (aka `detail::map<...>`), we see:
|
||||
```cpp
|
||||
template <typename A, typename K, typename M, typename H, typename P>
|
||||
struct map {
|
||||
// ...
|
||||
typedef boost::unordered::detail::table<types> table;
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Examining the `detail::table<types>` struct, we see:
|
||||
```cpp
|
||||
template <typename Types>
|
||||
struct table {
|
||||
// ...
|
||||
typedef typename Types::iterator iterator;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Collapsing all of this, we see that our iterator types are defined here:
|
||||
```cpp
|
||||
template <typename A, typename K, typename M, typename H, typename P>
|
||||
struct map
|
||||
{
|
||||
// ...
|
||||
typedef boost::unordered::detail::pick_node<A, value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
|
||||
typedef boost::unordered::iterator_detail::iterator<node> iterator;
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
|
||||
typedef boost::unordered::iterator_detail::l_iterator<node> l_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node>
|
||||
cl_iterator;
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
This is similarly designed for `detail::set`:
|
||||
```cpp
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> iterator;
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node> l_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node>
|
||||
cl_iterator;
|
||||
```
|
||||
|
||||
The only difference here is that `set::iterator` is always a `c_iterator`, a `const_iterator` type.
|
@ -12,6 +12,7 @@
|
||||
|
||||
include::unordered/intro.adoc[]
|
||||
include::unordered/buckets.adoc[]
|
||||
include::unordered/benchmarks.adoc[]
|
||||
include::unordered/hash_equality.adoc[]
|
||||
include::unordered/comparison.adoc[]
|
||||
include::unordered/compliance.adoc[]
|
||||
|
277
doc/unordered/benchmarks.adoc
Normal file
@ -0,0 +1,277 @@
|
||||
[#benchmarks]
|
||||
:idprefix: benchmarks_
|
||||
:imagesdir: ../diagrams
|
||||
|
||||
= Benchmarks
|
||||
|
||||
All benchmarks were created using `unordered_set<unsigned int>` (non-duplicate) and `unordered_multiset<unsigned int>` (duplicate). The source code can be https://github.com/joaquintides/boost_unordered_benchmark[found here].
|
||||
|
||||
The insertion benchmarks insert `n` random values, where `n` is between 10,000 and 3 million. For the duplicated benchmarks, the same random values are repeated an average of 5 times.
|
||||
|
||||
The erasure benchmarks erase all `n` elements randomly until the container is empty.
|
||||
|
||||
The successful lookup benchmarks are done by looking up all `n` values, in the their original insertion order.
|
||||
|
||||
The unsuccessful lookup benchmarks use `n` randomly generated integers but using a different seed value.
|
||||
|
||||
== GCC 11 + libstdc++-v3
|
||||
|
||||
=== Insertion
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/gcc/running insertion.xlsx.practice.png[width=250,link=../diagrams/benchmarks/gcc/running insertion.xlsx.practice.png,window=_blank]
|
||||
|image::benchmarks/gcc/running%20insertion.xlsx.practice non-unique.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice non-unique.png,window=_blank]
|
||||
|image::benchmarks/gcc/running%20insertion.xlsx.practice non-unique 5.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice non-unique 5.png,window=_blank]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements +
|
||||
max load factor 5
|
||||
|===
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/gcc/running%20insertion.xlsx.practice norehash.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice norehash.png,window=_blank]
|
||||
|image::benchmarks/gcc/running%20insertion.xlsx.practice norehash non-unique.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice norehash non-unique.png,window=_blank]
|
||||
|image::benchmarks/gcc/running%20insertion.xlsx.practice norehash non-unique 5.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice norehash non-unique 5.png,window=_blank]
|
||||
|
||||
h|non-duplicate elements, +
|
||||
prior `reserve`
|
||||
h|duplicate elements, +
|
||||
prior `reserve`
|
||||
h|duplicate elements, +
|
||||
max load factor 5, +
|
||||
prior `reserve`
|
||||
|
||||
|===
|
||||
|
||||
=== Erasure
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/gcc/scattered%20erasure.xlsx.practice.png[width=250,link=../diagrams/benchmarks/gcc/scattered%20erasure.xlsx.practice.png,window=_blank]
|
||||
|image::benchmarks/gcc/scattered%20erasure.xlsx.practice non-unique.png[width=250,link=../diagrams/benchmarks/gcc/scattered%20erasure.xlsx.practice non-unique.png,window=_blank]
|
||||
|image::benchmarks/gcc/scattered%20erasure.xlsx.practice non-unique 5.png[width=250,link=../diagrams/benchmarks/gcc/scattered%20erasure.xlsx.practice non-unique 5.png,window=_blank]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements +
|
||||
max load factor 5
|
||||
|===
|
||||
|
||||
=== Successful Lookup
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice.png]
|
||||
|image::benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
=== Unsuccessful lookup
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice.png]
|
||||
|image::benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
== Clang 12 + libc++
|
||||
|
||||
=== Insertion
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice.png[width=250, window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice.png]
|
||||
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice non-unique.png[width=250, window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice non-unique 5.png[width=250, window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash.png]
|
||||
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash non-unique.png]
|
||||
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements, +
|
||||
prior `reserve`
|
||||
h|duplicate elements, +
|
||||
prior `reserve`
|
||||
h|duplicate elements, +
|
||||
max load factor 5, +
|
||||
prior `reserve`
|
||||
|
||||
|===
|
||||
|
||||
=== Erasure
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice.png]
|
||||
|image::benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
=== Successful lookup
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice.png]
|
||||
|image::benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
=== Unsuccessful lookup
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice.png]
|
||||
|image::benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
== Visual Studio 2019 + Dinkumware
|
||||
|
||||
=== Insertion
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/vs/running%20insertion.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice.png]
|
||||
|image::benchmarks/vs/running%20insertion.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/vs/running%20insertion.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/vs/running%20insertion.xlsx.practice norehash.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice norehash.png]
|
||||
|image::benchmarks/vs/running%20insertion.xlsx.practice norehash non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice norehash non-unique.png]
|
||||
|image::benchmarks/vs/running%20insertion.xlsx.practice norehash non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice norehash non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements, +
|
||||
prior `reserve`
|
||||
h|duplicate elements, +
|
||||
prior `reserve`
|
||||
h|duplicate elements, +
|
||||
max load factor 5, +
|
||||
prior `reserve`
|
||||
|
||||
|===
|
||||
|
||||
=== Erasure
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/vs/scattered%20erasure.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20erasure.xlsx.practice.png]
|
||||
|image::benchmarks/vs/scattered%20erasure.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20erasure.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/vs/scattered%20erasure.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20erasure.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
=== Successful lookup
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/vs/scattered%20successful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20successful%20looukp.xlsx.practice.png]
|
||||
|image::benchmarks/vs/scattered%20successful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20successful%20looukp.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/vs/scattered%20successful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20successful%20looukp.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
=== Unsuccessful lookup
|
||||
|
||||
[caption=]
|
||||
[cols="3*^.^a", frame=all, grid=all]
|
||||
|===
|
||||
|
||||
|image::benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice.png]
|
||||
|image::benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png]
|
||||
|image::benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png]
|
||||
|
||||
h|non-duplicate elements
|
||||
h|duplicate elements
|
||||
h|duplicate elements, +
|
||||
max load factor 5
|
||||
|
||||
|===
|
@ -1,5 +1,6 @@
|
||||
[#buckets]
|
||||
:idprefix: buckets_
|
||||
:imagesdir: ../diagrams
|
||||
|
||||
= The Data Structure
|
||||
|
||||
@ -8,7 +9,7 @@ any number of elements. For example, the following diagram shows an <<unordered_
|
||||
`B`, `C`, `D` and `E` (this is just for illustration, containers will typically
|
||||
have more buckets).
|
||||
|
||||
image::../diagrams/buckets.png[]
|
||||
image::buckets.png[]
|
||||
|
||||
In order to decide which bucket to place an element in, the container applies
|
||||
the hash function, `Hash`, to the element's key (for `unordered_set` and
|
||||
@ -150,3 +151,60 @@ x.rehash(std::ceil(n / x.max_load_factor()))
|
||||
+
|
||||
See the <<unordered_map_rehash,reference for more details>> on the `rehash` function.
|
||||
|
||||
== Fast Closed Addressing Implementation
|
||||
|
||||
++++
|
||||
<style>
|
||||
.imageblock > .title {
|
||||
text-align: inherit;
|
||||
}
|
||||
</style>
|
||||
++++
|
||||
|
||||
Boost.Unordered sports one of the fastest implementations of closed addressing, also commonly known as https://en.wikipedia.org/wiki/Hash_table#Separate_chaining[separate chaining]. An example figure representing the data structure is below:
|
||||
|
||||
[#img-bucket-groups,.text-center]
|
||||
.A simple bucket group approach
|
||||
image::bucket-groups.png[align=center]
|
||||
|
||||
An array of "buckets" is allocated and each bucket in turn points to its own individual linked list. This makes meeting the standard requirements of bucket iteration straight-forward. Unfortunately, iteration of the entire container is often times slow using this layout as each bucket must be examined for occupancy, yielding a time complexity of `O(bucket_count() + size())` when the standard requires complexity to be `O(size())`.
|
||||
|
||||
Canonical standard implementations will wind up looking like the diagram below:
|
||||
|
||||
[.text-center]
|
||||
.The canonical standard approach
|
||||
image::singly-linked.png[align=center,link=../diagrams/singly-linked.png,window=_blank]
|
||||
|
||||
It's worth noting that this approach is only used by pass:[libc++] and pass:[libstdc++]; the MSVC Dinkumware implementation uses a different one. A more detailed analysis of the standard containers can be found http://bannalia.blogspot.com/2013/10/implementation-of-c-unordered.html[here].
|
||||
|
||||
This unusually laid out data structure is chosen to make iteration of the entire container efficient by inter-connecting all of the nodes into a singly-linked list. One might also notice that buckets point to the node _before_ the start of the bucket's elements. This is done so that removing elements from the list can be done efficiently without introducing the need for a doubly-linked list. Unfortunately, this data structure introduces a guaranteed extra indirection. For example, to access the first element of a bucket, something like this must be done:
|
||||
|
||||
```c++
|
||||
auto const idx = get_bucket_idx(hash_function(key));
|
||||
node* p = buckets[idx]; // first load
|
||||
node* n = p->next; // second load
|
||||
if (n && is_in_bucket(n, idx)) {
|
||||
value_type const& v = *n; // third load
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
With a simple bucket group layout, this is all that must be done:
|
||||
```c++
|
||||
auto const idx = get_bucket_idx(hash_function(key));
|
||||
node* n = buckets[idx]; // first load
|
||||
if (n) {
|
||||
value_type const& v = *n; // second load
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In practice, the extra indirection can have a dramatic performance impact to common operations such as `insert`, `find` and `erase`. But to keep iteration of the container fast, Boost.Unordered introduces a novel data structure, a "bucket group". A bucket group is a fixed-width view of a subsection of the buckets array. It contains a bitmask (a `std::size_t`) which it uses to track occupancy of buckets and contains two pointers so that it can form a doubly-linked list with non-empty groups. An example diagram is below:
|
||||
|
||||
[#img-fca-layout]
|
||||
.The new layout used by Boost
|
||||
image::fca.png[align=center]
|
||||
|
||||
Thus container-wide iteration is turned into traversing the non-empty bucket groups (an operation with constant time complexity) which reduces the time complexity back to `O(size())`. In total, a bucket group is only 4 words in size and it views `sizeof(std::size_t) * CHAR_BIT` buckets meaning that for all common implementations, there's only 4 bits of space overhead per bucket introduced by the bucket groups.
|
||||
|
||||
For more information on implementation rationale, read the <<Implementation Rationale, corresponding section>>.
|
||||
|
@ -6,6 +6,13 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.80.0 - Major update
|
||||
|
||||
* Refactor internal implementation to be dramatically faster
|
||||
* Allow `final` Hasher and KeyEqual objects
|
||||
* Update documentation, adding benchmark graphs and notes on the new internal
|
||||
data structures
|
||||
|
||||
== Release 1.79.0
|
||||
|
||||
* Improved {cpp}20 support:
|
||||
|
@ -9,4 +9,6 @@ Copyright (C) 2003, 2004 Jeremy B. Maitin-Shepard
|
||||
|
||||
Copyright (C) 2005-2008 Daniel James
|
||||
|
||||
Copyright (C) 2022 Christian Mazakas
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
@ -66,3 +66,7 @@ by using `(h * m) >> (w - k)`, where `h` is the hash value, `m` is the golden
|
||||
ratio multiplied by `2^w`, `w` is the word size (32 or 64), and `2^k` is the
|
||||
number of buckets. This provides a good compromise between speed and
|
||||
distribution.
|
||||
|
||||
Since release 1.80.0, prime numbers are chosen for the number of buckets in
|
||||
tandem with sophisticated modulo arithmetic. This removes the need for "mixing"
|
||||
the result of the user's hash function as was used for release 1.79.0.
|
||||
|
819
include/boost/unordered/detail/fca.hpp
Normal file
@ -0,0 +1,819 @@
|
||||
// Copyright (C) 2022 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FCA_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FCA_HPP
|
||||
|
||||
/*
|
||||
|
||||
The general structure of the fast closed addressing implementation is that we
|
||||
use straight-forward separate chaining (i.e. each bucket contains its own linked
|
||||
list) and then improve iteration time by adding an array of "bucket groups".
|
||||
|
||||
A bucket group is a constant-width view into a subsection of the buckets array,
|
||||
containing a bitmask that indicates which one of the buckets in the subsection
|
||||
contains a list of nodes. This allows the code to test N buckets for occupancy
|
||||
in a single operation. Additional speed can be found by inter-linking occupied
|
||||
bucket groups with one another in a doubly-linked list. To this end, large
|
||||
swathes of the bucket groups array no longer need to be iterated and have their
|
||||
bitmasks examined for occupancy.
|
||||
|
||||
A bucket group iterator contains a pointer to a bucket group along with a
|
||||
pointer into the buckets array. The iterator's bucket pointer is guaranteed to
|
||||
point to a bucket within the bucket group's view of the array. To advance the
|
||||
iterator, we need to determine if we need to skip to the next bucket group or
|
||||
simply move to the next occupied bucket as denoted by the bitmask.
|
||||
|
||||
To accomplish this, we perform something roughly equivalent to this:
|
||||
```
|
||||
bucket_iterator itb = ...
|
||||
bucket_pointer p = itb.p
|
||||
bucket_group_pointer pbg = itb.pbg
|
||||
|
||||
offset = p - pbg->buckets
|
||||
// because we wish to see if the _next_ bit in the mask is occupied, we'll
|
||||
// generate a testing mask from the current offset + 1
|
||||
//
|
||||
testing_mask = reset_first_bits(offset + 1)
|
||||
n = ctz(pbg->bitmask & testing_mask)
|
||||
|
||||
if (n < N) {
|
||||
p = pbg->buckets + n
|
||||
} else {
|
||||
pbg = pbg->next
|
||||
p = pbg->buckets + ctz(pbg->bitmask)
|
||||
}
|
||||
```
|
||||
|
||||
`reset_first_bits` yields an unsigned integral with the first n bits set to 0
|
||||
and then by counting the number of trailing zeroes when AND'd against the bucket
|
||||
group's bitmask, we can derive the offset into the buckets array. When the
|
||||
calculated offset is equal to N, we know we've reached the end of a bucket group
|
||||
and we can advance to the next one.
|
||||
|
||||
This is a rough explanation for how iterator incrementation should work for a
|
||||
fixed width size of N as 3 for the bucket groups
|
||||
```
|
||||
N = 3
|
||||
p = buckets
|
||||
pbg->bitmask = 0b101
|
||||
pbg->buckets = buckets
|
||||
|
||||
offset = p - pbg->buckets // => 0
|
||||
testing_mask = reset_first_bits(offset + 1) // reset_first_bits(1) => 0b110
|
||||
|
||||
x = bitmask & testing_mask // => 0b101 & 0b110 => 0b100
|
||||
ctz(x) // ctz(0b100) => 2
|
||||
// 2 < 3
|
||||
=> p = pbg->buckets + 2
|
||||
|
||||
// increment again...
|
||||
offset = p - pbg->buckets // => 2
|
||||
testing_mask = reset_first_bits(offset + 1) // reset_first_bits(3) => 0b000
|
||||
|
||||
bitmask & testing_mask // 0b101 & 0b000 => 0b000
|
||||
ctz(0b000) => 3
|
||||
// 3 < 3 is false now
|
||||
pbg = pbg->next
|
||||
initial_offset = ctz(pbg->bitmask)
|
||||
p = pbg->buckets + initial_offset
|
||||
```
|
||||
|
||||
For `size_` number of buckets, there are `1 + (size_ / N)` bucket groups where
|
||||
`N` is the width of a bucket group, determined at compile-time.
|
||||
|
||||
We allocate space for `size_ + 1` buckets, using the last one as a dummy bucket
|
||||
which is kept permanently empty so it can act as a sentinel value in the
|
||||
implementation of `iterator end();`. We set the last bucket group to act as a
|
||||
sentinel.
|
||||
|
||||
```
|
||||
num_groups = size_ / N + 1
|
||||
groups = allocate(num_groups)
|
||||
pbg = groups + (num_groups - 1)
|
||||
|
||||
// not guaranteed to point to exactly N buckets
|
||||
pbg->buckets = buckets + N * (size_ / N)
|
||||
|
||||
// this marks the true end of the bucket array
|
||||
buckets pbg->bitmask = set_bit(size_ % N)
|
||||
|
||||
// links in on itself
|
||||
pbg->next = pbg->prev = pbg
|
||||
```
|
||||
|
||||
To this end, we can devise a safe iteration scheme while also creating a useful
|
||||
sentinel to use as the end iterator.
|
||||
|
||||
Otherwise, usage of the data structure is relatively straight-forward compared
|
||||
to normal separate chaining implementations.
|
||||
|
||||
*/
|
||||
|
||||
#include <boost/unordered/detail/prime_fmod.hpp>
|
||||
|
||||
#include <boost/core/addressof.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/bit.hpp>
|
||||
#include <boost/core/empty_value.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/move/core.hpp>
|
||||
#include <boost/move/utility_core.hpp>
|
||||
#include <boost/swap.hpp>
|
||||
#include <boost/type_traits/aligned_storage.hpp>
|
||||
#include <boost/type_traits/alignment_of.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
|
||||
template <class ValueType, class VoidPtr> struct node
|
||||
{
|
||||
typedef ValueType value_type;
|
||||
typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
|
||||
node>::type node_pointer;
|
||||
|
||||
node_pointer next;
|
||||
typename boost::aligned_storage<sizeof(value_type),
|
||||
boost::alignment_of<value_type>::value>::type buf;
|
||||
|
||||
node() BOOST_NOEXCEPT : next(), buf() {}
|
||||
|
||||
value_type* value_ptr() BOOST_NOEXCEPT
|
||||
{
|
||||
return reinterpret_cast<value_type*>(buf.address());
|
||||
}
|
||||
|
||||
value_type& value() BOOST_NOEXCEPT
|
||||
{
|
||||
return *reinterpret_cast<value_type*>(buf.address());
|
||||
}
|
||||
};
|
||||
|
||||
template <class Node, class VoidPtr> struct bucket
|
||||
{
|
||||
typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
|
||||
Node>::type node_pointer;
|
||||
|
||||
typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
|
||||
bucket>::type bucket_pointer;
|
||||
|
||||
node_pointer next;
|
||||
|
||||
bucket() BOOST_NOEXCEPT : next() {}
|
||||
};
|
||||
|
||||
template <class Bucket> struct bucket_group
|
||||
{
|
||||
typedef typename Bucket::bucket_pointer bucket_pointer;
|
||||
typedef
|
||||
typename boost::pointer_traits<bucket_pointer>::template rebind_to<
|
||||
bucket_group>::type bucket_group_pointer;
|
||||
|
||||
BOOST_STATIC_CONSTANT(std::size_t, N = sizeof(std::size_t) * CHAR_BIT);
|
||||
|
||||
bucket_pointer buckets;
|
||||
std::size_t bitmask;
|
||||
bucket_group_pointer next, prev;
|
||||
|
||||
bucket_group() BOOST_NOEXCEPT : buckets(), bitmask(0), next(), prev() {}
|
||||
~bucket_group() {}
|
||||
};
|
||||
|
||||
inline std::size_t set_bit(std::size_t n) { return std::size_t(1) << n; }
|
||||
|
||||
inline std::size_t reset_bit(std::size_t n)
|
||||
{
|
||||
return ~(std::size_t(1) << n);
|
||||
}
|
||||
|
||||
inline std::size_t reset_first_bits(std::size_t n) // n>0
|
||||
{
|
||||
return ~(~(std::size_t(0)) >> (sizeof(std::size_t) * 8 - n));
|
||||
}
|
||||
|
||||
template <class Bucket> struct grouped_bucket_iterator
|
||||
{
|
||||
public:
|
||||
typedef typename Bucket::bucket_pointer bucket_pointer;
|
||||
typedef
|
||||
typename boost::pointer_traits<bucket_pointer>::template rebind_to<
|
||||
bucket_group<Bucket> >::type bucket_group_pointer;
|
||||
|
||||
typedef Bucket value_type;
|
||||
typedef typename boost::pointer_traits<bucket_pointer>::difference_type
|
||||
difference_type;
|
||||
typedef Bucket& reference;
|
||||
typedef Bucket* pointer;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
private:
|
||||
bucket_pointer p;
|
||||
bucket_group_pointer pbg;
|
||||
|
||||
public:
|
||||
grouped_bucket_iterator() : p(), pbg() {}
|
||||
|
||||
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
|
||||
pointer operator->() const BOOST_NOEXCEPT
|
||||
{
|
||||
return boost::to_address(p);
|
||||
}
|
||||
|
||||
grouped_bucket_iterator& operator++() BOOST_NOEXCEPT
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
grouped_bucket_iterator operator++(int) BOOST_NOEXCEPT
|
||||
{
|
||||
grouped_bucket_iterator old = *this;
|
||||
increment();
|
||||
return old;
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return equal(other);
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return !equal(other);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename, typename, typename>
|
||||
friend class grouped_bucket_array;
|
||||
|
||||
BOOST_STATIC_CONSTANT(std::size_t, N = bucket_group<Bucket>::N);
|
||||
|
||||
grouped_bucket_iterator(bucket_pointer p_, bucket_group_pointer pbg_)
|
||||
: p(p_), pbg(pbg_)
|
||||
{
|
||||
}
|
||||
|
||||
Bucket& dereference() const BOOST_NOEXCEPT { return *p; }
|
||||
|
||||
bool equal(const grouped_bucket_iterator& x) const BOOST_NOEXCEPT
|
||||
{
|
||||
return p == x.p;
|
||||
}
|
||||
|
||||
void increment() BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t const offset = static_cast<std::size_t>(p - pbg->buckets);
|
||||
|
||||
std::size_t n = std::size_t(boost::core::countr_zero(
|
||||
pbg->bitmask & reset_first_bits(offset + 1)));
|
||||
|
||||
if (n < N) {
|
||||
p = pbg->buckets + static_cast<difference_type>(n);
|
||||
} else {
|
||||
pbg = pbg->next;
|
||||
|
||||
std::ptrdiff_t x = boost::core::countr_zero(pbg->bitmask);
|
||||
p = pbg->buckets + x;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Node> struct const_grouped_local_bucket_iterator;
|
||||
|
||||
template <class Node> struct grouped_local_bucket_iterator
|
||||
{
|
||||
typedef typename Node::node_pointer node_pointer;
|
||||
|
||||
public:
|
||||
typedef typename Node::value_type value_type;
|
||||
typedef value_type element_type;
|
||||
typedef value_type* pointer;
|
||||
typedef value_type& reference;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
grouped_local_bucket_iterator() : p() {}
|
||||
|
||||
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
|
||||
|
||||
pointer operator->() const BOOST_NOEXCEPT
|
||||
{
|
||||
return boost::to_address(p);
|
||||
}
|
||||
|
||||
grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
|
||||
{
|
||||
grouped_local_bucket_iterator old = *this;
|
||||
increment();
|
||||
return old;
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return equal(other);
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return !equal(other);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename, typename, typename>
|
||||
friend class grouped_bucket_array;
|
||||
|
||||
template <class> friend struct const_grouped_local_bucket_iterator;
|
||||
|
||||
grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
|
||||
|
||||
value_type& dereference() const BOOST_NOEXCEPT { return p->value(); }
|
||||
|
||||
bool equal(const grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
|
||||
{
|
||||
return p == x.p;
|
||||
}
|
||||
|
||||
void increment() BOOST_NOEXCEPT { p = p->next; }
|
||||
|
||||
node_pointer p;
|
||||
};
|
||||
|
||||
template <class Node> struct const_grouped_local_bucket_iterator
|
||||
{
|
||||
typedef typename Node::node_pointer node_pointer;
|
||||
|
||||
public:
|
||||
typedef typename Node::value_type const value_type;
|
||||
typedef value_type const element_type;
|
||||
typedef value_type const* pointer;
|
||||
typedef value_type const& reference;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
const_grouped_local_bucket_iterator() : p() {}
|
||||
const_grouped_local_bucket_iterator(
|
||||
grouped_local_bucket_iterator<Node> it)
|
||||
: p(it.p)
|
||||
{
|
||||
}
|
||||
|
||||
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
|
||||
|
||||
pointer operator->() const BOOST_NOEXCEPT
|
||||
{
|
||||
return boost::to_address(p);
|
||||
}
|
||||
|
||||
const_grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
|
||||
{
|
||||
const_grouped_local_bucket_iterator old = *this;
|
||||
increment();
|
||||
return old;
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return equal(other);
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
|
||||
{
|
||||
return !equal(other);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename, typename, typename>
|
||||
friend class grouped_bucket_array;
|
||||
|
||||
const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
|
||||
|
||||
value_type& dereference() const BOOST_NOEXCEPT { return p->value(); }
|
||||
|
||||
bool equal(
|
||||
const const_grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
|
||||
{
|
||||
return p == x.p;
|
||||
}
|
||||
|
||||
void increment() BOOST_NOEXCEPT { p = p->next; }
|
||||
|
||||
node_pointer p;
|
||||
};
|
||||
|
||||
template <class T> struct span
|
||||
{
|
||||
T* begin() const BOOST_NOEXCEPT { return data; }
|
||||
T* end() const BOOST_NOEXCEPT { return data + size; }
|
||||
|
||||
T* data;
|
||||
std::size_t size;
|
||||
|
||||
span(T* data_, std::size_t size_) : data(data_), size(size_) {}
|
||||
};
|
||||
|
||||
template <class Bucket, class Allocator, class SizePolicy>
|
||||
class grouped_bucket_array
|
||||
: boost::empty_value<typename boost::allocator_rebind<Allocator,
|
||||
node<typename boost::allocator_value_type<Allocator>::type,
|
||||
typename boost::allocator_void_pointer<Allocator>::type> >::
|
||||
type>
|
||||
{
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(grouped_bucket_array)
|
||||
|
||||
typedef typename boost::allocator_value_type<Allocator>::type
|
||||
allocator_value_type;
|
||||
typedef
|
||||
typename boost::allocator_void_pointer<Allocator>::type void_pointer;
|
||||
typedef typename boost::allocator_difference_type<Allocator>::type
|
||||
difference_type;
|
||||
|
||||
public:
|
||||
typedef typename boost::allocator_rebind<Allocator,
|
||||
node<allocator_value_type, void_pointer> >::type node_allocator_type;
|
||||
|
||||
typedef node<allocator_value_type, void_pointer> node_type;
|
||||
typedef typename boost::allocator_pointer<node_allocator_type>::type
|
||||
node_pointer;
|
||||
typedef SizePolicy size_policy;
|
||||
|
||||
private:
|
||||
typedef typename boost::allocator_rebind<Allocator, Bucket>::type
|
||||
bucket_allocator_type;
|
||||
typedef typename boost::allocator_pointer<bucket_allocator_type>::type
|
||||
bucket_pointer;
|
||||
typedef boost::pointer_traits<bucket_pointer> bucket_pointer_traits;
|
||||
|
||||
typedef bucket_group<Bucket> group;
|
||||
typedef typename boost::allocator_rebind<Allocator, group>::type
|
||||
group_allocator_type;
|
||||
typedef typename boost::allocator_pointer<group_allocator_type>::type
|
||||
group_pointer;
|
||||
typedef typename boost::pointer_traits<group_pointer>
|
||||
group_pointer_traits;
|
||||
|
||||
public:
|
||||
typedef Bucket value_type;
|
||||
typedef Bucket bucket_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef Allocator allocator_type;
|
||||
typedef grouped_bucket_iterator<Bucket> iterator;
|
||||
typedef grouped_local_bucket_iterator<node_type> local_iterator;
|
||||
typedef const_grouped_local_bucket_iterator<node_type>
|
||||
const_local_iterator;
|
||||
|
||||
private:
|
||||
std::size_t size_index_, size_;
|
||||
bucket_pointer buckets;
|
||||
group_pointer groups;
|
||||
|
||||
public:
|
||||
grouped_bucket_array(size_type n, const Allocator& al)
|
||||
: empty_value<node_allocator_type>(empty_init_t(), al),
|
||||
size_index_(size_policy::size_index(n)),
|
||||
size_(size_policy::size(size_index_)), buckets(), groups()
|
||||
{
|
||||
bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
|
||||
group_allocator_type group_alloc = this->get_group_allocator();
|
||||
|
||||
size_type const num_buckets = buckets_len();
|
||||
size_type const num_groups = groups_len();
|
||||
|
||||
buckets = boost::allocator_allocate(bucket_alloc, num_buckets);
|
||||
BOOST_TRY
|
||||
{
|
||||
groups = boost::allocator_allocate(group_alloc, num_groups);
|
||||
|
||||
bucket_type* pb = boost::to_address(buckets);
|
||||
for (size_type i = 0; i < num_buckets; ++i) {
|
||||
new (pb + i) bucket_type();
|
||||
}
|
||||
|
||||
group* pg = boost::to_address(groups);
|
||||
for (size_type i = 0; i < num_groups; ++i) {
|
||||
new (pg + i) group();
|
||||
}
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
|
||||
size_type const N = group::N;
|
||||
group_pointer pbg =
|
||||
groups + static_cast<difference_type>(num_groups - 1);
|
||||
|
||||
pbg->buckets =
|
||||
buckets + static_cast<difference_type>(N * (size_ / N));
|
||||
pbg->bitmask = set_bit(size_ % N);
|
||||
pbg->next = pbg->prev = pbg;
|
||||
}
|
||||
|
||||
~grouped_bucket_array() { this->deallocate(); }
|
||||
|
||||
grouped_bucket_array(
|
||||
BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
|
||||
: empty_value<node_allocator_type>(
|
||||
empty_init_t(), other.get_node_allocator()),
|
||||
size_index_(other.size_index_),
|
||||
size_(other.size_),
|
||||
buckets(other.buckets),
|
||||
groups(other.groups)
|
||||
{
|
||||
other.size_ = 0;
|
||||
other.size_index_ = 0;
|
||||
other.buckets = bucket_pointer();
|
||||
other.groups = group_pointer();
|
||||
}
|
||||
|
||||
grouped_bucket_array& operator=(
|
||||
BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
this->get_node_allocator() == other.get_node_allocator());
|
||||
|
||||
if (this == boost::addressof(other)) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
this->deallocate();
|
||||
size_index_ = other.size_index_;
|
||||
size_ = other.size_;
|
||||
|
||||
buckets = other.buckets;
|
||||
groups = other.groups;
|
||||
|
||||
other.size_index_ = 0;
|
||||
other.size_ = 0;
|
||||
other.buckets = bucket_pointer();
|
||||
other.groups = group_pointer();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void deallocate() BOOST_NOEXCEPT
|
||||
{
|
||||
if (buckets) {
|
||||
bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
|
||||
boost::allocator_deallocate(
|
||||
bucket_alloc, buckets, this->buckets_len());
|
||||
|
||||
buckets = bucket_pointer();
|
||||
}
|
||||
|
||||
if (groups) {
|
||||
group_allocator_type group_alloc = this->get_group_allocator();
|
||||
boost::allocator_deallocate(
|
||||
group_alloc, groups, this->groups_len());
|
||||
|
||||
groups = group_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
void swap(grouped_bucket_array& other)
|
||||
{
|
||||
std::swap(size_index_, other.size_index_);
|
||||
std::swap(size_, other.size_);
|
||||
std::swap(buckets, other.buckets);
|
||||
std::swap(groups, other.groups);
|
||||
|
||||
bool b = boost::allocator_propagate_on_container_swap<
|
||||
allocator_type>::type::value;
|
||||
if (b) {
|
||||
boost::swap(get_node_allocator(), other.get_node_allocator());
|
||||
}
|
||||
}
|
||||
|
||||
node_allocator_type const& get_node_allocator() const
|
||||
{
|
||||
return empty_value<node_allocator_type>::get();
|
||||
}
|
||||
|
||||
node_allocator_type& get_node_allocator()
|
||||
{
|
||||
return empty_value<node_allocator_type>::get();
|
||||
}
|
||||
|
||||
bucket_allocator_type get_bucket_allocator() const
|
||||
{
|
||||
return this->get_node_allocator();
|
||||
}
|
||||
|
||||
group_allocator_type get_group_allocator() const
|
||||
{
|
||||
return this->get_node_allocator();
|
||||
}
|
||||
|
||||
size_type buckets_len() const BOOST_NOEXCEPT { return size_ + 1; }
|
||||
|
||||
size_type groups_len() const BOOST_NOEXCEPT
|
||||
{
|
||||
return size_ / group::N + 1;
|
||||
}
|
||||
|
||||
void reset_allocator(Allocator const& allocator_)
|
||||
{
|
||||
this->get_node_allocator() = node_allocator_type(allocator_);
|
||||
}
|
||||
|
||||
size_type bucket_count() const { return size_; }
|
||||
|
||||
iterator begin() const { return ++at(size_); }
|
||||
|
||||
iterator end() const
|
||||
{
|
||||
// micro optimization: no need to return the bucket group
|
||||
// as end() is not incrementable
|
||||
iterator pbg;
|
||||
pbg.p =
|
||||
buckets + static_cast<difference_type>(this->buckets_len() - 1);
|
||||
return pbg;
|
||||
}
|
||||
|
||||
local_iterator begin(size_type n) const
|
||||
{
|
||||
return local_iterator(
|
||||
(buckets + static_cast<difference_type>(n))->next);
|
||||
}
|
||||
|
||||
local_iterator end(size_type) const { return local_iterator(); }
|
||||
|
||||
size_type capacity() const BOOST_NOEXCEPT { return size_; }
|
||||
|
||||
iterator at(size_type n) const
|
||||
{
|
||||
std::size_t const N = group::N;
|
||||
|
||||
iterator pbg(buckets + static_cast<difference_type>(n),
|
||||
groups + static_cast<difference_type>(n / N));
|
||||
|
||||
return pbg;
|
||||
}
|
||||
|
||||
span<Bucket> raw()
|
||||
{
|
||||
BOOST_ASSERT(size_ == 0 || size_ < this->buckets_len());
|
||||
return span<Bucket>(boost::to_address(buckets), size_);
|
||||
}
|
||||
|
||||
size_type position(std::size_t hash) const
|
||||
{
|
||||
return size_policy::position(hash, size_index_);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
this->deallocate();
|
||||
size_index_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
void append_bucket_group(iterator itb) BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t const N = group::N;
|
||||
|
||||
bool const is_empty_bucket = (!itb->next);
|
||||
if (is_empty_bucket) {
|
||||
bucket_pointer pb = itb.p;
|
||||
group_pointer pbg = itb.pbg;
|
||||
|
||||
std::size_t n =
|
||||
static_cast<std::size_t>(boost::to_address(pb) - &buckets[0]);
|
||||
|
||||
bool const is_empty_group = (!pbg->bitmask);
|
||||
if (is_empty_group) {
|
||||
size_type const num_groups = this->groups_len();
|
||||
group_pointer last_group =
|
||||
groups + static_cast<difference_type>(num_groups - 1);
|
||||
|
||||
pbg->buckets =
|
||||
buckets + static_cast<difference_type>(N * (n / N));
|
||||
pbg->next = last_group->next;
|
||||
pbg->next->prev = pbg;
|
||||
pbg->prev = last_group;
|
||||
pbg->prev->next = pbg;
|
||||
}
|
||||
|
||||
pbg->bitmask |= set_bit(n % N);
|
||||
}
|
||||
}
|
||||
|
||||
void insert_node(iterator itb, node_pointer p) BOOST_NOEXCEPT
|
||||
{
|
||||
this->append_bucket_group(itb);
|
||||
|
||||
p->next = itb->next;
|
||||
itb->next = p;
|
||||
}
|
||||
|
||||
void insert_node_hint(
|
||||
iterator itb, node_pointer p, node_pointer hint) BOOST_NOEXCEPT
|
||||
{
|
||||
this->append_bucket_group(itb);
|
||||
|
||||
if (hint) {
|
||||
p->next = hint->next;
|
||||
hint->next = p;
|
||||
} else {
|
||||
p->next = itb->next;
|
||||
itb->next = p;
|
||||
}
|
||||
}
|
||||
|
||||
void extract_node(iterator itb, node_pointer p) BOOST_NOEXCEPT
|
||||
{
|
||||
node_pointer* pp = boost::addressof(itb->next);
|
||||
while ((*pp) != p)
|
||||
pp = boost::addressof((*pp)->next);
|
||||
*pp = p->next;
|
||||
if (!itb->next)
|
||||
unlink_bucket(itb);
|
||||
}
|
||||
|
||||
void extract_node_after(iterator itb, node_pointer* pp) BOOST_NOEXCEPT
|
||||
{
|
||||
*pp = (*pp)->next;
|
||||
if (!itb->next)
|
||||
unlink_bucket(itb);
|
||||
}
|
||||
|
||||
void unlink_empty_buckets() BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t const N = group::N;
|
||||
|
||||
group_pointer pbg = groups,
|
||||
last = groups + static_cast<difference_type>(
|
||||
this->groups_len() - 1);
|
||||
|
||||
for (; pbg != last; ++pbg) {
|
||||
if (!pbg->buckets) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (std::size_t n = 0; n < N; ++n) {
|
||||
bucket_pointer bs = pbg->buckets;
|
||||
bucket_type& b = bs[static_cast<std::ptrdiff_t>(n)];
|
||||
if (!b.next)
|
||||
pbg->bitmask &= reset_bit(n);
|
||||
}
|
||||
if (!pbg->bitmask && pbg->next)
|
||||
unlink_group(pbg);
|
||||
}
|
||||
|
||||
// do not check end bucket
|
||||
for (std::size_t n = 0; n < size_ % N; ++n) {
|
||||
if (!pbg->buckets[static_cast<std::ptrdiff_t>(n)].next)
|
||||
pbg->bitmask &= reset_bit(n);
|
||||
}
|
||||
}
|
||||
|
||||
void unlink_bucket(iterator itb)
|
||||
{
|
||||
typename iterator::bucket_pointer p = itb.p;
|
||||
typename iterator::bucket_group_pointer pbg = itb.pbg;
|
||||
if (!(pbg->bitmask &=
|
||||
reset_bit(static_cast<std::size_t>(p - pbg->buckets))))
|
||||
unlink_group(pbg);
|
||||
}
|
||||
|
||||
private:
|
||||
void unlink_group(group_pointer pbg)
|
||||
{
|
||||
pbg->next->prev = pbg->prev;
|
||||
pbg->prev->next = pbg->next;
|
||||
pbg->prev = pbg->next = group_pointer();
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FCA_HPP
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (C) 2008-2016 Daniel James.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -31,7 +32,7 @@
|
||||
// 2012 = VC+11 = BOOST_MSVC 1700 Hopefully!
|
||||
// I have no idea when Dinkumware added it, probably a lot
|
||||
// earlier than this check.
|
||||
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 50, 0) || \
|
||||
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 10, 0) || \
|
||||
BOOST_COMP_MSVC >= BOOST_VERSION_NUMBER(17, 0, 0)
|
||||
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
|
||||
#endif
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (C) 2005-2016 Daniel James
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -25,25 +26,18 @@ namespace boost {
|
||||
typedef boost::unordered::detail::allocator_traits<value_allocator>
|
||||
value_allocator_traits;
|
||||
|
||||
typedef boost::unordered::detail::pick_node<A, value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
|
||||
typedef boost::unordered::detail::table<types> table;
|
||||
typedef boost::unordered::detail::map_extractor<value_type> extractor;
|
||||
|
||||
typedef typename boost::unordered::detail::pick_policy<K>::type policy;
|
||||
typedef typename boost::allocator_void_pointer<value_allocator>::type
|
||||
void_pointer;
|
||||
|
||||
typedef boost::unordered::iterator_detail::iterator<node> iterator;
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
|
||||
typedef boost::unordered::iterator_detail::l_iterator<node> l_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node>
|
||||
cl_iterator;
|
||||
typedef boost::unordered::node_handle_map<
|
||||
node<value_type, void_pointer>, K, M, A>
|
||||
node_type;
|
||||
|
||||
typedef boost::unordered::node_handle_map<node, K, M, A> node_type;
|
||||
typedef boost::unordered::insert_return_type_map<node, K, M, A>
|
||||
insert_return_type;
|
||||
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>
|
||||
|
262
include/boost/unordered/detail/prime_fmod.hpp
Normal file
@ -0,0 +1,262 @@
|
||||
// Copyright (C) 2022 Joaquin M Lopez Munoz.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/preprocessor/seq/enum.hpp>
|
||||
#include <boost/preprocessor/seq/for_each.hpp>
|
||||
#include <boost/preprocessor/seq/size.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
|
||||
#if defined(SIZE_MAX)
|
||||
#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0
|
||||
#define BOOST_UNORDERED_FCA_HAS_64B_SIZE_T
|
||||
#endif
|
||||
#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */
|
||||
#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0
|
||||
#define BOOST_UNORDERED_FCA_HAS_64B_SIZE_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) && defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
template <class = void> struct prime_fmod_size
|
||||
{
|
||||
// Because we compile for C++03, we don't have access to any inline
|
||||
// initialization for array data members so the definitions must exist
|
||||
// out-of-line. To keep the library header-only, we introduce a dummy
|
||||
// template parameter which permits the definition to be included in
|
||||
// multiple TUs without conflict.
|
||||
//
|
||||
static std::size_t sizes[];
|
||||
static std::size_t const sizes_len;
|
||||
static std::size_t (*positions[])(std::size_t);
|
||||
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
static boost::uint64_t inv_sizes32[];
|
||||
static std::size_t const inv_sizes32_len;
|
||||
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
|
||||
|
||||
static inline std::size_t size_index(std::size_t n)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for (; i < (sizes_len - 1); ++i) {
|
||||
if (sizes[i] >= n) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline std::size_t size(std::size_t size_index)
|
||||
{
|
||||
return sizes[size_index];
|
||||
}
|
||||
|
||||
template <std::size_t Size> static std::size_t modulo(std::size_t hash)
|
||||
{
|
||||
return hash % Size;
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
// We emulate the techniques taken from:
|
||||
// Faster Remainder by Direct Computation: Applications to Compilers and
|
||||
// Software Libraries
|
||||
// https://arxiv.org/abs/1902.01961
|
||||
//
|
||||
// In essence, use fancy math to directly calculate the remainder (aka
|
||||
// modulo) exploiting how compilers transform division
|
||||
//
|
||||
|
||||
static inline boost::uint64_t get_remainder(
|
||||
boost::uint64_t fractional, boost::uint32_t d)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
// use MSVC intrinsics when available to avoid promotion to 128 bits
|
||||
|
||||
return __umulh(fractional, d);
|
||||
#elif defined(BOOST_HAS_INT128)
|
||||
return static_cast<boost::uint64_t>(
|
||||
((boost::uint128_type)fractional * d) >> 64);
|
||||
#else
|
||||
// portable implementation in the absence of boost::uint128_type on 64
|
||||
// bits, which happens at least in GCC 4.5 and prior
|
||||
|
||||
boost::uint64_t r1 = (fractional & UINT32_MAX) * d;
|
||||
boost::uint64_t r2 = (fractional >> 32) * d;
|
||||
r2 += r1 >> 32;
|
||||
return r2 >> 32;
|
||||
#endif /* defined(_MSC_VER) */
|
||||
}
|
||||
|
||||
static inline boost::uint32_t fast_modulo(
|
||||
boost::uint32_t a, boost::uint64_t M, boost::uint32_t d)
|
||||
{
|
||||
boost::uint64_t fractional = M * a;
|
||||
return (boost::uint32_t)(get_remainder(fractional, d));
|
||||
}
|
||||
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
|
||||
|
||||
static inline std::size_t position(
|
||||
std::size_t hash, std::size_t size_index)
|
||||
{
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
std::size_t sizes_under_32bit = inv_sizes32_len;
|
||||
if (BOOST_LIKELY(size_index < sizes_under_32bit)) {
|
||||
return fast_modulo(
|
||||
boost::uint32_t(hash) + boost::uint32_t(hash >> 32),
|
||||
inv_sizes32[size_index], boost::uint32_t(sizes[size_index]));
|
||||
} else {
|
||||
return positions[size_index - sizes_under_32bit](hash);
|
||||
}
|
||||
#else
|
||||
return positions[size_index](hash);
|
||||
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
|
||||
}
|
||||
}; // prime_fmod_size
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE \
|
||||
(13ul)(29ul)(53ul)(97ul)(193ul)(389ul)(769ul)(1543ul)(3079ul)(6151ul)( \
|
||||
12289ul)(24593ul)(49157ul)(98317ul)(196613ul)(393241ul)(786433ul)( \
|
||||
1572869ul)(3145739ul)(6291469ul)(12582917ul)(25165843ul)(50331653ul)( \
|
||||
100663319ul)(201326611ul)(402653189ul)(805306457ul)(1610612741ul)( \
|
||||
3221225473ul)
|
||||
|
||||
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE(4294967291ul)
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
|
||||
|
||||
// The original sequence here is this:
|
||||
// (6442450939ul)
|
||||
// (12884901893ul)
|
||||
// (25769803751ul)
|
||||
// (51539607551ul)
|
||||
// (103079215111ul)
|
||||
// (206158430209ul)
|
||||
// (412316860441ul)
|
||||
// (824633720831ul)
|
||||
// (1649267441651ul)
|
||||
//
|
||||
// but this causes problems on versions of mingw where the `long` type is 32
|
||||
// bits, even for 64-bit targets. We work around this by replacing the literals
|
||||
// with compile-time arithmetic, using bitshifts to reconstruct the number.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT \
|
||||
((boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(2147483643ul)) \
|
||||
((boost::ulong_long_type(3ul) << 32) + boost::ulong_long_type(5ul)) \
|
||||
((boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(4294967271ul)) \
|
||||
((boost::ulong_long_type(11ul) << 32) + boost::ulong_long_type(4294967295ul)) \
|
||||
((boost::ulong_long_type(24ul) << 32) + boost::ulong_long_type(7ul)) \
|
||||
((boost::ulong_long_type(48ul) << 32) + boost::ulong_long_type(1ul)) \
|
||||
((boost::ulong_long_type(96ul) << 32) + boost::ulong_long_type(25ul)) \
|
||||
((boost::ulong_long_type(191ul) << 32) + boost::ulong_long_type(4294967295ul)) \
|
||||
((boost::ulong_long_type(383ul) << 32) + boost::ulong_long_type(4294967283ul))
|
||||
// clang-format on
|
||||
|
||||
#endif /* BOOST_UNORDERED_FCA_HAS_64B_SIZE_T */
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_SIZES \
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
|
||||
|
||||
template <class T>
|
||||
std::size_t prime_fmod_size<T>::sizes[] = {
|
||||
BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIME_FMOD_SIZES)};
|
||||
|
||||
template <class T>
|
||||
std::size_t const prime_fmod_size<T>::sizes_len = BOOST_PP_SEQ_SIZE(
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES);
|
||||
|
||||
// Similarly here, we have to re-express the integer initialization using
|
||||
// arithmetic such that each literal can fit in a 32-bit value.
|
||||
//
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
// clang-format off
|
||||
template <class T>
|
||||
boost::uint64_t prime_fmod_size<T>::inv_sizes32[] = {
|
||||
(boost::ulong_long_type(330382099ul) << 32) + boost::ulong_long_type(2973438898ul) /* = 1418980313362273202 */,
|
||||
(boost::ulong_long_type(148102320ul) << 32) + boost::ulong_long_type(2369637129ul) /* = 636094623231363849 */,
|
||||
(boost::ulong_long_type(81037118ul) << 32) + boost::ulong_long_type(3403558990ul) /* = 348051774975651918 */,
|
||||
(boost::ulong_long_type(44278013ul) << 32) + boost::ulong_long_type(1549730468ul) /* = 190172619316593316 */,
|
||||
(boost::ulong_long_type(22253716ul) << 32) + boost::ulong_long_type(2403401389ul) /* = 95578984837873325 */,
|
||||
(boost::ulong_long_type(11041047ul) << 32) + boost::ulong_long_type(143533612ul) /* = 47420935922132524 */,
|
||||
(boost::ulong_long_type(5585133ul) << 32) + boost::ulong_long_type(106117528ul) /* = 23987963684927896 */,
|
||||
(boost::ulong_long_type(2783517ul) << 32) + boost::ulong_long_type(1572687312ul) /* = 11955116055547344 */,
|
||||
(boost::ulong_long_type(1394922ul) << 32) + boost::ulong_long_type(3428720239ul) /* = 5991147799191151 */,
|
||||
(boost::ulong_long_type(698255ul) << 32) + boost::ulong_long_type(552319807ul) /* = 2998982941588287 */,
|
||||
(boost::ulong_long_type(349496ul) << 32) + boost::ulong_long_type(3827689953ul) /* = 1501077717772769 */,
|
||||
(boost::ulong_long_type(174641ul) << 32) + boost::ulong_long_type(3699438549ul) /* = 750081082979285 */,
|
||||
(boost::ulong_long_type(87372ul) << 32) + boost::ulong_long_type(1912757574ul) /* = 375261795343686 */,
|
||||
(boost::ulong_long_type(43684ul) << 32) + boost::ulong_long_type(3821029929ul) /* = 187625172388393 */,
|
||||
(boost::ulong_long_type(21844ul) << 32) + boost::ulong_long_type(3340590800ul) /* = 93822606204624 */,
|
||||
(boost::ulong_long_type(10921ul) << 32) + boost::ulong_long_type(4175852267ul) /* = 46909513691883 */,
|
||||
(boost::ulong_long_type(5461ul) << 32) + boost::ulong_long_type(1401829642ul) /* = 23456218233098 */,
|
||||
(boost::ulong_long_type(2730ul) << 32) + boost::ulong_long_type(2826028947ul) /* = 11728086747027 */,
|
||||
(boost::ulong_long_type(1365ul) << 32) + boost::ulong_long_type(1411150351ul) /* = 5864041509391 */,
|
||||
(boost::ulong_long_type(682ul) << 32) + boost::ulong_long_type(2857253105ul) /* = 2932024948977 */,
|
||||
(boost::ulong_long_type(341ul) << 32) + boost::ulong_long_type(1431073224ul) /* = 1466014921160 */,
|
||||
(boost::ulong_long_type(170ul) << 32) + boost::ulong_long_type(2862758116ul) /* = 733007198436 */,
|
||||
(boost::ulong_long_type(85ul) << 32) + boost::ulong_long_type(1431619357ul) /* = 366503839517 */,
|
||||
(boost::ulong_long_type(42ul) << 32) + boost::ulong_long_type(2863269661ul) /* = 183251896093 */,
|
||||
(boost::ulong_long_type(21ul) << 32) + boost::ulong_long_type(1431647119ul) /* = 91625960335 */,
|
||||
(boost::ulong_long_type(10ul) << 32) + boost::ulong_long_type(2863310962ul) /* = 45812983922 */,
|
||||
(boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(1431653234ul) /* = 22906489714 */,
|
||||
(boost::ulong_long_type(2ul) << 32) + boost::ulong_long_type(2863311496ul) /* = 11453246088 */,
|
||||
(boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(1431655764ul) /* = 5726623060 */,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
template <class T>
|
||||
std::size_t const
|
||||
prime_fmod_size<T>::inv_sizes32_len = sizeof(inv_sizes32) /
|
||||
sizeof(inv_sizes32[0]);
|
||||
|
||||
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
|
||||
|
||||
#define BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT(z, _, n) \
|
||||
prime_fmod_size<T>::template modulo<n>,
|
||||
|
||||
template <class T>
|
||||
std::size_t (*prime_fmod_size<T>::positions[])(std::size_t) = {
|
||||
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT)
|
||||
#else
|
||||
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
|
||||
BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT)
|
||||
#endif
|
||||
};
|
||||
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT
|
||||
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (C) 2005-2016 Daniel James
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -24,24 +25,18 @@ namespace boost {
|
||||
typedef boost::unordered::detail::allocator_traits<value_allocator>
|
||||
value_allocator_traits;
|
||||
|
||||
typedef boost::unordered::detail::pick_node<A, value_type> pick;
|
||||
typedef typename pick::node node;
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
|
||||
typedef boost::unordered::detail::table<types> table;
|
||||
typedef boost::unordered::detail::set_extractor<value_type> extractor;
|
||||
|
||||
typedef typename boost::unordered::detail::pick_policy<T>::type policy;
|
||||
typedef typename boost::allocator_void_pointer<value_allocator>::type
|
||||
void_pointer;
|
||||
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> iterator;
|
||||
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node> l_iterator;
|
||||
typedef boost::unordered::iterator_detail::cl_iterator<node>
|
||||
cl_iterator;
|
||||
typedef boost::unordered::node_handle_set<
|
||||
node<value_type, void_pointer>, T, A>
|
||||
node_type;
|
||||
|
||||
typedef boost::unordered::node_handle_set<node, T, A> node_type;
|
||||
typedef boost::unordered::insert_return_type_set<node, T, A>
|
||||
typedef typename table::c_iterator iterator;
|
||||
typedef boost::unordered::insert_return_type_set<iterator, node_type>
|
||||
insert_return_type;
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -57,8 +58,6 @@ namespace boost {
|
||||
typedef boost::unordered::detail::map<A, K, T, H, P> types;
|
||||
typedef typename types::value_allocator_traits value_allocator_traits;
|
||||
typedef typename types::table table;
|
||||
typedef typename table::node_pointer node_pointer;
|
||||
typedef typename table::link_pointer link_pointer;
|
||||
|
||||
public:
|
||||
typedef typename value_allocator_traits::pointer pointer;
|
||||
@ -97,7 +96,7 @@ namespace boost {
|
||||
|
||||
unordered_map(unordered_map const&);
|
||||
|
||||
#if defined(BOOST_UNORDERED_USE_MOVE) || \
|
||||
#if defined(BOOST_UNORDERED_USE_MOVE) || \
|
||||
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
unordered_map(BOOST_RV_REF(unordered_map) other)
|
||||
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
|
||||
@ -188,9 +187,9 @@ namespace boost {
|
||||
return table_.node_alloc();
|
||||
}
|
||||
|
||||
// iterators
|
||||
// // iterators
|
||||
|
||||
iterator begin() BOOST_NOEXCEPT { return iterator(table_.begin()); }
|
||||
iterator begin() BOOST_NOEXCEPT { return table_.begin(); }
|
||||
|
||||
const_iterator begin() const BOOST_NOEXCEPT
|
||||
{
|
||||
@ -445,7 +444,7 @@ namespace boost {
|
||||
insert_return_type insert(BOOST_RV_REF(node_type) np)
|
||||
{
|
||||
insert_return_type result;
|
||||
table_.move_insert_node_type_unique(np, result);
|
||||
table_.move_insert_node_type_unique((node_type&)np, result);
|
||||
return boost::move(result);
|
||||
}
|
||||
|
||||
@ -728,8 +727,7 @@ namespace boost {
|
||||
size_type>::type
|
||||
erase(BOOST_FWD_REF(Key) k)
|
||||
{
|
||||
return table_.erase_key_unique_impl(
|
||||
this->key_eq(), boost::forward<Key>(k));
|
||||
return table_.erase_key_unique_impl(boost::forward<Key>(k));
|
||||
}
|
||||
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
@ -774,9 +772,7 @@ namespace boost {
|
||||
iterator>::type
|
||||
find(const Key& key)
|
||||
{
|
||||
return iterator(table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq()));
|
||||
return table_.find(key);
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -784,9 +780,7 @@ namespace boost {
|
||||
const_iterator>::type
|
||||
find(const Key& key) const
|
||||
{
|
||||
return const_iterator(table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq()));
|
||||
return const_iterator(table_.find(key));
|
||||
}
|
||||
|
||||
template <class CompatibleKey, class CompatibleHash,
|
||||
@ -801,9 +795,7 @@ namespace boost {
|
||||
|
||||
bool contains(const key_type& k) const
|
||||
{
|
||||
return 0 != table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
return table_.find(k) != this->end();
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -811,9 +803,7 @@ namespace boost {
|
||||
bool>::type
|
||||
contains(const Key& k) const
|
||||
{
|
||||
return 0 != table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
return table_.find(k) != this->end();
|
||||
}
|
||||
|
||||
size_type count(const key_type&) const;
|
||||
@ -823,14 +813,7 @@ namespace boost {
|
||||
size_type>::type
|
||||
count(const Key& k) const
|
||||
{
|
||||
std::size_t const key_hash =
|
||||
table::policy::apply_hash(this->hash_function(), k);
|
||||
|
||||
P const& eq = this->key_eq();
|
||||
|
||||
node_pointer p = table_.find_node_impl(key_hash, k, eq);
|
||||
|
||||
return (p ? 1 : 0);
|
||||
return (table_.find(k) != this->end() ? 1 : 0);
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const key_type&);
|
||||
@ -842,12 +825,13 @@ namespace boost {
|
||||
std::pair<iterator, iterator> >::type
|
||||
equal_range(const Key& key)
|
||||
{
|
||||
node_pointer p = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq());
|
||||
iterator first = table_.find(key);
|
||||
iterator last = first;
|
||||
if (last != this->end()) {
|
||||
++last;
|
||||
}
|
||||
|
||||
return std::make_pair(
|
||||
iterator(p), iterator(p ? table::next_node(p) : p));
|
||||
return std::make_pair(first, last);
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -855,12 +839,13 @@ namespace boost {
|
||||
std::pair<const_iterator, const_iterator> >::type
|
||||
equal_range(const Key& key) const
|
||||
{
|
||||
node_pointer p = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq());
|
||||
iterator first = table_.find(key);
|
||||
iterator last = first;
|
||||
if (last != this->end()) {
|
||||
++last;
|
||||
}
|
||||
|
||||
return std::make_pair(
|
||||
const_iterator(p), const_iterator(p ? table::next_node(p) : p));
|
||||
return std::make_pair(first, last);
|
||||
}
|
||||
|
||||
mapped_type& operator[](const key_type&);
|
||||
@ -872,7 +857,7 @@ namespace boost {
|
||||
|
||||
size_type bucket_count() const BOOST_NOEXCEPT
|
||||
{
|
||||
return table_.bucket_count_;
|
||||
return table_.bucket_count();
|
||||
}
|
||||
|
||||
size_type max_bucket_count() const BOOST_NOEXCEPT
|
||||
@ -889,16 +874,15 @@ namespace boost {
|
||||
|
||||
local_iterator begin(size_type n)
|
||||
{
|
||||
return local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return table_.begin(n);
|
||||
}
|
||||
|
||||
const_local_iterator begin(size_type n) const
|
||||
{
|
||||
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return const_local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
local_iterator end(size_type) { return local_iterator(); }
|
||||
|
||||
const_local_iterator end(size_type) const
|
||||
{
|
||||
return const_local_iterator();
|
||||
@ -906,7 +890,7 @@ namespace boost {
|
||||
|
||||
const_local_iterator cbegin(size_type n) const
|
||||
{
|
||||
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return const_local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
const_local_iterator cend(size_type) const
|
||||
@ -1026,8 +1010,6 @@ namespace boost {
|
||||
typedef boost::unordered::detail::map<A, K, T, H, P> types;
|
||||
typedef typename types::value_allocator_traits value_allocator_traits;
|
||||
typedef typename types::table table;
|
||||
typedef typename table::node_pointer node_pointer;
|
||||
typedef typename table::link_pointer link_pointer;
|
||||
|
||||
public:
|
||||
typedef typename value_allocator_traits::pointer pointer;
|
||||
@ -1394,7 +1376,7 @@ namespace boost {
|
||||
|
||||
node_type extract(const key_type& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key(k), table_.node_alloc());
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -1437,8 +1419,7 @@ namespace boost {
|
||||
size_type>::type
|
||||
erase(BOOST_FWD_REF(Key) k)
|
||||
{
|
||||
return table_.erase_key_equiv_impl(
|
||||
this->key_eq(), boost::forward<Key>(k));
|
||||
return table_.erase_key_equiv_impl(boost::forward<Key>(k));
|
||||
}
|
||||
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
@ -1483,9 +1464,7 @@ namespace boost {
|
||||
iterator>::type
|
||||
find(const Key& key)
|
||||
{
|
||||
return iterator(table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq()));
|
||||
return table_.find(key);
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -1493,9 +1472,7 @@ namespace boost {
|
||||
const_iterator>::type
|
||||
find(const Key& key) const
|
||||
{
|
||||
return const_iterator(table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq()));
|
||||
return const_iterator(table_.find(key));
|
||||
}
|
||||
|
||||
template <class CompatibleKey, class CompatibleHash,
|
||||
@ -1510,9 +1487,7 @@ namespace boost {
|
||||
|
||||
bool contains(key_type const& k) const
|
||||
{
|
||||
return 0 != table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
return table_.find(k) != this->end();
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -1520,9 +1495,7 @@ namespace boost {
|
||||
bool>::type
|
||||
contains(const Key& k) const
|
||||
{
|
||||
return 0 != table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
return table_.find(k) != this->end();
|
||||
}
|
||||
|
||||
size_type count(const key_type&) const;
|
||||
@ -1532,11 +1505,7 @@ namespace boost {
|
||||
size_type>::type
|
||||
count(const Key& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
|
||||
return n ? table_.group_count(n) : 0;
|
||||
return table_.group_count(k);
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const key_type&);
|
||||
@ -1548,12 +1517,8 @@ namespace boost {
|
||||
std::pair<iterator, iterator> >::type
|
||||
equal_range(const Key& key)
|
||||
{
|
||||
node_pointer p = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq());
|
||||
|
||||
return std::make_pair(
|
||||
iterator(p), iterator(p ? table_.next_group(p) : p));
|
||||
iterator p = table_.find(key);
|
||||
return std::make_pair(p, table_.next_group(key, p));
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -1561,19 +1526,16 @@ namespace boost {
|
||||
std::pair<const_iterator, const_iterator> >::type
|
||||
equal_range(const Key& key) const
|
||||
{
|
||||
node_pointer p = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq());
|
||||
|
||||
iterator p = table_.find(key);
|
||||
return std::make_pair(
|
||||
const_iterator(p), const_iterator(p ? table_.next_group(p) : p));
|
||||
const_iterator(p), const_iterator(table_.next_group(key, p)));
|
||||
}
|
||||
|
||||
// bucket interface
|
||||
|
||||
size_type bucket_count() const BOOST_NOEXCEPT
|
||||
{
|
||||
return table_.bucket_count_;
|
||||
return table_.bucket_count();
|
||||
}
|
||||
|
||||
size_type max_bucket_count() const BOOST_NOEXCEPT
|
||||
@ -1590,12 +1552,12 @@ namespace boost {
|
||||
|
||||
local_iterator begin(size_type n)
|
||||
{
|
||||
return local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
const_local_iterator begin(size_type n) const
|
||||
{
|
||||
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return const_local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
local_iterator end(size_type) { return local_iterator(); }
|
||||
@ -1607,7 +1569,7 @@ namespace boost {
|
||||
|
||||
const_local_iterator cbegin(size_type n) const
|
||||
{
|
||||
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return const_local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
const_local_iterator cend(size_type) const
|
||||
@ -1730,7 +1692,7 @@ namespace boost {
|
||||
unordered_map::value_allocator_traits::
|
||||
select_on_container_copy_construction(other.get_allocator()))
|
||||
{
|
||||
if (other.table_.size_) {
|
||||
if (other.size()) {
|
||||
table_.copy_buckets(
|
||||
other.table_, boost::unordered::detail::true_type());
|
||||
}
|
||||
@ -1894,29 +1856,21 @@ namespace boost {
|
||||
typename unordered_map<K, T, H, P, A>::iterator
|
||||
unordered_map<K, T, H, P, A>::erase(iterator position)
|
||||
{
|
||||
node_pointer node = table::get_node(position);
|
||||
BOOST_ASSERT(node);
|
||||
node_pointer next = table::next_node(node);
|
||||
table_.erase_nodes_unique(node, next);
|
||||
return iterator(next);
|
||||
return table_.erase_node(position);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
typename unordered_map<K, T, H, P, A>::iterator
|
||||
unordered_map<K, T, H, P, A>::erase(const_iterator position)
|
||||
{
|
||||
node_pointer node = table::get_node(position);
|
||||
BOOST_ASSERT(node);
|
||||
node_pointer next = table::next_node(node);
|
||||
table_.erase_nodes_unique(node, next);
|
||||
return iterator(next);
|
||||
return table_.erase_node(position);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
typename unordered_map<K, T, H, P, A>::size_type
|
||||
unordered_map<K, T, H, P, A>::erase(const key_type& k)
|
||||
{
|
||||
return table_.erase_key_unique_impl(this->key_eq(), k);
|
||||
return table_.erase_key_unique_impl(k);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -1924,11 +1878,7 @@ namespace boost {
|
||||
unordered_map<K, T, H, P, A>::erase(
|
||||
const_iterator first, const_iterator last)
|
||||
{
|
||||
node_pointer last_node = table::get_node(last);
|
||||
if (first == last)
|
||||
return iterator(last_node);
|
||||
table_.erase_nodes_unique(table::get_node(first), last_node);
|
||||
return iterator(last_node);
|
||||
return table_.erase_nodes_range(first, last);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -1998,14 +1948,14 @@ namespace boost {
|
||||
typename unordered_map<K, T, H, P, A>::iterator
|
||||
unordered_map<K, T, H, P, A>::find(const key_type& k)
|
||||
{
|
||||
return iterator(table_.find_node(k));
|
||||
return iterator(table_.find(k));
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
typename unordered_map<K, T, H, P, A>::const_iterator
|
||||
unordered_map<K, T, H, P, A>::find(const key_type& k) const
|
||||
{
|
||||
return const_iterator(table_.find_node(k));
|
||||
return const_iterator(table_.find(k));
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2015,8 +1965,7 @@ namespace boost {
|
||||
unordered_map<K, T, H, P, A>::find(CompatibleKey const& k,
|
||||
CompatibleHash const& hash, CompatiblePredicate const& eq)
|
||||
{
|
||||
return iterator(
|
||||
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
|
||||
return table_.transparent_find(k, hash, eq);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2026,8 +1975,7 @@ namespace boost {
|
||||
unordered_map<K, T, H, P, A>::find(CompatibleKey const& k,
|
||||
CompatibleHash const& hash, CompatiblePredicate const& eq) const
|
||||
{
|
||||
return const_iterator(
|
||||
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
|
||||
return table_.transparent_find(k, hash, eq);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2042,8 +1990,12 @@ namespace boost {
|
||||
typename unordered_map<K, T, H, P, A>::iterator>
|
||||
unordered_map<K, T, H, P, A>::equal_range(const key_type& k)
|
||||
{
|
||||
node_pointer n = table_.find_node(k);
|
||||
return std::make_pair(iterator(n), iterator(n ? table::next_node(n) : n));
|
||||
iterator first = table_.find(k);
|
||||
iterator second = first;
|
||||
if (second != this->end()) {
|
||||
++second;
|
||||
}
|
||||
return std::make_pair(first, second);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2051,9 +2003,12 @@ namespace boost {
|
||||
typename unordered_map<K, T, H, P, A>::const_iterator>
|
||||
unordered_map<K, T, H, P, A>::equal_range(const key_type& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node(k);
|
||||
return std::make_pair(
|
||||
const_iterator(n), const_iterator(n ? table::next_node(n) : n));
|
||||
iterator first = table_.find(k);
|
||||
iterator second = first;
|
||||
if (second != this->end()) {
|
||||
++second;
|
||||
}
|
||||
return std::make_pair(const_iterator(first), const_iterator(second));
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2074,10 +2029,12 @@ namespace boost {
|
||||
typename unordered_map<K, T, H, P, A>::mapped_type&
|
||||
unordered_map<K, T, H, P, A>::at(const key_type& k)
|
||||
{
|
||||
typedef typename table::node_pointer node_pointer;
|
||||
|
||||
if (table_.size_) {
|
||||
node_pointer n = table_.find_node(k);
|
||||
if (n)
|
||||
return n->value().second;
|
||||
node_pointer p = table_.find_node(k);
|
||||
if (p)
|
||||
return p->value().second;
|
||||
}
|
||||
|
||||
boost::throw_exception(
|
||||
@ -2088,10 +2045,12 @@ namespace boost {
|
||||
typename unordered_map<K, T, H, P, A>::mapped_type const&
|
||||
unordered_map<K, T, H, P, A>::at(const key_type& k) const
|
||||
{
|
||||
typedef typename table::node_pointer node_pointer;
|
||||
|
||||
if (table_.size_) {
|
||||
node_pointer n = table_.find_node(k);
|
||||
if (n)
|
||||
return n->value().second;
|
||||
node_pointer p = table_.find_node(k);
|
||||
if (p)
|
||||
return p->value().second;
|
||||
}
|
||||
|
||||
boost::throw_exception(
|
||||
@ -2110,9 +2069,9 @@ namespace boost {
|
||||
template <class K, class T, class H, class P, class A>
|
||||
float unordered_map<K, T, H, P, A>::load_factor() const BOOST_NOEXCEPT
|
||||
{
|
||||
BOOST_ASSERT(table_.bucket_count_ != 0);
|
||||
BOOST_ASSERT(table_.bucket_count() != 0);
|
||||
return static_cast<float>(table_.size_) /
|
||||
static_cast<float>(table_.bucket_count_);
|
||||
static_cast<float>(table_.bucket_count());
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2130,8 +2089,7 @@ namespace boost {
|
||||
template <class K, class T, class H, class P, class A>
|
||||
void unordered_map<K, T, H, P, A>::reserve(size_type n)
|
||||
{
|
||||
table_.rehash(static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>(n) / table_.mlf_)));
|
||||
table_.reserve(n);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2377,22 +2335,16 @@ namespace boost {
|
||||
typename unordered_multimap<K, T, H, P, A>::iterator
|
||||
unordered_multimap<K, T, H, P, A>::erase(iterator position)
|
||||
{
|
||||
node_pointer node = table::get_node(position);
|
||||
BOOST_ASSERT(node);
|
||||
node_pointer next = table::next_node(node);
|
||||
table_.erase_nodes_equiv(node, next);
|
||||
return iterator(next);
|
||||
BOOST_ASSERT(position != this->end());
|
||||
return table_.erase_node(position);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
typename unordered_multimap<K, T, H, P, A>::iterator
|
||||
unordered_multimap<K, T, H, P, A>::erase(const_iterator position)
|
||||
{
|
||||
node_pointer node = table::get_node(position);
|
||||
BOOST_ASSERT(node);
|
||||
node_pointer next = table::next_node(node);
|
||||
table_.erase_nodes_equiv(node, next);
|
||||
return iterator(next);
|
||||
BOOST_ASSERT(position != this->end());
|
||||
return table_.erase_node(position);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2407,11 +2359,7 @@ namespace boost {
|
||||
unordered_multimap<K, T, H, P, A>::erase(
|
||||
const_iterator first, const_iterator last)
|
||||
{
|
||||
node_pointer last_node = table::get_node(last);
|
||||
if (first == last)
|
||||
return iterator(last_node);
|
||||
table_.erase_nodes_equiv(table::get_node(first), last_node);
|
||||
return iterator(last_node);
|
||||
return table_.erase_nodes_range(first, last);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2489,14 +2437,14 @@ namespace boost {
|
||||
typename unordered_multimap<K, T, H, P, A>::iterator
|
||||
unordered_multimap<K, T, H, P, A>::find(const key_type& k)
|
||||
{
|
||||
return iterator(table_.find_node(k));
|
||||
return iterator(table_.find(k));
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
typename unordered_multimap<K, T, H, P, A>::const_iterator
|
||||
unordered_multimap<K, T, H, P, A>::find(const key_type& k) const
|
||||
{
|
||||
return const_iterator(table_.find_node(k));
|
||||
return const_iterator(table_.find(k));
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2506,8 +2454,7 @@ namespace boost {
|
||||
unordered_multimap<K, T, H, P, A>::find(CompatibleKey const& k,
|
||||
CompatibleHash const& hash, CompatiblePredicate const& eq)
|
||||
{
|
||||
return iterator(
|
||||
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
|
||||
return table_.transparent_find(k, hash, eq);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2525,8 +2472,7 @@ namespace boost {
|
||||
typename unordered_multimap<K, T, H, P, A>::size_type
|
||||
unordered_multimap<K, T, H, P, A>::count(const key_type& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node(k);
|
||||
return n ? table_.group_count(n) : 0;
|
||||
return table_.group_count(k);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2534,9 +2480,8 @@ namespace boost {
|
||||
typename unordered_multimap<K, T, H, P, A>::iterator>
|
||||
unordered_multimap<K, T, H, P, A>::equal_range(const key_type& k)
|
||||
{
|
||||
node_pointer n = table_.find_node(k);
|
||||
return std::make_pair(
|
||||
iterator(n), iterator(n ? table_.next_group(n) : n));
|
||||
iterator n = table_.find(k);
|
||||
return std::make_pair(n, (n == end() ? n : table_.next_group(k, n)));
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2544,9 +2489,9 @@ namespace boost {
|
||||
typename unordered_multimap<K, T, H, P, A>::const_iterator>
|
||||
unordered_multimap<K, T, H, P, A>::equal_range(const key_type& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node(k);
|
||||
return std::make_pair(
|
||||
const_iterator(n), const_iterator(n ? table_.next_group(n) : n));
|
||||
iterator n = table_.find(k);
|
||||
return std::make_pair(const_iterator(n),
|
||||
const_iterator(n == end() ? n : table_.next_group(k, n)));
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2561,9 +2506,9 @@ namespace boost {
|
||||
template <class K, class T, class H, class P, class A>
|
||||
float unordered_multimap<K, T, H, P, A>::load_factor() const BOOST_NOEXCEPT
|
||||
{
|
||||
BOOST_ASSERT(table_.bucket_count_ != 0);
|
||||
BOOST_ASSERT(table_.bucket_count() != 0);
|
||||
return static_cast<float>(table_.size_) /
|
||||
static_cast<float>(table_.bucket_count_);
|
||||
static_cast<float>(table_.bucket_count());
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2582,8 +2527,7 @@ namespace boost {
|
||||
template <class K, class T, class H, class P, class A>
|
||||
void unordered_multimap<K, T, H, P, A>::reserve(size_type n)
|
||||
{
|
||||
table_.rehash(static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>(n) / table_.mlf_)));
|
||||
table_.reserve(n);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2643,16 +2587,14 @@ namespace boost {
|
||||
template <class K2, class T2, class H2, class P2, class A2>
|
||||
friend class boost::unordered::unordered_multimap;
|
||||
|
||||
typedef typename boost::unordered::detail::rebind_wrap<A,
|
||||
std::pair<K const, T> >::type value_allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<value_allocator>
|
||||
value_allocator_traits;
|
||||
typedef typename boost::allocator_rebind<A, std::pair<K const, T> >::type
|
||||
value_allocator;
|
||||
|
||||
typedef N node;
|
||||
typedef typename boost::unordered::detail::rebind_wrap<A, node>::type
|
||||
node_allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<node_allocator>
|
||||
node_allocator_traits;
|
||||
typedef typename node_allocator_traits::pointer node_pointer;
|
||||
typedef typename boost::allocator_rebind<A, node>::type node_allocator;
|
||||
|
||||
typedef
|
||||
typename boost::allocator_pointer<node_allocator>::type node_pointer;
|
||||
|
||||
public:
|
||||
typedef K key_type;
|
||||
@ -2681,8 +2623,8 @@ namespace boost {
|
||||
}
|
||||
|
||||
node_handle_map(BOOST_RV_REF(node_handle_map) n) BOOST_NOEXCEPT
|
||||
: ptr_(n.ptr_),
|
||||
alloc_(boost::move(n.alloc_))
|
||||
: ptr_(n.ptr_),
|
||||
alloc_(boost::move(n.alloc_))
|
||||
{
|
||||
n.ptr_ = node_pointer();
|
||||
}
|
||||
@ -2690,8 +2632,8 @@ namespace boost {
|
||||
node_handle_map& operator=(BOOST_RV_REF(node_handle_map) n)
|
||||
{
|
||||
BOOST_ASSERT(!alloc_.has_value() ||
|
||||
value_allocator_traits::
|
||||
propagate_on_container_move_assignment::value ||
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
value_allocator>::type::value ||
|
||||
(n.alloc_.has_value() && alloc_ == n.alloc_));
|
||||
|
||||
if (ptr_) {
|
||||
@ -2702,8 +2644,8 @@ namespace boost {
|
||||
}
|
||||
|
||||
if (!alloc_.has_value() ||
|
||||
value_allocator_traits::propagate_on_container_move_assignment::
|
||||
value) {
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
value_allocator>::type::value) {
|
||||
alloc_ = boost::move(n.alloc_);
|
||||
}
|
||||
ptr_ = n.ptr_;
|
||||
@ -2731,14 +2673,17 @@ namespace boost {
|
||||
}
|
||||
|
||||
void swap(node_handle_map& n) BOOST_NOEXCEPT_IF(
|
||||
value_allocator_traits::propagate_on_container_swap::value ||
|
||||
value_allocator_traits::is_always_equal::value)
|
||||
boost::allocator_propagate_on_container_swap<
|
||||
value_allocator>::type::value ||
|
||||
boost::allocator_is_always_equal<value_allocator>::type::value)
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
!alloc_.has_value() || !n.alloc_.has_value() ||
|
||||
value_allocator_traits::propagate_on_container_swap::value ||
|
||||
alloc_ == n.alloc_);
|
||||
if (value_allocator_traits::propagate_on_container_swap::value ||
|
||||
|
||||
BOOST_ASSERT(!alloc_.has_value() || !n.alloc_.has_value() ||
|
||||
boost::allocator_propagate_on_container_swap<
|
||||
value_allocator>::type::value ||
|
||||
alloc_ == n.alloc_);
|
||||
if (boost::allocator_propagate_on_container_swap<
|
||||
value_allocator>::type::value ||
|
||||
!alloc_.has_value() || !n.alloc_.has_value()) {
|
||||
boost::swap(alloc_, n.alloc_);
|
||||
}
|
||||
@ -2753,25 +2698,25 @@ namespace boost {
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
template <class N, class K, class T, class A> struct insert_return_type_map
|
||||
template <class Iter, class NodeType> struct insert_return_type_map
|
||||
{
|
||||
private:
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type_map)
|
||||
|
||||
typedef typename boost::unordered::detail::rebind_wrap<A,
|
||||
std::pair<K const, T> >::type value_allocator;
|
||||
typedef N node_;
|
||||
// typedef typename boost::allocator_rebind<A,
|
||||
// std::pair<K const, T> >::type value_allocator;
|
||||
// typedef N node_;
|
||||
|
||||
public:
|
||||
Iter position;
|
||||
bool inserted;
|
||||
boost::unordered::iterator_detail::iterator<node_> position;
|
||||
boost::unordered::node_handle_map<N, K, T, A> node;
|
||||
NodeType node;
|
||||
|
||||
insert_return_type_map() : inserted(false), position(), node() {}
|
||||
insert_return_type_map() : position(), inserted(false), node() {}
|
||||
|
||||
insert_return_type_map(BOOST_RV_REF(insert_return_type_map)
|
||||
x) BOOST_NOEXCEPT : inserted(x.inserted),
|
||||
position(x.position),
|
||||
x) BOOST_NOEXCEPT : position(x.position),
|
||||
inserted(x.inserted),
|
||||
node(boost::move(x.node))
|
||||
{
|
||||
}
|
||||
@ -2785,9 +2730,9 @@ namespace boost {
|
||||
}
|
||||
};
|
||||
|
||||
template <class N, class K, class T, class A>
|
||||
void swap(insert_return_type_map<N, K, T, A>& x,
|
||||
insert_return_type_map<N, K, T, A>& y)
|
||||
template <class Iter, class NodeType>
|
||||
void swap(insert_return_type_map<Iter, NodeType>& x,
|
||||
insert_return_type_map<Iter, NodeType>& y)
|
||||
{
|
||||
boost::swap(x.node, y.node);
|
||||
boost::swap(x.inserted, y.inserted);
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (C) 2008-2011 Daniel James.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -59,7 +60,7 @@ namespace boost {
|
||||
unordered_multimap<K, T, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class N, class K, class T, class A> class node_handle_map;
|
||||
template <class N, class K, class T, class A> struct insert_return_type_map;
|
||||
template <class Iter, class NodeType> struct insert_return_type_map;
|
||||
}
|
||||
|
||||
using boost::unordered::unordered_map;
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
|
||||
// Copyright (C) 2005-2011 Daniel James.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -55,8 +56,6 @@ namespace boost {
|
||||
typedef boost::unordered::detail::set<A, T, H, P> types;
|
||||
typedef typename types::value_allocator_traits value_allocator_traits;
|
||||
typedef typename types::table table;
|
||||
typedef typename table::node_pointer node_pointer;
|
||||
typedef typename table::link_pointer link_pointer;
|
||||
|
||||
public:
|
||||
typedef typename value_allocator_traits::pointer pointer;
|
||||
@ -68,9 +67,9 @@ namespace boost {
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
typedef typename table::iterator iterator;
|
||||
typedef typename table::c_iterator iterator;
|
||||
typedef typename table::c_iterator const_iterator;
|
||||
typedef typename table::l_iterator local_iterator;
|
||||
typedef typename table::cl_iterator local_iterator;
|
||||
typedef typename table::cl_iterator const_local_iterator;
|
||||
typedef typename types::node_type node_type;
|
||||
typedef typename types::insert_return_type insert_return_type;
|
||||
@ -411,7 +410,7 @@ namespace boost {
|
||||
|
||||
node_type extract(const key_type& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key(k), table_.node_alloc());
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -455,8 +454,7 @@ namespace boost {
|
||||
size_type>::type
|
||||
erase(BOOST_FWD_REF(Key) k)
|
||||
{
|
||||
return table_.erase_key_unique_impl(
|
||||
this->key_eq(), boost::forward<Key>(k));
|
||||
return table_.erase_key_unique_impl(boost::forward<Key>(k));
|
||||
}
|
||||
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
@ -500,9 +498,7 @@ namespace boost {
|
||||
const_iterator>::type
|
||||
find(const Key& k) const
|
||||
{
|
||||
return const_iterator(table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq()));
|
||||
return const_iterator(table_.find(k));
|
||||
}
|
||||
|
||||
template <class CompatibleKey, class CompatibleHash,
|
||||
@ -512,9 +508,7 @@ namespace boost {
|
||||
|
||||
bool contains(key_type const& k) const
|
||||
{
|
||||
return 0 != table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
return table_.find(k) != this->end();
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -522,9 +516,7 @@ namespace boost {
|
||||
bool>::type
|
||||
contains(const Key& k) const
|
||||
{
|
||||
return 0 != table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
return table_.find(k) != this->end();
|
||||
}
|
||||
|
||||
size_type count(const key_type&) const;
|
||||
@ -534,11 +526,7 @@ namespace boost {
|
||||
size_type>::type
|
||||
count(const Key& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
|
||||
return n ? 1 : 0;
|
||||
return table_.find(k) != this->end() ? 1 : 0;
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(
|
||||
@ -549,19 +537,20 @@ namespace boost {
|
||||
std::pair<const_iterator, const_iterator> >::type
|
||||
equal_range(Key const& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
iterator n = table_.find(k);
|
||||
iterator m = n;
|
||||
if (m != this->end()) {
|
||||
++m;
|
||||
}
|
||||
|
||||
return std::make_pair(
|
||||
const_iterator(n), const_iterator(n ? table::next_node(n) : n));
|
||||
return std::make_pair(const_iterator(n), const_iterator(m));
|
||||
}
|
||||
|
||||
// bucket interface
|
||||
|
||||
size_type bucket_count() const BOOST_NOEXCEPT
|
||||
{
|
||||
return table_.bucket_count_;
|
||||
return table_.bucket_count();
|
||||
}
|
||||
|
||||
size_type max_bucket_count() const BOOST_NOEXCEPT
|
||||
@ -578,12 +567,12 @@ namespace boost {
|
||||
|
||||
local_iterator begin(size_type n)
|
||||
{
|
||||
return local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
const_local_iterator begin(size_type n) const
|
||||
{
|
||||
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return const_local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
local_iterator end(size_type) { return local_iterator(); }
|
||||
@ -595,7 +584,7 @@ namespace boost {
|
||||
|
||||
const_local_iterator cbegin(size_type n) const
|
||||
{
|
||||
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return const_local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
const_local_iterator cend(size_type) const
|
||||
@ -684,8 +673,6 @@ namespace boost {
|
||||
typedef boost::unordered::detail::set<A, T, H, P> types;
|
||||
typedef typename types::value_allocator_traits value_allocator_traits;
|
||||
typedef typename types::table table;
|
||||
typedef typename table::node_pointer node_pointer;
|
||||
typedef typename table::link_pointer link_pointer;
|
||||
|
||||
public:
|
||||
typedef typename value_allocator_traits::pointer pointer;
|
||||
@ -697,9 +684,9 @@ namespace boost {
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
typedef typename table::iterator iterator;
|
||||
typedef typename table::c_iterator iterator;
|
||||
typedef typename table::c_iterator const_iterator;
|
||||
typedef typename table::l_iterator local_iterator;
|
||||
typedef typename table::cl_iterator local_iterator;
|
||||
typedef typename table::cl_iterator const_local_iterator;
|
||||
typedef typename types::node_type node_type;
|
||||
|
||||
@ -1035,7 +1022,7 @@ namespace boost {
|
||||
|
||||
node_type extract(const key_type& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key(k), table_.node_alloc());
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -1076,7 +1063,7 @@ namespace boost {
|
||||
size_type>::type
|
||||
erase(const Key& k)
|
||||
{
|
||||
return table_.erase_key_equiv_impl(this->key_eq(), k);
|
||||
return table_.erase_key_equiv_impl(k);
|
||||
}
|
||||
|
||||
iterator erase(const_iterator, const_iterator);
|
||||
@ -1121,9 +1108,7 @@ namespace boost {
|
||||
const_iterator>::type
|
||||
find(const Key& k) const
|
||||
{
|
||||
return const_iterator(table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq()));
|
||||
return table_.find(k);
|
||||
}
|
||||
|
||||
template <class CompatibleKey, class CompatibleHash,
|
||||
@ -1133,9 +1118,7 @@ namespace boost {
|
||||
|
||||
bool contains(const key_type& k) const
|
||||
{
|
||||
return 0 != table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
return table_.find(k) != this->end();
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -1143,9 +1126,7 @@ namespace boost {
|
||||
bool>::type
|
||||
contains(const Key& k) const
|
||||
{
|
||||
return 0 != table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
return table_.find(k) != this->end();
|
||||
}
|
||||
|
||||
size_type count(const key_type&) const;
|
||||
@ -1155,11 +1136,7 @@ namespace boost {
|
||||
size_type>::type
|
||||
count(const Key& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
|
||||
return n ? table_.group_count(n) : 0;
|
||||
return table_.group_count(k);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(
|
||||
@ -1170,19 +1147,16 @@ namespace boost {
|
||||
std::pair<const_iterator, const_iterator> >::type
|
||||
equal_range(const Key& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), k), k,
|
||||
this->key_eq());
|
||||
|
||||
return std::make_pair(
|
||||
const_iterator(n), const_iterator(n ? table_.next_group(n) : n));
|
||||
iterator first = table_.find(k);
|
||||
iterator last = table_.next_group(k, first);
|
||||
return std::make_pair(const_iterator(first), const_iterator(last));
|
||||
}
|
||||
|
||||
// bucket interface
|
||||
|
||||
size_type bucket_count() const BOOST_NOEXCEPT
|
||||
{
|
||||
return table_.bucket_count_;
|
||||
return table_.bucket_count();
|
||||
}
|
||||
|
||||
size_type max_bucket_count() const BOOST_NOEXCEPT
|
||||
@ -1199,12 +1173,12 @@ namespace boost {
|
||||
|
||||
local_iterator begin(size_type n)
|
||||
{
|
||||
return local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
const_local_iterator begin(size_type n) const
|
||||
{
|
||||
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return const_local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
local_iterator end(size_type) { return local_iterator(); }
|
||||
@ -1216,7 +1190,7 @@ namespace boost {
|
||||
|
||||
const_local_iterator cbegin(size_type n) const
|
||||
{
|
||||
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
|
||||
return const_local_iterator(table_.begin(n));
|
||||
}
|
||||
|
||||
const_local_iterator cend(size_type) const
|
||||
@ -1319,7 +1293,7 @@ namespace boost {
|
||||
unordered_set::value_allocator_traits::
|
||||
select_on_container_copy_construction(other.get_allocator()))
|
||||
{
|
||||
if (other.table_.size_) {
|
||||
if (other.size()) {
|
||||
table_.copy_buckets(
|
||||
other.table_, boost::unordered::detail::true_type());
|
||||
}
|
||||
@ -1483,29 +1457,21 @@ namespace boost {
|
||||
typename unordered_set<T, H, P, A>::iterator
|
||||
unordered_set<T, H, P, A>::erase(const_iterator position)
|
||||
{
|
||||
node_pointer node = table::get_node(position);
|
||||
BOOST_ASSERT(node);
|
||||
node_pointer next = table::next_node(node);
|
||||
table_.erase_nodes_unique(node, next);
|
||||
return iterator(next);
|
||||
return table_.erase_node(position);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
typename unordered_set<T, H, P, A>::size_type
|
||||
unordered_set<T, H, P, A>::erase(const key_type& k)
|
||||
{
|
||||
return table_.erase_key_unique_impl(this->key_eq(), k);
|
||||
return table_.erase_key_unique_impl(k);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
typename unordered_set<T, H, P, A>::iterator
|
||||
unordered_set<T, H, P, A>::erase(const_iterator first, const_iterator last)
|
||||
{
|
||||
node_pointer last_node = table::get_node(last);
|
||||
if (first == last)
|
||||
return iterator(last_node);
|
||||
table_.erase_nodes_unique(table::get_node(first), last_node);
|
||||
return iterator(last_node);
|
||||
return table_.erase_nodes_range(first, last);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1575,7 +1541,7 @@ namespace boost {
|
||||
typename unordered_set<T, H, P, A>::const_iterator
|
||||
unordered_set<T, H, P, A>::find(const key_type& k) const
|
||||
{
|
||||
return const_iterator(table_.find_node(k));
|
||||
return const_iterator(table_.find(k));
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1585,8 +1551,7 @@ namespace boost {
|
||||
unordered_set<T, H, P, A>::find(CompatibleKey const& k,
|
||||
CompatibleHash const& hash, CompatiblePredicate const& eq) const
|
||||
{
|
||||
return const_iterator(
|
||||
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
|
||||
return table_.transparent_find(k, hash, eq);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1601,9 +1566,12 @@ namespace boost {
|
||||
typename unordered_set<T, H, P, A>::const_iterator>
|
||||
unordered_set<T, H, P, A>::equal_range(const key_type& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node(k);
|
||||
return std::make_pair(
|
||||
const_iterator(n), const_iterator(n ? table::next_node(n) : n));
|
||||
iterator first = table_.find(k);
|
||||
iterator second = first;
|
||||
if (second != this->end()) {
|
||||
++second;
|
||||
}
|
||||
return std::make_pair(first, second);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1618,9 +1586,9 @@ namespace boost {
|
||||
template <class T, class H, class P, class A>
|
||||
float unordered_set<T, H, P, A>::load_factor() const BOOST_NOEXCEPT
|
||||
{
|
||||
BOOST_ASSERT(table_.bucket_count_ != 0);
|
||||
BOOST_ASSERT(table_.bucket_count() != 0);
|
||||
return static_cast<float>(table_.size_) /
|
||||
static_cast<float>(table_.bucket_count_);
|
||||
static_cast<float>(table_.bucket_count());
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1638,8 +1606,7 @@ namespace boost {
|
||||
template <class T, class H, class P, class A>
|
||||
void unordered_set<T, H, P, A>::reserve(size_type n)
|
||||
{
|
||||
table_.rehash(static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>(n) / table_.mlf_)));
|
||||
table_.reserve(n);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1883,11 +1850,8 @@ namespace boost {
|
||||
typename unordered_multiset<T, H, P, A>::iterator
|
||||
unordered_multiset<T, H, P, A>::erase(const_iterator position)
|
||||
{
|
||||
node_pointer node = table::get_node(position);
|
||||
BOOST_ASSERT(node);
|
||||
node_pointer next = table::next_node(node);
|
||||
table_.erase_nodes_equiv(node, next);
|
||||
return iterator(next);
|
||||
BOOST_ASSERT(position != this->end());
|
||||
return table_.erase_node(position);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1902,11 +1866,7 @@ namespace boost {
|
||||
unordered_multiset<T, H, P, A>::erase(
|
||||
const_iterator first, const_iterator last)
|
||||
{
|
||||
node_pointer last_node = table::get_node(last);
|
||||
if (first == last)
|
||||
return iterator(last_node);
|
||||
table_.erase_nodes_equiv(table::get_node(first), last_node);
|
||||
return iterator(last_node);
|
||||
return table_.erase_nodes_range(first, last);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1984,7 +1944,7 @@ namespace boost {
|
||||
typename unordered_multiset<T, H, P, A>::const_iterator
|
||||
unordered_multiset<T, H, P, A>::find(const key_type& k) const
|
||||
{
|
||||
return const_iterator(table_.find_node(k));
|
||||
return const_iterator(table_.find(k));
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -1994,16 +1954,14 @@ namespace boost {
|
||||
unordered_multiset<T, H, P, A>::find(CompatibleKey const& k,
|
||||
CompatibleHash const& hash, CompatiblePredicate const& eq) const
|
||||
{
|
||||
return const_iterator(
|
||||
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
|
||||
return table_.transparent_find(k, hash, eq);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
typename unordered_multiset<T, H, P, A>::size_type
|
||||
unordered_multiset<T, H, P, A>::count(const key_type& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node(k);
|
||||
return n ? table_.group_count(n) : 0;
|
||||
return table_.group_count(k);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -2011,9 +1969,9 @@ namespace boost {
|
||||
typename unordered_multiset<T, H, P, A>::const_iterator>
|
||||
unordered_multiset<T, H, P, A>::equal_range(const key_type& k) const
|
||||
{
|
||||
node_pointer n = table_.find_node(k);
|
||||
return std::make_pair(
|
||||
const_iterator(n), const_iterator(n ? table_.next_group(n) : n));
|
||||
iterator n = table_.find(k);
|
||||
return std::make_pair(const_iterator(n),
|
||||
const_iterator(n == end() ? n : table_.next_group(k, n)));
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -2028,9 +1986,9 @@ namespace boost {
|
||||
template <class T, class H, class P, class A>
|
||||
float unordered_multiset<T, H, P, A>::load_factor() const BOOST_NOEXCEPT
|
||||
{
|
||||
BOOST_ASSERT(table_.bucket_count_ != 0);
|
||||
BOOST_ASSERT(table_.bucket_count() != 0);
|
||||
return static_cast<float>(table_.size_) /
|
||||
static_cast<float>(table_.bucket_count_);
|
||||
static_cast<float>(table_.bucket_count());
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -2048,8 +2006,7 @@ namespace boost {
|
||||
template <class T, class H, class P, class A>
|
||||
void unordered_multiset<T, H, P, A>::reserve(size_type n)
|
||||
{
|
||||
table_.rehash(static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>(n) / table_.mlf_)));
|
||||
table_.reserve(n);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -2217,25 +2174,25 @@ namespace boost {
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
template <typename N, typename T, typename A> struct insert_return_type_set
|
||||
template <class Iter, class NodeType> struct insert_return_type_set
|
||||
{
|
||||
private:
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type_set)
|
||||
|
||||
typedef typename boost::unordered::detail::rebind_wrap<A, T>::type
|
||||
value_allocator;
|
||||
typedef N node_;
|
||||
// typedef typename boost::unordered::detail::rebind_wrap<A, T>::type
|
||||
// value_allocator;
|
||||
// typedef N node_;
|
||||
|
||||
public:
|
||||
Iter position;
|
||||
bool inserted;
|
||||
boost::unordered::iterator_detail::c_iterator<node_> position;
|
||||
boost::unordered::node_handle_set<N, T, A> node;
|
||||
NodeType node;
|
||||
|
||||
insert_return_type_set() : inserted(false), position(), node() {}
|
||||
insert_return_type_set() : position(), inserted(false), node() {}
|
||||
|
||||
insert_return_type_set(BOOST_RV_REF(insert_return_type_set)
|
||||
x) BOOST_NOEXCEPT : inserted(x.inserted),
|
||||
position(x.position),
|
||||
x) BOOST_NOEXCEPT : position(x.position),
|
||||
inserted(x.inserted),
|
||||
node(boost::move(x.node))
|
||||
{
|
||||
}
|
||||
@ -2249,9 +2206,9 @@ namespace boost {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename N, typename T, typename A>
|
||||
template <class Iter, class NodeType>
|
||||
void swap(
|
||||
insert_return_type_set<N, T, A>& x, insert_return_type_set<N, T, A>& y)
|
||||
insert_return_type_set<Iter, NodeType>& x, insert_return_type_set<Iter, NodeType>& y)
|
||||
{
|
||||
boost::swap(x.node, y.node);
|
||||
boost::swap(x.inserted, y.inserted);
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright (C) 2008-2011 Daniel James.
|
||||
// Copyright (C) 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -57,7 +58,7 @@ namespace boost {
|
||||
unordered_multiset<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class N, class T, class A> class node_handle_set;
|
||||
template <class N, class T, class A> struct insert_return_type_set;
|
||||
template <class Iter, class NodeType> struct insert_return_type_set;
|
||||
}
|
||||
|
||||
using boost::unordered::unordered_set;
|
||||
|
17
test/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright 2018, 2019 Peter Dimov
|
||||
# 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(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
|
||||
|
||||
if(HAVE_BOOST_TEST)
|
||||
|
||||
boost_test_jamfile(
|
||||
FILE Jamfile.v2
|
||||
LINK_LIBRARIES
|
||||
Boost::unordered
|
||||
Boost::core
|
||||
Boost::concept_check
|
||||
)
|
||||
|
||||
endif()
|
141
test/Jamfile.v2
@ -1,5 +1,6 @@
|
||||
|
||||
# Copyright 2006-2008 Daniel James.
|
||||
# Copyright 2022 Christian Mazakas
|
||||
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -23,88 +24,74 @@ project
|
||||
<toolset>clang:<cxxflags>$(clang-flags)
|
||||
<toolset>msvc:<cxxflags>$(msvc-flags)
|
||||
|
||||
<toolset>gcc-4.4:<cxxflags>-Wno-strict-aliasing
|
||||
<toolset>gcc-4.4:<cxxflags>-fno-deduce-init-list
|
||||
|
||||
<toolset>gcc:<warnings-as-errors>on
|
||||
<toolset>clang:<warnings-as-errors>on
|
||||
<toolset>msvc:<warnings-as-errors>on
|
||||
;
|
||||
|
||||
#alias framework : /boost/test//boost_unit_test_framework ;
|
||||
alias framework : ;
|
||||
run unordered/prime_fmod_tests.cpp ;
|
||||
run unordered/fwd_set_test.cpp ;
|
||||
run unordered/fwd_map_test.cpp ;
|
||||
run unordered/allocator_traits.cpp ;
|
||||
run unordered/minimal_allocator.cpp ;
|
||||
run unordered/compile_set.cpp ;
|
||||
run unordered/compile_map.cpp ;
|
||||
run unordered/noexcept_tests.cpp ;
|
||||
run unordered/link_test_1.cpp unordered/link_test_2.cpp ;
|
||||
run unordered/incomplete_test.cpp ;
|
||||
run unordered/simple_tests.cpp ;
|
||||
run unordered/equivalent_keys_tests.cpp ;
|
||||
run unordered/constructor_tests.cpp ;
|
||||
run unordered/copy_tests.cpp ;
|
||||
run unordered/move_tests.cpp ;
|
||||
run unordered/assign_tests.cpp ;
|
||||
run unordered/insert_tests.cpp ;
|
||||
run unordered/insert_stable_tests.cpp ;
|
||||
run unordered/insert_hint_tests.cpp ;
|
||||
run unordered/emplace_tests.cpp ;
|
||||
run unordered/unnecessary_copy_tests.cpp ;
|
||||
run unordered/erase_tests.cpp : : : <define>BOOST_UNORDERED_SUPPRESS_DEPRECATED ;
|
||||
run unordered/erase_equiv_tests.cpp ;
|
||||
run unordered/extract_tests.cpp ;
|
||||
run unordered/node_handle_tests.cpp ;
|
||||
run unordered/merge_tests.cpp ;
|
||||
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 ;
|
||||
run unordered/find_tests.cpp ;
|
||||
run unordered/at_tests.cpp ;
|
||||
run unordered/bucket_tests.cpp ;
|
||||
run unordered/load_factor_tests.cpp ;
|
||||
run unordered/rehash_tests.cpp ;
|
||||
run unordered/equality_tests.cpp ;
|
||||
run unordered/swap_tests.cpp ;
|
||||
run unordered/deduction_tests.cpp ;
|
||||
run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no ;
|
||||
run unordered/transparent_tests.cpp ;
|
||||
run unordered/reserve_tests.cpp ;
|
||||
run unordered/contains_tests.cpp ;
|
||||
run unordered/erase_if.cpp ;
|
||||
run unordered/scary_tests.cpp ;
|
||||
|
||||
test-suite unordered
|
||||
:
|
||||
[ run unordered/fwd_set_test.cpp ]
|
||||
[ run unordered/fwd_map_test.cpp ]
|
||||
[ run unordered/allocator_traits.cpp ]
|
||||
[ run unordered/minimal_allocator.cpp ]
|
||||
[ run unordered/compile_set.cpp ]
|
||||
[ run unordered/compile_map.cpp ]
|
||||
[ run unordered/noexcept_tests.cpp ]
|
||||
[ run unordered/link_test_1.cpp unordered/link_test_2.cpp ]
|
||||
[ run unordered/incomplete_test.cpp ]
|
||||
[ run unordered/simple_tests.cpp ]
|
||||
[ run unordered/equivalent_keys_tests.cpp ]
|
||||
[ run unordered/constructor_tests.cpp ]
|
||||
[ run unordered/copy_tests.cpp ]
|
||||
[ run unordered/move_tests.cpp ]
|
||||
[ run unordered/assign_tests.cpp ]
|
||||
[ run unordered/insert_tests.cpp ]
|
||||
[ run unordered/insert_stable_tests.cpp ]
|
||||
[ run unordered/insert_hint_tests.cpp ]
|
||||
[ run unordered/emplace_tests.cpp ]
|
||||
[ run unordered/unnecessary_copy_tests.cpp ]
|
||||
[ run unordered/erase_tests.cpp : : : <define>BOOST_UNORDERED_SUPPRESS_DEPRECATED ]
|
||||
[ run unordered/erase_equiv_tests.cpp ]
|
||||
[ run unordered/extract_tests.cpp ]
|
||||
[ run unordered/node_handle_tests.cpp ]
|
||||
[ run unordered/merge_tests.cpp ]
|
||||
[ 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 ]
|
||||
[ run unordered/find_tests.cpp ]
|
||||
[ run unordered/at_tests.cpp ]
|
||||
[ run unordered/bucket_tests.cpp ]
|
||||
[ run unordered/load_factor_tests.cpp ]
|
||||
[ run unordered/rehash_tests.cpp ]
|
||||
[ run unordered/equality_tests.cpp ]
|
||||
[ run unordered/swap_tests.cpp ]
|
||||
[ run unordered/detail_tests.cpp ]
|
||||
[ run unordered/deduction_tests.cpp ]
|
||||
[ run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no ]
|
||||
[ run unordered/transparent_tests.cpp ]
|
||||
[ run unordered/reserve_tests.cpp ]
|
||||
[ run unordered/contains_tests.cpp ]
|
||||
[ run unordered/mix_policy.cpp ]
|
||||
[ run unordered/erase_if.cpp ]
|
||||
run unordered/compile_set.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_set ;
|
||||
run unordered/compile_map.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_map ;
|
||||
run unordered/copy_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_copy ;
|
||||
run unordered/move_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_move ;
|
||||
run unordered/assign_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_assign ;
|
||||
|
||||
[ run unordered/compile_set.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_compile_set ]
|
||||
[ run unordered/compile_map.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_compile_map ]
|
||||
[ run unordered/copy_tests.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_copy ]
|
||||
[ run unordered/move_tests.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_move ]
|
||||
[ run unordered/assign_tests.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
: bmove_assign ]
|
||||
;
|
||||
|
||||
test-suite unordered-exception
|
||||
:
|
||||
[ run exception/constructor_exception_tests.cpp framework ]
|
||||
[ run exception/copy_exception_tests.cpp framework ]
|
||||
[ run exception/assign_exception_tests.cpp framework ]
|
||||
[ run exception/move_assign_exception_tests.cpp framework ]
|
||||
[ run exception/insert_exception_tests.cpp framework ]
|
||||
[ run exception/erase_exception_tests.cpp framework ]
|
||||
[ run exception/rehash_exception_tests.cpp framework ]
|
||||
[ run exception/swap_exception_tests.cpp framework : : :
|
||||
<define>BOOST_UNORDERED_SWAP_METHOD=2 ]
|
||||
[ run exception/merge_exception_tests.cpp framework ]
|
||||
;
|
||||
run exception/constructor_exception_tests.cpp ;
|
||||
run exception/copy_exception_tests.cpp ;
|
||||
run exception/assign_exception_tests.cpp ;
|
||||
run exception/move_assign_exception_tests.cpp ;
|
||||
run exception/insert_exception_tests.cpp ;
|
||||
run exception/erase_exception_tests.cpp ;
|
||||
run exception/rehash_exception_tests.cpp ;
|
||||
run exception/swap_exception_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ;
|
||||
run exception/merge_exception_tests.cpp ;
|
||||
|
||||
run quick.cpp ;
|
||||
|
17
test/cmake_install_test/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright 2018, 2019, 2021 Peter Dimov
|
||||
# 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
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.20)
|
||||
|
||||
project(cmake_install_test LANGUAGES CXX)
|
||||
|
||||
find_package(boost_unordered REQUIRED)
|
||||
|
||||
add_executable(quick ../quick.cpp)
|
||||
target_link_libraries(quick Boost::unordered)
|
||||
|
||||
enable_testing()
|
||||
add_test(quick quick)
|
||||
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)
|
49
test/cmake_subdir_test/CMakeLists.txt
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright 2018, 2019, 2021 Peter Dimov
|
||||
# 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
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.20)
|
||||
|
||||
project(cmake_subdir_test LANGUAGES CXX)
|
||||
|
||||
add_subdirectory(../.. boostorg/unordered)
|
||||
|
||||
# `boostdep --brief unordered`
|
||||
|
||||
set(deps
|
||||
|
||||
# Primary dependencies
|
||||
|
||||
assert
|
||||
config
|
||||
container_hash
|
||||
core
|
||||
move
|
||||
mp11
|
||||
predef
|
||||
preprocessor
|
||||
throw_exception
|
||||
tuple
|
||||
type_traits
|
||||
|
||||
# Secondary dependencies
|
||||
|
||||
detail
|
||||
integer
|
||||
static_assert
|
||||
winapi
|
||||
)
|
||||
|
||||
foreach(dep IN LISTS deps)
|
||||
|
||||
add_subdirectory(../../../${dep} boostorg/${dep})
|
||||
|
||||
endforeach()
|
||||
|
||||
add_executable(quick ../quick.cpp)
|
||||
target_link_libraries(quick Boost::unordered Boost::core)
|
||||
|
||||
enable_testing()
|
||||
add_test(quick quick)
|
||||
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)
|
@ -9,8 +9,8 @@
|
||||
#include "./generators.hpp"
|
||||
#include "./list.hpp"
|
||||
#include "./metafunctions.hpp"
|
||||
#include <boost/type_traits/conditional.hpp>
|
||||
#include <algorithm>
|
||||
#include <boost/detail/select_type.hpp>
|
||||
|
||||
namespace test {
|
||||
template <class X> struct unordered_generator_set
|
||||
@ -69,9 +69,8 @@ namespace test {
|
||||
|
||||
template <class X>
|
||||
struct unordered_generator_base
|
||||
: public boost::detail::if_true<test::is_set<X>::value>::
|
||||
BOOST_NESTED_TEMPLATE then<test::unordered_generator_set<X>,
|
||||
test::unordered_generator_map<X> >
|
||||
: public boost::conditional<test::is_set<X>::value,
|
||||
test::unordered_generator_set<X>, test::unordered_generator_map<X> >
|
||||
{
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright 2006-2011 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -213,25 +214,27 @@ namespace test
|
||||
::operator delete((void*)p);
|
||||
}
|
||||
|
||||
void construct(T* p, T const& t)
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U, class V>
|
||||
void construct(U* p, V const& v)
|
||||
{
|
||||
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
new (p) T(t);
|
||||
detail::tracker.track_construct((void*)p, sizeof(U), tag_);
|
||||
new (p) U(v);
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <typename... Args>
|
||||
void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
#else
|
||||
template <class U, typename... Args>
|
||||
void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
new (p) T(boost::forward<Args>(args)...);
|
||||
detail::tracker.track_construct((void*)p, sizeof(U), tag_);
|
||||
new (p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p)
|
||||
template <class U>
|
||||
void destroy(U* p)
|
||||
{
|
||||
detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
|
||||
p->~T();
|
||||
detail::tracker.track_destroy((void*)p, sizeof(U), tag_);
|
||||
p->~U();
|
||||
}
|
||||
|
||||
size_type max_size() const
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -475,32 +476,34 @@ namespace test {
|
||||
}
|
||||
}
|
||||
|
||||
void construct(pointer p, T const& t)
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U, class Arg>
|
||||
void construct(U* p, Arg const& t)
|
||||
{
|
||||
UNORDERED_SCOPE(allocator::construct(T*, T))
|
||||
UNORDERED_SCOPE(allocator::construct(U*, Arg))
|
||||
{
|
||||
UNORDERED_EPOINT("Mock allocator construct function.");
|
||||
new (p) T(t);
|
||||
new (p) U(t);
|
||||
}
|
||||
test::detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
test::detail::tracker.track_construct((void*)p, sizeof(U), tag_);
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
#else
|
||||
template <class U, class... Args> void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
UNORDERED_SCOPE(allocator::construct(pointer, BOOST_FWD_REF(Args)...))
|
||||
UNORDERED_SCOPE(allocator::construct(U*, BOOST_FWD_REF(Args)...))
|
||||
{
|
||||
UNORDERED_EPOINT("Mock allocator construct function.");
|
||||
new (p) T(boost::forward<Args>(args)...);
|
||||
new (p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
test::detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
test::detail::tracker.track_construct((void*)p, sizeof(U), tag_);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p)
|
||||
template <class U>
|
||||
void destroy(U* p)
|
||||
{
|
||||
test::detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
|
||||
p->~T();
|
||||
test::detail::tracker.track_destroy((void*)p, sizeof(U), tag_);
|
||||
p->~U();
|
||||
}
|
||||
|
||||
size_type max_size() const
|
||||
@ -654,32 +657,35 @@ namespace test {
|
||||
}
|
||||
}
|
||||
|
||||
void construct(pointer p, T const& t)
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U, class V>
|
||||
void construct(U* p, V const& v)
|
||||
{
|
||||
UNORDERED_SCOPE(allocator2::construct(T*, T))
|
||||
UNORDERED_SCOPE(allocator2::construct(U*, V))
|
||||
{
|
||||
UNORDERED_EPOINT("Mock allocator2 construct function.");
|
||||
new (p) T(t);
|
||||
new (p) U(v);
|
||||
}
|
||||
test::detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
test::detail::tracker.track_construct((void*)p, sizeof(U), tag_);
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
#else
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
UNORDERED_SCOPE(allocator2::construct(pointer, BOOST_FWD_REF(Args)...))
|
||||
UNORDERED_SCOPE(allocator2::construct(U*, BOOST_FWD_REF(Args)...))
|
||||
{
|
||||
UNORDERED_EPOINT("Mock allocator2 construct function.");
|
||||
new (p) T(boost::forward<Args>(args)...);
|
||||
new (p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
test::detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
test::detail::tracker.track_construct((void*)p, sizeof(U), tag_);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p)
|
||||
template <class U>
|
||||
void destroy(U* p)
|
||||
{
|
||||
test::detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
|
||||
p->~T();
|
||||
test::detail::tracker.track_destroy((void*)p, sizeof(U), tag_);
|
||||
p->~U();
|
||||
}
|
||||
|
||||
size_type max_size() const
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -10,6 +11,8 @@
|
||||
#if !defined(BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER)
|
||||
#define BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/pointer_traits.hpp>
|
||||
#include <boost/move/move.hpp>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
@ -309,13 +312,17 @@ namespace test {
|
||||
return tmp;
|
||||
}
|
||||
ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); }
|
||||
friend ptr operator+(std::ptrdiff_t s, ptr p)
|
||||
{
|
||||
return ptr<T>(s + p.ptr_);
|
||||
}
|
||||
friend ptr operator+(std::ptrdiff_t s, ptr p) { return ptr<T>(s + p.ptr_); }
|
||||
|
||||
std::ptrdiff_t operator-(ptr p) const { return ptr_ - p.ptr_; }
|
||||
ptr operator-(std::ptrdiff_t s) const { return ptr(ptr_ - s); }
|
||||
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
|
||||
bool operator!() const { return !ptr_; }
|
||||
|
||||
static ptr pointer_to(T& p) {
|
||||
return ptr(&p);
|
||||
}
|
||||
|
||||
// I'm not using the safe bool idiom because the containers should be
|
||||
// able to cope with bool conversions.
|
||||
operator bool() const { return !!ptr_; }
|
||||
@ -428,16 +435,20 @@ namespace test {
|
||||
::operator delete((void*)p.ptr_);
|
||||
}
|
||||
|
||||
void construct(T* p, T const& t) { new ((void*)p) T(t); }
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U, class V> void construct(U* p, V const& v)
|
||||
{
|
||||
new ((void*)p) T(boost::forward<Args>(args)...);
|
||||
new ((void*)p) U(v);
|
||||
}
|
||||
#else
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
new ((void*)p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p) { p->~T(); }
|
||||
template <class U> void destroy(U* p) { p->~U(); }
|
||||
|
||||
size_type max_size() const { return 1000; }
|
||||
|
||||
@ -498,17 +509,20 @@ namespace test {
|
||||
::operator delete((void*)p.ptr_);
|
||||
}
|
||||
|
||||
void construct(T const* p, T const& t) { new ((void*)p) T(t); }
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args>
|
||||
void construct(T const* p, BOOST_FWD_REF(Args)... args)
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U> void construct(U* p, U const& t)
|
||||
{
|
||||
new ((void*)p) T(boost::forward<Args>(args)...);
|
||||
new (p) U(t);
|
||||
}
|
||||
#else
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
new (p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T const* p) { p->~T(); }
|
||||
template <class U> void destroy(U* p) { p->~U(); }
|
||||
|
||||
size_type max_size() const { return 1000; }
|
||||
|
||||
@ -573,16 +587,20 @@ namespace test {
|
||||
|
||||
void deallocate(T* p, std::size_t) { ::operator delete((void*)p); }
|
||||
|
||||
void construct(T* p, T const& t) { new ((void*)p) T(t); }
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U, class V> void construct(U* p, V const& v)
|
||||
{
|
||||
new ((void*)p) T(boost::forward<Args>(args)...);
|
||||
new ((void*)p) U(v);
|
||||
}
|
||||
#else
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
new ((void*)p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p) { p->~T(); }
|
||||
template <class U> void destroy(U* p) { p->~U(); }
|
||||
|
||||
std::size_t max_size() const { return 1000u; }
|
||||
};
|
||||
@ -624,4 +642,14 @@ namespace test {
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
template <> struct pointer_traits< ::test::minimal::void_ptr>
|
||||
{
|
||||
template <class U> struct rebind_to
|
||||
{
|
||||
typedef ::test::minimal::ptr<U> type;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
// Copyright 2006-2009 Daniel James.
|
||||
// Copyright 2022 Christian Mazakas
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -184,7 +185,7 @@ namespace test {
|
||||
};
|
||||
|
||||
// Note: This is a deliberately bad hash function.
|
||||
class hash
|
||||
class hash BOOST_FINAL
|
||||
{
|
||||
int type_;
|
||||
|
||||
@ -289,7 +290,7 @@ namespace test {
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t operator()(int x1, int x2) const { return x1 < x2; }
|
||||
bool operator()(int x1, int x2) const { return x1 < x2; }
|
||||
|
||||
friend bool operator==(less const& x1, less const& x2)
|
||||
{
|
||||
@ -297,7 +298,7 @@ namespace test {
|
||||
}
|
||||
};
|
||||
|
||||
class equal_to
|
||||
class equal_to BOOST_FINAL
|
||||
{
|
||||
int type_;
|
||||
|
||||
@ -330,7 +331,7 @@ namespace test {
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t operator()(int x1, int x2) const { return x1 == x2; }
|
||||
bool operator()(int x1, int x2) const { return x1 == x2; }
|
||||
|
||||
friend bool operator==(equal_to const& x1, equal_to const& x2)
|
||||
{
|
||||
@ -499,11 +500,9 @@ namespace test {
|
||||
friend class const_ptr<T>;
|
||||
friend struct void_ptr;
|
||||
|
||||
T* ptr_;
|
||||
|
||||
ptr(T* x) : ptr_(x) {}
|
||||
|
||||
public:
|
||||
T* ptr_;
|
||||
ptr(T* x) : ptr_(x) {}
|
||||
ptr() : ptr_(0) {}
|
||||
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
|
||||
|
||||
@ -520,11 +519,22 @@ namespace test {
|
||||
++ptr_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); }
|
||||
friend ptr operator+(std::ptrdiff_t s, ptr p) { return ptr<T>(s + p.ptr_); }
|
||||
|
||||
std::ptrdiff_t operator-(ptr p) const { return ptr_ - p.ptr_; }
|
||||
ptr operator-(std::ptrdiff_t s) const { return ptr(ptr_ - s); }
|
||||
|
||||
ptr& operator+=(std::ptrdiff_t s) { ptr_ += s; return *this; }
|
||||
|
||||
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
|
||||
bool operator!() const { return !ptr_; }
|
||||
|
||||
static ptr pointer_to(T& p) {
|
||||
return ptr(&p);
|
||||
}
|
||||
|
||||
// I'm not using the safe bool idiom because the containers should be
|
||||
// able to cope with bool conversions.
|
||||
operator bool() const { return !!ptr_; }
|
||||
@ -646,24 +656,25 @@ namespace test {
|
||||
::operator delete((void*)p.ptr_);
|
||||
}
|
||||
|
||||
void construct(T* p, T const& t)
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U, class V> void construct(U* p, V const& v)
|
||||
{
|
||||
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
new (p) T(t);
|
||||
detail::tracker.track_construct((void*)p, sizeof(U), tag_);
|
||||
new (p) U(v);
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
|
||||
#else
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
|
||||
new (p) T(boost::forward<Args>(args)...);
|
||||
detail::tracker.track_construct((void*)p, sizeof(U), tag_);
|
||||
new (p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
void destroy(T* p)
|
||||
template <class U> void destroy(U* p)
|
||||
{
|
||||
detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
|
||||
p->~T();
|
||||
detail::tracker.track_destroy((void*)p, sizeof(U), tag_);
|
||||
p->~U();
|
||||
}
|
||||
|
||||
size_type max_size() const
|
||||
@ -699,4 +710,14 @@ namespace test {
|
||||
}
|
||||
}
|
||||
|
||||
namespace boost {
|
||||
template <> struct pointer_traits< ::test::void_ptr>
|
||||
{
|
||||
template <class U> struct rebind_to
|
||||
{
|
||||
typedef ::test::ptr<U> type;
|
||||
};
|
||||
};
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
|
19
test/quick.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2022 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
boost::unordered_map<std::string, int> map;
|
||||
|
||||
map[ "2" ] = 2;
|
||||
|
||||
BOOST_TEST_EQ( map[ "1" ], 0 );
|
||||
BOOST_TEST_EQ( map[ "2" ], 2 );
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
|
||||
// Copyright 2017 Daniel James.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// clang-format off
|
||||
#include "../helpers/prefix.hpp"
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include "../helpers/postfix.hpp"
|
||||
// clang-format on
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include <map>
|
||||
|
||||
// Pretty inefficient, but the test is fast enough.
|
||||
// Might be too slow if we had larger primes?
|
||||
bool is_prime(std::size_t x)
|
||||
{
|
||||
if (x == 2) {
|
||||
return true;
|
||||
} else if (x == 1 || x % 2 == 0) {
|
||||
return false;
|
||||
} else {
|
||||
// y*y <= x had rounding errors, so instead use y <= (x/y).
|
||||
for (std::size_t y = 3; y <= (x / y); y += 2) {
|
||||
if (x % y == 0) {
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void test_next_prime(std::size_t value)
|
||||
{
|
||||
std::size_t x = boost::unordered::detail::next_prime(value);
|
||||
BOOST_TEST(is_prime(x));
|
||||
BOOST_TEST(x >= value);
|
||||
}
|
||||
|
||||
void test_prev_prime(std::size_t value)
|
||||
{
|
||||
std::size_t x = boost::unordered::detail::prev_prime(value);
|
||||
BOOST_TEST(is_prime(x));
|
||||
BOOST_TEST(x <= value);
|
||||
if (x > value) {
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << x << "," << value << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (next_prime_test) {
|
||||
BOOST_TEST(!is_prime(0));
|
||||
BOOST_TEST(!is_prime(1));
|
||||
BOOST_TEST(is_prime(2));
|
||||
BOOST_TEST(is_prime(3));
|
||||
BOOST_TEST(is_prime(13));
|
||||
BOOST_TEST(!is_prime(4));
|
||||
BOOST_TEST(!is_prime(100));
|
||||
|
||||
BOOST_TEST(boost::unordered::detail::next_prime(0) > 0);
|
||||
|
||||
// test_prev_prime doesn't work for values less than 17.
|
||||
// Which should be okay, unless an allocator has a really tiny
|
||||
// max_size?
|
||||
const std::size_t min_prime = 17;
|
||||
|
||||
// test_next_prime doesn't work for values greater than this,
|
||||
// which might be a problem if you've got terrabytes of memory?
|
||||
// I seriously doubt the container would work well at such sizes
|
||||
// regardless.
|
||||
const std::size_t max_prime = 4294967291ul;
|
||||
|
||||
std::size_t i;
|
||||
|
||||
BOOST_TEST(is_prime(min_prime));
|
||||
BOOST_TEST(is_prime(max_prime));
|
||||
|
||||
for (i = 0; i < 10000; ++i) {
|
||||
if (i < min_prime) {
|
||||
BOOST_TEST(boost::unordered::detail::prev_prime(i) == min_prime);
|
||||
} else {
|
||||
test_prev_prime(i);
|
||||
}
|
||||
test_next_prime(i);
|
||||
}
|
||||
|
||||
std::size_t last = i - 1;
|
||||
for (; i > last; last = i, i += i / 5) {
|
||||
if (i > max_prime) {
|
||||
BOOST_TEST(boost::unordered::detail::next_prime(i) == max_prime);
|
||||
} else {
|
||||
test_next_prime(i);
|
||||
}
|
||||
test_prev_prime(i);
|
||||
}
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 Christian Mazakas.
|
||||
// Copyright 2021-2022 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -11,9 +11,10 @@
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <string>
|
||||
|
||||
#if BOOST_CXX_VERSION >= 201103L
|
||||
#if !defined(BOOST_NO_CXX11_REF_QUALIFIERS)
|
||||
#define UNORDERED_LVALUE_QUAL &
|
||||
#else
|
||||
#define UNORDERED_LVALUE_QUAL
|
||||
|
@ -1,65 +0,0 @@
|
||||
// Copyright 2022 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/unordered/detail/implementation.hpp>
|
||||
#include <boost/core/bit.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
template<class Policy, class SizeT> void test( SizeT x )
|
||||
{
|
||||
if( x <= 4 )
|
||||
{
|
||||
BOOST_TEST_EQ( Policy::new_bucket_count( x ), 4u );
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_TEST_EQ( Policy::new_bucket_count( x ), boost::core::bit_ceil( x ) );
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ( Policy::prev_bucket_count( x ), boost::core::bit_floor( x ) );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
typedef boost::uint64_t SizeT;
|
||||
typedef boost::unordered::detail::mix64_policy<SizeT> policy;
|
||||
|
||||
for( SizeT i = 1; i < 200; ++i )
|
||||
{
|
||||
test<policy>( i );
|
||||
}
|
||||
|
||||
for( int i = 8; i < 64; ++i )
|
||||
{
|
||||
SizeT x = SizeT( 1 ) << i;
|
||||
|
||||
test<policy>( x - 1 );
|
||||
test<policy>( x );
|
||||
test<policy>( x + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
typedef boost::uint32_t SizeT;
|
||||
typedef boost::unordered::detail::mix32_policy<SizeT> policy;
|
||||
|
||||
for( SizeT i = 1; i < 200; ++i )
|
||||
{
|
||||
test<policy>( i );
|
||||
}
|
||||
|
||||
for( int i = 8; i < 32; ++i )
|
||||
{
|
||||
SizeT x = SizeT( 1 ) << i;
|
||||
|
||||
test<policy>( x - 1 );
|
||||
test<policy>( x );
|
||||
test<policy>( x + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
@ -189,7 +189,8 @@ namespace noexcept_tests {
|
||||
|
||||
#if !defined(BOOST_NO_SFINAE_EXPR) && !defined(BOOST_NO_CXX11_NOEXCEPT) && \
|
||||
!defined(BOOST_NO_CXX11_DECLTYPE) && \
|
||||
!defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS)
|
||||
!defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) && \
|
||||
!BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40700)
|
||||
BOOST_TEST(have_is_nothrow_swap);
|
||||
#endif
|
||||
|
||||
|
215
test/unordered/prime_fmod_tests.cpp
Normal file
@ -0,0 +1,215 @@
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/unordered/detail/prime_fmod.hpp>
|
||||
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
// conditional expression is constant
|
||||
#pragma warning(disable : 4127)
|
||||
#endif
|
||||
|
||||
void macros_test()
|
||||
{
|
||||
if (std::numeric_limits<std::size_t>::digits >= 64) {
|
||||
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
BOOST_ERROR("std::numeric_limits<size_t>::digits >= 64, but "
|
||||
"BOOST_UNORDERED_FCA_HAS_64B_SIZE_T is not defined");
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
BOOST_ERROR("std::numeric_limits<size_t>::digits < 64, but "
|
||||
"BOOST_UNORDERED_FCA_HAS_64B_SIZE_T is defined");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Pretty inefficient, but the test is fast enough.
|
||||
// Might be too slow if we had larger primes?
|
||||
bool is_prime(std::size_t x)
|
||||
{
|
||||
if (x == 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x == 1 || x % 2 == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// y*y <= x is susceptible to overflow, so instead make sure to use y <= (x/y)
|
||||
for (std::size_t y = 3; y <= (x / y); y += 2) {
|
||||
if (x % y == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void prime_sizes_test()
|
||||
{
|
||||
// just some basic sanity checks
|
||||
//
|
||||
BOOST_TEST(!is_prime(0));
|
||||
BOOST_TEST(!is_prime(1));
|
||||
BOOST_TEST(is_prime(2));
|
||||
BOOST_TEST(is_prime(3));
|
||||
BOOST_TEST(is_prime(13));
|
||||
BOOST_TEST(!is_prime(4));
|
||||
BOOST_TEST(!is_prime(100));
|
||||
BOOST_TEST(!is_prime(49));
|
||||
|
||||
std::size_t* sizes = boost::unordered::detail::prime_fmod_size<>::sizes;
|
||||
std::size_t sizes_len =
|
||||
boost::unordered::detail::prime_fmod_size<>::sizes_len;
|
||||
|
||||
// prove every number in our sizes array is prime
|
||||
//
|
||||
BOOST_TEST_GT(sizes_len, 0u);
|
||||
|
||||
for (std::size_t i = 0; i < sizes_len; ++i) {
|
||||
BOOST_TEST(is_prime(sizes[i]));
|
||||
}
|
||||
|
||||
// prove that every subsequent number in the sequence is larger than the
|
||||
// previous
|
||||
//
|
||||
for (std::size_t i = 1; i < sizes_len; ++i) {
|
||||
BOOST_TEST_GT(sizes[i], sizes[i - 1]);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
// now we wish to prove that if we do have the reciprocals stored, we have the
|
||||
// correct amount of them, i.e. one for every entry in sizes[] that fits in 32
|
||||
// bits
|
||||
//
|
||||
boost::uint64_t* inv_sizes32 =
|
||||
boost::unordered::detail::prime_fmod_size<>::inv_sizes32;
|
||||
|
||||
std::size_t inv_sizes32_len =
|
||||
boost::unordered::detail::prime_fmod_size<>::inv_sizes32_len;
|
||||
|
||||
std::size_t count = 0;
|
||||
for (std::size_t i = 0; i < sizes_len; ++i) {
|
||||
if (sizes[i] <= UINT32_MAX) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_TEST_GT(inv_sizes32_len, 0u);
|
||||
BOOST_TEST_EQ(inv_sizes32_len, count);
|
||||
|
||||
// these values should also be monotonically decreasing
|
||||
//
|
||||
for (std::size_t i = 1; i < inv_sizes32_len; ++i) {
|
||||
BOOST_TEST_LT(inv_sizes32[i], inv_sizes32[i - 1]);
|
||||
}
|
||||
|
||||
// now make sure the values in inv_sizes32 are what they should be as derived
|
||||
// from the paper
|
||||
//
|
||||
for (std::size_t i = 0; i < inv_sizes32_len; ++i) {
|
||||
std::size_t const size = sizes[i];
|
||||
BOOST_TEST_LE(size, UINT_MAX);
|
||||
|
||||
boost::uint32_t d = static_cast<boost::uint32_t>(sizes[i]);
|
||||
boost::uint64_t M = ((boost::ulong_long_type(0xffffffff) << 32) +
|
||||
boost::ulong_long_type(0xffffffff)) /
|
||||
d +
|
||||
1;
|
||||
|
||||
BOOST_TEST_EQ(inv_sizes32[i], M);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void get_remainder_test()
|
||||
{
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
struct
|
||||
{
|
||||
// boost::unordered::detail::prime_fmod_size<>::get_remainder
|
||||
// uses several internal implementations depending on the availability of
|
||||
// certain intrinsics or 128 bit integer support, defaulting to a slow,
|
||||
// portable routine. The following is a transcription of the portable
|
||||
// routine used here for verification purposes.
|
||||
//
|
||||
boost::uint64_t operator()(boost::uint64_t f, boost::uint32_t d)
|
||||
{
|
||||
boost::uint64_t r1 = (f & UINT32_MAX) * d;
|
||||
boost::uint64_t r2 = (f >> 32) * d;
|
||||
|
||||
r2 += r1 >> 32;
|
||||
|
||||
return r2 >> 32;
|
||||
}
|
||||
} get_remainder;
|
||||
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for (std::size_t i = 0; i < 1000000u; ++i) {
|
||||
boost::uint64_t f = rng();
|
||||
boost::uint32_t d = static_cast<uint32_t>(rng());
|
||||
|
||||
boost::uint64_t r1 =
|
||||
boost::unordered::detail::prime_fmod_size<>::get_remainder(f, d);
|
||||
|
||||
boost::uint64_t r2 = get_remainder(f, d);
|
||||
|
||||
if (!BOOST_TEST_EQ(r1, r2)) {
|
||||
std::cerr << "f: " << f << ", d: " << d << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void modulo_test()
|
||||
{
|
||||
std::size_t* sizes = boost::unordered::detail::prime_fmod_size<>::sizes;
|
||||
|
||||
std::size_t const sizes_len =
|
||||
boost::unordered::detail::prime_fmod_size<>::sizes_len;
|
||||
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for (std::size_t i = 0; i < 1000000u; ++i) {
|
||||
std::size_t hash = static_cast<std::size_t>(rng());
|
||||
|
||||
for (std::size_t j = 0; j < sizes_len; ++j) {
|
||||
std::size_t h = hash;
|
||||
|
||||
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
|
||||
if (sizes[j] <= UINT_MAX) {
|
||||
h = boost::uint32_t(h) + boost::uint32_t(h >> 32);
|
||||
}
|
||||
#endif
|
||||
std::size_t p1 =
|
||||
boost::unordered::detail::prime_fmod_size<>::position(hash, j);
|
||||
|
||||
std::size_t p2 = h % sizes[j];
|
||||
|
||||
if (!BOOST_TEST_EQ(p1, p2)) {
|
||||
std::cerr << "hash: " << hash << ", j: " << j << ", h: " << h
|
||||
<< ", sizes[" << j << "]: " << sizes[j] << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
macros_test();
|
||||
prime_sizes_test();
|
||||
get_remainder_test();
|
||||
modulo_test();
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 Christian Mazakas.
|
||||
// Copyright 2021-2022 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -20,24 +20,16 @@
|
||||
std::size_t total_allocation = 0;
|
||||
std::size_t num_allocations = 0;
|
||||
|
||||
struct B
|
||||
{
|
||||
B() : i(++count) {}
|
||||
static int count;
|
||||
int i;
|
||||
bool operator==(B const& b) const { return i == b.i; };
|
||||
bool operator!=(B const& b) const { return i != b.i; };
|
||||
};
|
||||
|
||||
int B::count = 0;
|
||||
|
||||
template <typename T> struct A : B
|
||||
template <typename T> struct A
|
||||
{
|
||||
typedef T value_type;
|
||||
|
||||
A() {}
|
||||
static int count;
|
||||
int i;
|
||||
|
||||
template <class U> A(const A<U>&) BOOST_NOEXCEPT {}
|
||||
A() : i(++count) {}
|
||||
|
||||
template <class U> A(const A<U>& a) BOOST_NOEXCEPT : i(a.i) {}
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
@ -51,8 +43,13 @@ template <typename T> struct A : B
|
||||
total_allocation -= n * sizeof(T);
|
||||
std::free(p);
|
||||
}
|
||||
|
||||
bool operator==(A const& a) const { return i == a.i; };
|
||||
bool operator!=(A const& a) const { return i != a.i; };
|
||||
};
|
||||
|
||||
template <class T> int A<T>::count = 0;
|
||||
|
||||
template <class UnorderedContainer> void bucket_count_constructor()
|
||||
{
|
||||
BOOST_TEST_EQ(num_allocations, 0u);
|
||||
@ -152,9 +149,11 @@ template <class UnorderedContainer> void rehash_tests()
|
||||
// no reallocations
|
||||
//
|
||||
std::size_t prev_allocations = num_allocations;
|
||||
std::size_t prev_total_allocation = total_allocation;
|
||||
|
||||
s.rehash(count);
|
||||
BOOST_TEST_EQ(num_allocations, prev_allocations);
|
||||
BOOST_TEST_EQ(total_allocation, prev_total_allocation);
|
||||
|
||||
// prove that when we rehash, exceeding the current bucket count, that we
|
||||
// properly deallocate the current bucket array and then reallocate the
|
||||
@ -174,8 +173,15 @@ template <class UnorderedContainer> void rehash_tests()
|
||||
// note, the test is vulnerable to cases where the next calculated bucket
|
||||
// count can exceed `prev_count + count`
|
||||
//
|
||||
std::size_t const estimated_bucket_group_size =
|
||||
3 * sizeof(void*) + sizeof(std::size_t);
|
||||
std::size_t const estimated_bucket_groups =
|
||||
s.bucket_count() / (sizeof(std::size_t) * 8);
|
||||
|
||||
BOOST_TEST_LT(s.bucket_count(), prev_count + count);
|
||||
BOOST_TEST_LT(total_allocation, (prev_count + count) * sizeof(void*));
|
||||
BOOST_TEST_LE(total_allocation,
|
||||
(prev_count + count) * sizeof(void*) +
|
||||
estimated_bucket_group_size * estimated_bucket_groups);
|
||||
}
|
||||
|
||||
BOOST_TEST_GT(num_allocations, 0u);
|
||||
@ -184,6 +190,30 @@ template <class UnorderedContainer> void rehash_tests()
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (unordered_set_reserve) {
|
||||
{
|
||||
// prove Allocator invariants
|
||||
// from cppref:
|
||||
// Given:
|
||||
// * A, an Allocator type for type T
|
||||
// * B, the corresponding Allocator type for some cv-unqualified object type
|
||||
// U (as obtained by rebinding A)
|
||||
//
|
||||
// Expression:
|
||||
// A a(b)
|
||||
//
|
||||
// Return Type:
|
||||
// Constructs `a` such that `B(a)==b` and `A(b)==a`.
|
||||
// (Note: This implies that all allocators related by rebind maintain each
|
||||
// other's resources, such as memory pools.)
|
||||
//
|
||||
//
|
||||
typedef boost::allocator_rebind<A<int>, float>::type alloc_rebound;
|
||||
alloc_rebound b;
|
||||
A<int> a(b);
|
||||
BOOST_ASSERT(alloc_rebound(a) == b);
|
||||
BOOST_ASSERT(A<int>(b) == a);
|
||||
}
|
||||
|
||||
typedef boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
A<int> >
|
||||
unordered_set;
|
||||
|
326
test/unordered/scary_tests.cpp
Normal file
@ -0,0 +1,326 @@
|
||||
// Copyright 2022 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// clang-format off
|
||||
#include "../helpers/prefix.hpp"
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "../helpers/postfix.hpp"
|
||||
// clang-format on
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct hash1
|
||||
{
|
||||
template <class Key> std::size_t operator()(Key const&) const { return 1337; }
|
||||
};
|
||||
|
||||
struct hash2
|
||||
{
|
||||
template <class Key> std::size_t operator()(Key const&) const { return 7331; }
|
||||
};
|
||||
|
||||
struct equal1
|
||||
{
|
||||
template <class T> bool operator==(T const&) const { return true; }
|
||||
};
|
||||
|
||||
struct equal2
|
||||
{
|
||||
template <class T> bool operator==(T const&) const { return true; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// we define two duplicated allocators here, each one having the same smart
|
||||
// pointer type
|
||||
// we want to prove in our test that different allocators with the same pointers
|
||||
// (either fancy or raw) work equally well for our SCARY iterators
|
||||
//
|
||||
template <class T> struct allocator1
|
||||
{
|
||||
#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
|
||||
public:
|
||||
#else
|
||||
template <class> friend struct allocator1;
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef test::void_ptr void_pointer;
|
||||
typedef test::void_const_ptr const_void_pointer;
|
||||
typedef test::ptr<T> pointer;
|
||||
typedef test::const_ptr<T> const_pointer;
|
||||
typedef T& reference;
|
||||
typedef T const& const_reference;
|
||||
typedef T value_type;
|
||||
|
||||
template <class U> struct rebind
|
||||
{
|
||||
typedef allocator1<U> other;
|
||||
};
|
||||
|
||||
allocator1() {}
|
||||
|
||||
template <class Y> allocator1(allocator1<Y> const&) {}
|
||||
|
||||
allocator1(allocator1 const&) {}
|
||||
|
||||
~allocator1() {}
|
||||
|
||||
pointer address(reference r) { return pointer(&r); }
|
||||
|
||||
const_pointer address(const_reference r) { return const_pointer(&r); }
|
||||
|
||||
pointer allocate(size_type n)
|
||||
{
|
||||
pointer p(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
return p;
|
||||
}
|
||||
|
||||
pointer allocate(size_type n, void const*)
|
||||
{
|
||||
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type)
|
||||
{
|
||||
::operator delete((void*)p.operator->());
|
||||
}
|
||||
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U, class V> void construct(U* p, V const& v) { new (p) U(v); }
|
||||
#else
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
new (p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
// msvc-12.0 and msvc-14.0 seem to eliminate the destructor call as we're only
|
||||
// ever using it with an int with has a trivial destructor so it eliminates
|
||||
// the code and generates a warning about an unused variable
|
||||
//
|
||||
template <class U>
|
||||
void destroy(U* p)
|
||||
{
|
||||
(void)p;
|
||||
p->~U();
|
||||
}
|
||||
|
||||
size_type max_size() const { return (std::numeric_limits<size_type>::max)(); }
|
||||
|
||||
bool operator==(allocator1 const&) const { return true; }
|
||||
bool operator!=(allocator1 const&) const { return false; }
|
||||
|
||||
enum
|
||||
{
|
||||
is_select_on_copy = false,
|
||||
is_propagate_on_swap = false,
|
||||
is_propagate_on_assign = false,
|
||||
is_propagate_on_move = false
|
||||
};
|
||||
};
|
||||
|
||||
template <class T> struct allocator2
|
||||
{
|
||||
#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
|
||||
public:
|
||||
#else
|
||||
template <class> friend struct allocator2;
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef test::void_ptr void_pointer;
|
||||
typedef test::void_const_ptr const_void_pointer;
|
||||
typedef test::ptr<T> pointer;
|
||||
typedef test::const_ptr<T> const_pointer;
|
||||
typedef T& reference;
|
||||
typedef T const& const_reference;
|
||||
typedef T value_type;
|
||||
|
||||
template <class U> struct rebind
|
||||
{
|
||||
typedef allocator2<U> other;
|
||||
};
|
||||
|
||||
allocator2() {}
|
||||
|
||||
template <class Y> allocator2(allocator2<Y> const&) {}
|
||||
|
||||
allocator2(allocator2 const&) {}
|
||||
|
||||
~allocator2() {}
|
||||
|
||||
pointer address(reference r) { return pointer(&r); }
|
||||
|
||||
const_pointer address(const_reference r) { return const_pointer(&r); }
|
||||
|
||||
pointer allocate(size_type n)
|
||||
{
|
||||
pointer p(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
return p;
|
||||
}
|
||||
|
||||
pointer allocate(size_type n, void const*)
|
||||
{
|
||||
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type) { ::operator delete((void*)p.ptr_); }
|
||||
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
template <class U, class V> void construct(U* p, V const& v) { new (p) U(v); }
|
||||
#else
|
||||
template <class U, class... Args>
|
||||
void construct(U* p, BOOST_FWD_REF(Args)... args)
|
||||
{
|
||||
new (p) U(boost::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
// msvc-12.0 and msvc-14.0 seem to eliminate the destructor call as we're only
|
||||
// ever using it with an int with has a trivial destructor so it eliminates
|
||||
// the code and generates a warning about an unused variable
|
||||
//
|
||||
template <class U>
|
||||
void destroy(U* p)
|
||||
{
|
||||
(void)p;
|
||||
p->~U();
|
||||
}
|
||||
|
||||
size_type max_size() const { return (std::numeric_limits<size_type>::max)(); }
|
||||
|
||||
bool operator==(allocator2 const&) const { return true; }
|
||||
bool operator!=(allocator2 const&) const { return false; }
|
||||
|
||||
enum
|
||||
{
|
||||
is_select_on_copy = false,
|
||||
is_propagate_on_swap = false,
|
||||
is_propagate_on_assign = false,
|
||||
is_propagate_on_move = false
|
||||
};
|
||||
};
|
||||
|
||||
template <class C1, class C2> void scary_test()
|
||||
{
|
||||
C1 x;
|
||||
C2 y;
|
||||
|
||||
typename C2::iterator begin(x.begin());
|
||||
BOOST_TEST(begin == x.end());
|
||||
|
||||
typename C2::const_iterator cbegin(x.cbegin());
|
||||
BOOST_TEST(cbegin == x.cend());
|
||||
|
||||
BOOST_ASSERT(x.bucket_count() > 0);
|
||||
|
||||
typename C2::local_iterator lbegin(x.begin(0));
|
||||
BOOST_TEST(lbegin == x.end(0));
|
||||
|
||||
typename C2::const_local_iterator clbegin(x.cbegin(0));
|
||||
BOOST_TEST(clbegin == x.cend(0));
|
||||
}
|
||||
|
||||
template <
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
class Map>
|
||||
void map_scary_test()
|
||||
{
|
||||
typedef std::pair<int const, int> value_type;
|
||||
typedef std::allocator<value_type> std_allocator_type;
|
||||
|
||||
typedef Map<int, int, hash1, std::equal_to<int>, std_allocator_type>
|
||||
hash1_unordered_map;
|
||||
typedef Map<int, int, hash2, std::equal_to<int>, std_allocator_type>
|
||||
hash2_unordered_map;
|
||||
|
||||
typedef Map<int, int, boost::hash<int>, equal1, std_allocator_type>
|
||||
equal1_unordered_map;
|
||||
typedef Map<int, int, boost::hash<int>, equal2, std_allocator_type>
|
||||
equal2_unordered_map;
|
||||
|
||||
// test allocators with a raw pointer as their `::pointer` type
|
||||
//
|
||||
typedef Map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::allocator1<value_type> >
|
||||
alloc1_unordered_map;
|
||||
typedef Map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
std_allocator_type>
|
||||
std_alloc_unordered_map;
|
||||
|
||||
// test allocators with a fancy pointer as their `::pointer` type
|
||||
//
|
||||
typedef Map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
allocator1<value_type> >
|
||||
fancy1_unordered_map;
|
||||
typedef Map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
allocator2<value_type> >
|
||||
fancy2_unordered_map;
|
||||
|
||||
scary_test<alloc1_unordered_map, std_alloc_unordered_map>();
|
||||
scary_test<hash1_unordered_map, hash2_unordered_map>();
|
||||
scary_test<equal1_unordered_map, equal2_unordered_map>();
|
||||
scary_test<fancy1_unordered_map, fancy2_unordered_map>();
|
||||
}
|
||||
|
||||
template <template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class Set>
|
||||
void set_scary_test()
|
||||
{
|
||||
typedef int value_type;
|
||||
typedef std::allocator<value_type> std_allocator_type;
|
||||
|
||||
typedef Set<int, hash1, std::equal_to<int>, std_allocator_type>
|
||||
hash1_unordered_set;
|
||||
typedef Set<int, hash2, std::equal_to<int>, std_allocator_type>
|
||||
hash2_unordered_set;
|
||||
|
||||
typedef Set<int, boost::hash<int>, equal1, std_allocator_type>
|
||||
equal1_unordered_set;
|
||||
typedef Set<int, boost::hash<int>, equal2, std_allocator_type>
|
||||
equal2_unordered_set;
|
||||
|
||||
// test allocators with a raw pointer as their `::pointer` type
|
||||
//
|
||||
typedef Set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::allocator1<value_type> >
|
||||
alloc1_unordered_set;
|
||||
typedef Set<int, boost::hash<int>, std::equal_to<int>, std_allocator_type>
|
||||
std_alloc_unordered_set;
|
||||
|
||||
// test allocators with a fancy pointer as their `::pointer` type
|
||||
//
|
||||
typedef Set<int, boost::hash<int>, std::equal_to<int>,
|
||||
allocator1<value_type> >
|
||||
fancy1_unordered_set;
|
||||
typedef Set<int, boost::hash<int>, std::equal_to<int>,
|
||||
allocator2<value_type> >
|
||||
fancy2_unordered_set;
|
||||
|
||||
scary_test<alloc1_unordered_set, std_alloc_unordered_set>();
|
||||
scary_test<hash1_unordered_set, hash2_unordered_set>();
|
||||
scary_test<equal1_unordered_set, equal2_unordered_set>();
|
||||
scary_test<fancy1_unordered_set, fancy2_unordered_set>();
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (scary_tests) {
|
||||
map_scary_test<boost::unordered_map>();
|
||||
map_scary_test<boost::unordered_multimap>();
|
||||
|
||||
set_scary_test<boost::unordered_set>();
|
||||
set_scary_test<boost::unordered_multiset>();
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
@ -1,12 +1,13 @@
|
||||
|
||||
// Copyright 2021 Christian Mazakas.
|
||||
// Copyright 2021-2022 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
|
||||
#if BOOST_CXX_VERSION <= 199711L
|
||||
#if BOOST_CXX_VERSION <= 199711L || BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE(
|
||||
"scoped allocator adaptor tests only work under C++11 and above")
|
||||
|