forked from boostorg/unordered
Compare commits
298 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33f81fd490 | |||
| a1c156cec1 | |||
| 3d62482fe9 | |||
| 470c9ffed0 | |||
| 49c70046e4 | |||
| ff1b01bd10 | |||
| 5bcb07dc7f | |||
| 35475a260f | |||
| 3d377ec0f3 | |||
| bca33372c2 | |||
| 96696b33c4 | |||
| 5772941057 | |||
| d676ad814b | |||
| 5f9fdb0b15 | |||
| 0f44fd0064 | |||
| ceba60831c | |||
| fd90df5d54 | |||
| 3fe2c29204 | |||
| 55d4aaeef5 | |||
| 71d3b77668 | |||
| 0d3ece98c1 | |||
| 3dc83e4075 | |||
| b57ac04728 | |||
| 3d952d3c0f | |||
| 0897423e69 | |||
| b994ddf894 | |||
| c322cc5621 | |||
| d943283f80 | |||
| 995707a43e | |||
| 107b5e6ab9 | |||
| 2e0fdf7eb4 | |||
| 14ecf54d8a | |||
| c6bdeae570 | |||
| 2d539a9b8f | |||
| 8e1f05082e | |||
| 170d86be9a | |||
| d810b2d073 | |||
| e948bab4d9 | |||
| 7bed1417b9 | |||
| f7eea71b0b | |||
| a0eee06c16 | |||
| b7c013c1e8 | |||
| 1ee2eaf5e9 | |||
| fe55012007 | |||
| 42eb31e7e1 | |||
| 83423adc05 | |||
| b019f17590 | |||
| e58ba2e044 | |||
| 79ca8e968c | |||
| 5a095c3771 | |||
| 65094532eb | |||
| 13caa6691c | |||
| bcd1770a46 | |||
| aa96d87502 | |||
| d20be2aaf8 | |||
| d2ded394f6 | |||
| 2b8f458a38 | |||
| e3a7ec6aed | |||
| 93f9fd7206 | |||
| 28915fdce0 | |||
| 497455d281 | |||
| c758cbda5e | |||
| 4655133843 | |||
| 672a97cb34 | |||
| 0f8cc79c00 | |||
| e2b6865938 | |||
| bf0bc6468a | |||
| 526bf15c3c | |||
| bdfb0e3e25 | |||
| 2d6ebf16d8 | |||
| 13c62043eb | |||
| 39d60cd91d | |||
| aaf79c5202 | |||
| 884c790009 | |||
| 5e5dbf5984 | |||
| 0794cfec9e | |||
| a878374d28 | |||
| 120861bf55 | |||
| b7514e1e04 | |||
| da390e3959 | |||
| 3062759ca8 | |||
| cb4b417f76 | |||
| ef951094b3 | |||
| b14aefa1d3 | |||
| aa7c11a873 | |||
| b871699103 | |||
| 8b946ec36d | |||
| da73e1eda9 | |||
| cd8716400b | |||
| 854ab0b151 | |||
| c1c98e16d3 | |||
| 1ee99268f1 | |||
| 403ed3abaf | |||
| de2ae678a9 | |||
| 45c92568a1 | |||
| 2f455409e2 | |||
| 55bdde560a | |||
| 4e249125eb | |||
| 1f0ba0198b | |||
| 145ccc77d6 | |||
| 1cb0908961 | |||
| 1db7fbad66 | |||
| 7d79b35f93 | |||
| 2f331b7a8b | |||
| d96d5335b4 | |||
| 4c2150fb3d | |||
| 2751b3515b | |||
| 76b36a81ca | |||
| 3eb244898f | |||
| 7aacf9836c | |||
| 24eeb67275 | |||
| bf86730a62 | |||
| 98494420c5 | |||
| 7717ff01a1 | |||
| 5c3576c7c6 | |||
| d6576ed2f1 | |||
| 9a61c8f8dd | |||
| d192ec8fae | |||
| fe913577f6 | |||
| 312d00cc33 | |||
| c3ac504c10 | |||
| 5d94f0eea6 | |||
| 97734fd895 | |||
| 596e1ce135 | |||
| e1c58b4584 | |||
| f5d470c531 | |||
| a87277c6e8 | |||
| 6700ecaf43 | |||
| ad8a11bb49 | |||
| ce2051ed39 | |||
| d16989ce55 | |||
| a26e1c0f41 | |||
| 510267f6e9 | |||
| 8e6a5e19c2 | |||
| 21244ab832 | |||
| 7a64f1634f | |||
| 2d8268d3d0 | |||
| f6b96e4984 | |||
| 7fd972d669 | |||
| 42190df874 | |||
| 19673e3b1c | |||
| b6b334dd16 | |||
| a8443abe80 | |||
| 49b630c2d4 | |||
| cd56cae032 | |||
| 6c74aa0289 | |||
| 8ce147dcbd | |||
| 7f51c8dba4 | |||
| 7c2ba681e9 | |||
| 1c459e6ee6 | |||
| f6a077e102 | |||
| b797862a91 | |||
| abc7327116 | |||
| 7c58a8247c | |||
| 263150e599 | |||
| 7a177d6ac0 | |||
| d5e5c08b87 | |||
| c485bc975a | |||
| 2dfdaca3eb | |||
| 56f11f94d8 | |||
| ccbe691cc8 | |||
| f8b53c1cf7 | |||
| c920354423 | |||
| 1ab8cc4c0f | |||
| 3aa62a821a | |||
| ecf76830a5 | |||
| ce6ca0cf9d | |||
| 202a438044 | |||
| d7ffd48c67 | |||
| 7440e7f789 | |||
| f813bbdf86 | |||
| 2656bfbcac | |||
| dc95efea1a | |||
| 81e7e4dd81 | |||
| 57a2b65488 | |||
| b23e47c478 | |||
| ff4ca3098b | |||
| b6f8363023 | |||
| 9c07cf60a6 | |||
| 36324af017 | |||
| 31392ce1aa | |||
| 6cf039eecc | |||
| 79ab9800c0 | |||
| 05373cbb6b | |||
| 1b009da4d0 | |||
| b39b6b7635 | |||
| c2d3713f40 | |||
| c4345c809e | |||
| c761934868 | |||
| 77c4a09a9b | |||
| 58326b8fff | |||
| ee5d4b9e73 | |||
| e667e6dbd9 | |||
| ec288246d0 | |||
| 71c332803a | |||
| 5e30830cb9 | |||
| 57054f7451 | |||
| 1c6c085127 | |||
| ff4d25d454 | |||
| 85cb09ae6d | |||
| 854a5aa3c3 | |||
| bde33a1d6a | |||
| 8d98d8752b | |||
| cebeb4ea5f | |||
| a3a27a9a6c | |||
| 973c72bdf0 | |||
| 7bdd180c30 | |||
| 19d2fe8738 | |||
| 3d5a2d26d1 | |||
| 4e37a14bf8 | |||
| 91500344d4 | |||
| d8fe1a17cc | |||
| 9945ce7583 | |||
| 54d36f89ea | |||
| 193cf30780 | |||
| ab8c09fcb9 | |||
| 1db53ba155 | |||
| b41bb5c595 | |||
| c7676755ab | |||
| 4f88b3865f | |||
| 089d2db104 | |||
| fefb6ad4c4 | |||
| 05b795bc14 | |||
| ff3f5067c8 | |||
| 0c54f60e17 | |||
| 82b33708ba | |||
| 5b8289c05a | |||
| ea5cabb27f | |||
| a6b9fb285c | |||
| 4041d06e95 | |||
| 13cd5aa4ce | |||
| 00b504ebc5 | |||
| afb83a6cb9 | |||
| f5b03fb2e8 | |||
| b8d3aa2a68 | |||
| 52f154ec02 | |||
| e4d0693eb9 | |||
| 4a42c93897 | |||
| 8b438dea76 | |||
| 12977a50bc | |||
| 33f84624ec | |||
| f252480bee | |||
| c9df887c4c | |||
| 03edf7f4a8 | |||
| a98a719546 | |||
| a97483b928 | |||
| 9955886ef5 | |||
| 3646a7143e | |||
| 13f40e4333 | |||
| 6249660e1f | |||
| 3eb2d3c4b3 | |||
| 8f1fc75fdf | |||
| d3c37344f0 | |||
| 5e8b6a9e55 | |||
| bbd0eedb5f | |||
| ad51b34438 | |||
| 0d4b753409 | |||
| 6f5727cbdb | |||
| 10e88d07af | |||
| 3f1e4a703a | |||
| bc9eca70d0 | |||
| 69b882a14b | |||
| 6984e6a4f2 | |||
| fd0cab2ab8 | |||
| 93216374ef | |||
| 8ba710637a | |||
| 937c3484cf | |||
| 59db6cf788 | |||
| f41b3e8295 | |||
| fe439890e8 | |||
| e29f762116 | |||
| c8abaf32ee | |||
| c0a9f638ce | |||
| 4a90ae5b0f | |||
| 7ccd62ba98 | |||
| 2e1ef850e3 | |||
| dbba786a35 | |||
| d0d4be9e35 | |||
| 2d69c7a5ca | |||
| 24a38922bd | |||
| 1e553df5b6 | |||
| 0f37f774f1 | |||
| 34c07ea148 | |||
| 70fca4483e | |||
| b2b017accb | |||
| a97160cf57 | |||
| 1d42f5b7b1 | |||
| 70ac0509df | |||
| f1678399af | |||
| bae1f8ca82 | |||
| 790c33d6a7 | |||
| a9f5da7799 | |||
| 6a59e6db39 | |||
| 76a44cff09 | |||
| e36dce52ba | |||
| c31ace5fc8 | |||
| 0f9f3eba72 | |||
| 0d033679d4 |
+36
-11
@@ -6,27 +6,52 @@ version: 1.0.{build}-{branch}
|
||||
|
||||
shallow_clone: true
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
- /feature\/.*/
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
TOOLSET: msvc-10.0,msvc-11.0,msvc-12.0,msvc-14.0
|
||||
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: msvc-14.2
|
||||
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
|
||||
|
||||
install:
|
||||
- set BOOST_ROOT=c:\projects\boost
|
||||
- cd c:\projects\
|
||||
- python %APPVEYOR_BUILD_FOLDER%\ci\download-boost-snapshot.py master
|
||||
- rd /s /q %BOOST_ROOT%\boost\unordered
|
||||
- cd %BOOST_ROOT%\tools\build
|
||||
- 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
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- echo. 2>Jamroot.jam
|
||||
- b2 -d0 headers
|
||||
|
||||
build: off
|
||||
|
||||
test_script:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\test
|
||||
- cmd /c %BOOST_ROOT%\tools\build\b2 -j 3 toolset=%TOOLSET% include=%APPVEYOR_BUILD_FOLDER%\include include=%BOOST_ROOT%
|
||||
- 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
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- feature/**
|
||||
|
||||
env:
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
|
||||
jobs:
|
||||
posix:
|
||||
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
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
|
||||
- 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
|
||||
|
||||
- name: Create user-config.jam
|
||||
if: matrix.compiler
|
||||
run: |
|
||||
echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam
|
||||
|
||||
- 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') || ''}}
|
||||
|
||||
windows:
|
||||
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
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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
|
||||
|
||||
- name: Run tests
|
||||
shell: cmd
|
||||
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
|
||||
@@ -0,0 +1,411 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.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
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
|
||||
{
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
|
||||
|
||||
t1 = t2;
|
||||
}
|
||||
|
||||
constexpr unsigned N = 2'000'000;
|
||||
constexpr int K = 10;
|
||||
|
||||
static std::vector<std::string> indices1, indices2;
|
||||
|
||||
static std::string make_index( unsigned x )
|
||||
{
|
||||
char buffer[ 64 ];
|
||||
std::snprintf( buffer, sizeof(buffer), "pfx_%u_sfx", x );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static std::string make_random_index( unsigned x )
|
||||
{
|
||||
char buffer[ 64 ];
|
||||
std::snprintf( buffer, sizeof(buffer), "pfx_%0*d_%u_sfx", x % 8 + 1, 0, x );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void init_indices()
|
||||
{
|
||||
indices1.reserve( N*2+1 );
|
||||
indices1.push_back( make_index( 0 ) );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices1.push_back( make_index( i ) );
|
||||
}
|
||||
|
||||
indices2.reserve( N*2+1 );
|
||||
indices2.push_back( make_index( 0 ) );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices2.push_back( make_random_index( static_cast<std::uint32_t>( rng() ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices1[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices2[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Random insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::uint32_t s;
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices1[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices2[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
auto it = map.begin();
|
||||
|
||||
while( it != map.end() )
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// counting allocator
|
||||
|
||||
static std::size_t s_alloc_bytes = 0;
|
||||
static std::size_t s_alloc_count = 0;
|
||||
|
||||
template<class T> struct allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
allocator() = default;
|
||||
|
||||
template<class U> allocator( allocator<U> const & ) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<class U> bool operator==( allocator<U> const & ) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U> bool operator!=( allocator<U> const& ) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate( std::size_t n ) const
|
||||
{
|
||||
s_alloc_bytes += n * sizeof(T);
|
||||
s_alloc_count++;
|
||||
|
||||
return std::allocator<T>().allocate( n );
|
||||
}
|
||||
|
||||
void deallocate( T* p, std::size_t n ) const noexcept
|
||||
{
|
||||
s_alloc_bytes -= n * sizeof(T);
|
||||
s_alloc_count--;
|
||||
|
||||
std::allocator<T>().deallocate( p, n );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
std::size_t bytes_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
s_alloc_bytes = 0;
|
||||
s_alloc_count = 0;
|
||||
|
||||
Map<std::string, std::uint32_t> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
|
||||
|
||||
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
|
||||
|
||||
test_lookup( map, t1 );
|
||||
test_iteration( map, t1 );
|
||||
test_lookup( map, t1 );
|
||||
test_erase( map, t1 );
|
||||
|
||||
auto tN = std::chrono::steady_clock::now();
|
||||
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
|
||||
|
||||
rec.time_ = ( tN - t0 ) / 1ms;
|
||||
times.push_back( rec );
|
||||
}
|
||||
|
||||
// multi_index emulation of unordered_map
|
||||
|
||||
template<class K, class V> struct pair
|
||||
{
|
||||
K first;
|
||||
mutable V second;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
template<class K, class V> using multi_index_map = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
// aliases using the counting allocator
|
||||
|
||||
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
|
||||
|
||||
template<class K, class V> using std_unordered_map =
|
||||
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map =
|
||||
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map =
|
||||
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
// fnv1a_hash
|
||||
|
||||
template<int Bits> struct fnv1a_hash_impl;
|
||||
|
||||
template<> struct fnv1a_hash_impl<32>
|
||||
{
|
||||
std::size_t operator()( std::string const& s ) const
|
||||
{
|
||||
std::size_t h = 0x811C9DC5u;
|
||||
|
||||
char const * first = s.data();
|
||||
char const * last = first + s.size();
|
||||
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( *first );
|
||||
h *= 0x01000193ul;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct fnv1a_hash_impl<64>
|
||||
{
|
||||
std::size_t operator()( std::string const& s ) const
|
||||
{
|
||||
std::size_t h = 0xCBF29CE484222325ull;
|
||||
|
||||
char const * first = s.data();
|
||||
char const * last = first + s.size();
|
||||
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( *first );
|
||||
h *= 0x00000100000001B3ull;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits > {};
|
||||
|
||||
template<class K, class V> using std_unordered_map_fnv1a =
|
||||
std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_map_fnv1a =
|
||||
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using multi_index_map_fnv1a = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first>, fnv1a_hash >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map_fnv1a =
|
||||
absl::node_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map_fnv1a =
|
||||
absl::flat_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
#if 0
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<multi_index_map>( "multi_index_map" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
|
||||
test<multi_index_map_fnv1a>( "multi_index_map, FNV-1a" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
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
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/internal/raw_hash_set.cc"
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
@@ -0,0 +1,412 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.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
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
|
||||
{
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
|
||||
|
||||
t1 = t2;
|
||||
}
|
||||
|
||||
constexpr unsigned N = 2'000'000;
|
||||
constexpr int K = 10;
|
||||
|
||||
static std::vector<std::string> indices1, indices2;
|
||||
|
||||
static std::string make_index( unsigned x )
|
||||
{
|
||||
char buffer[ 64 ];
|
||||
std::snprintf( buffer, sizeof(buffer), "pfx_%u_sfx", x );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static std::string make_random_index( unsigned x )
|
||||
{
|
||||
char buffer[ 64 ];
|
||||
std::snprintf( buffer, sizeof(buffer), "pfx_%0*d_%u_sfx", x % 8 + 1, 0, x );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void init_indices()
|
||||
{
|
||||
indices1.reserve( N*2+1 );
|
||||
indices1.push_back( make_index( 0 ) );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices1.push_back( make_index( i ) );
|
||||
}
|
||||
|
||||
indices2.reserve( N*2+1 );
|
||||
indices2.push_back( make_index( 0 ) );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices2.push_back( make_random_index( static_cast<std::uint32_t>( rng() ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices1[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices2[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Random insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::uint32_t s;
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices1[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices2[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
auto it = map.begin();
|
||||
|
||||
while( it != map.end() )
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// counting allocator
|
||||
|
||||
static std::size_t s_alloc_bytes = 0;
|
||||
static std::size_t s_alloc_count = 0;
|
||||
|
||||
template<class T> struct allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
allocator() = default;
|
||||
|
||||
template<class U> allocator( allocator<U> const & ) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<class U> bool operator==( allocator<U> const & ) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U> bool operator!=( allocator<U> const& ) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate( std::size_t n ) const
|
||||
{
|
||||
s_alloc_bytes += n * sizeof(T);
|
||||
s_alloc_count++;
|
||||
|
||||
return std::allocator<T>().allocate( n );
|
||||
}
|
||||
|
||||
void deallocate( T* p, std::size_t n ) const noexcept
|
||||
{
|
||||
s_alloc_bytes -= n * sizeof(T);
|
||||
s_alloc_count--;
|
||||
|
||||
std::allocator<T>().deallocate( p, n );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
std::size_t bytes_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
s_alloc_bytes = 0;
|
||||
s_alloc_count = 0;
|
||||
|
||||
Map<std::string_view, std::uint32_t> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
|
||||
|
||||
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
|
||||
|
||||
test_lookup( map, t1 );
|
||||
test_iteration( map, t1 );
|
||||
test_lookup( map, t1 );
|
||||
test_erase( map, t1 );
|
||||
|
||||
auto tN = std::chrono::steady_clock::now();
|
||||
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
|
||||
|
||||
rec.time_ = ( tN - t0 ) / 1ms;
|
||||
times.push_back( rec );
|
||||
}
|
||||
|
||||
// multi_index emulation of unordered_map
|
||||
|
||||
template<class K, class V> struct pair
|
||||
{
|
||||
K first;
|
||||
mutable V second;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
template<class K, class V> using multi_index_map = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
// aliases using the counting allocator
|
||||
|
||||
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
|
||||
|
||||
template<class K, class V> using std_unordered_map =
|
||||
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map =
|
||||
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map =
|
||||
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
// fnv1a_hash
|
||||
|
||||
template<int Bits> struct fnv1a_hash_impl;
|
||||
|
||||
template<> struct fnv1a_hash_impl<32>
|
||||
{
|
||||
std::size_t operator()( std::string_view const& s ) const
|
||||
{
|
||||
std::size_t h = 0x811C9DC5u;
|
||||
|
||||
char const * first = s.data();
|
||||
char const * last = first + s.size();
|
||||
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( *first );
|
||||
h *= 0x01000193ul;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct fnv1a_hash_impl<64>
|
||||
{
|
||||
std::size_t operator()( std::string_view const& s ) const
|
||||
{
|
||||
std::size_t h = 0xCBF29CE484222325ull;
|
||||
|
||||
char const * first = s.data();
|
||||
char const * last = first + s.size();
|
||||
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( *first );
|
||||
h *= 0x00000100000001B3ull;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits > {};
|
||||
|
||||
template<class K, class V> using std_unordered_map_fnv1a =
|
||||
std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_map_fnv1a =
|
||||
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using multi_index_map_fnv1a = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first>, fnv1a_hash >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map_fnv1a =
|
||||
absl::node_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map_fnv1a =
|
||||
absl::flat_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
#if 0
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<multi_index_map>( "multi_index_map" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
|
||||
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
|
||||
test<multi_index_map_fnv1a>( "multi_index_map, FNV-1a" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
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
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/internal/raw_hash_set.cc"
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
@@ -0,0 +1,342 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.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
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
|
||||
{
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
|
||||
|
||||
t1 = t2;
|
||||
}
|
||||
|
||||
constexpr unsigned N = 2'000'000;
|
||||
constexpr int K = 10;
|
||||
|
||||
static std::vector< std::uint32_t > indices1, indices2, indices3;
|
||||
|
||||
static void init_indices()
|
||||
{
|
||||
indices1.push_back( 0 );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices1.push_back( i );
|
||||
}
|
||||
|
||||
indices2.push_back( 0 );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices2.push_back( static_cast<std::uint32_t>( rng() ) );
|
||||
}
|
||||
}
|
||||
|
||||
indices3.push_back( 0 );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices3.push_back( (std::uint32_t)i << 11 );
|
||||
}
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices1[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices2[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Random insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices3[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::uint32_t s;
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices1[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices2[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices3[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
auto it = map.begin();
|
||||
|
||||
while( it != map.end() )
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices3[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// counting allocator
|
||||
|
||||
static std::size_t s_alloc_bytes = 0;
|
||||
static std::size_t s_alloc_count = 0;
|
||||
|
||||
template<class T> struct allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
allocator() = default;
|
||||
|
||||
template<class U> allocator( allocator<U> const & ) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<class U> bool operator==( allocator<U> const & ) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U> bool operator!=( allocator<U> const& ) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate( std::size_t n ) const
|
||||
{
|
||||
s_alloc_bytes += n * sizeof(T);
|
||||
s_alloc_count++;
|
||||
|
||||
return std::allocator<T>().allocate( n );
|
||||
}
|
||||
|
||||
void deallocate( T* p, std::size_t n ) const noexcept
|
||||
{
|
||||
s_alloc_bytes -= n * sizeof(T);
|
||||
s_alloc_count--;
|
||||
|
||||
std::allocator<T>().deallocate( p, n );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
std::size_t bytes_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
s_alloc_bytes = 0;
|
||||
s_alloc_count = 0;
|
||||
|
||||
Map<std::uint32_t, std::uint32_t> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
|
||||
|
||||
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
|
||||
|
||||
test_lookup( map, t1 );
|
||||
test_iteration( map, t1 );
|
||||
test_lookup( map, t1 );
|
||||
test_erase( map, t1 );
|
||||
|
||||
auto tN = std::chrono::steady_clock::now();
|
||||
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
|
||||
|
||||
rec.time_ = ( tN - t0 ) / 1ms;
|
||||
times.push_back( rec );
|
||||
}
|
||||
|
||||
// multi_index emulation of unordered_map
|
||||
|
||||
template<class K, class V> struct pair
|
||||
{
|
||||
K first;
|
||||
mutable V second;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
template<class K, class V> using multi_index_map = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
// aliases using the counting allocator
|
||||
|
||||
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
|
||||
|
||||
template<class K, class V> using std_unordered_map =
|
||||
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map =
|
||||
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map =
|
||||
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<multi_index_map>( "multi_index_map" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 25 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/internal/raw_hash_set.cc"
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
@@ -0,0 +1,342 @@
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.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
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint64_t s, std::size_t size )
|
||||
{
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
|
||||
|
||||
t1 = t2;
|
||||
}
|
||||
|
||||
constexpr unsigned N = 2'000'000;
|
||||
constexpr int K = 10;
|
||||
|
||||
static std::vector< std::uint64_t > indices1, indices2, indices3;
|
||||
|
||||
static void init_indices()
|
||||
{
|
||||
indices1.push_back( 0 );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices1.push_back( i );
|
||||
}
|
||||
|
||||
indices2.push_back( 0 );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices2.push_back( rng() );
|
||||
}
|
||||
}
|
||||
|
||||
indices3.push_back( 0 );
|
||||
|
||||
for( unsigned i = 1; i <= N*2; ++i )
|
||||
{
|
||||
indices3.push_back( (std::uint64_t)i << 40 );
|
||||
}
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices1[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices2[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Random insert", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.insert( { indices3[ i ], i } );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted insert", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::uint64_t s;
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices1[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices2[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random lookup", s, map.size() );
|
||||
|
||||
s = 0;
|
||||
|
||||
for( int j = 0; j < K; ++j )
|
||||
{
|
||||
for( unsigned i = 1; i <= N * 2; ++i )
|
||||
{
|
||||
auto it = map.find( indices3[ i ] );
|
||||
if( it != map.end() ) s += it->second;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted lookup", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
auto it = map.begin();
|
||||
|
||||
while( it != map.end() )
|
||||
{
|
||||
if( it->second & 1 )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
{
|
||||
boost::detail::splitmix64 rng;
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices3[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive shifted erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// counting allocator
|
||||
|
||||
static std::size_t s_alloc_bytes = 0;
|
||||
static std::size_t s_alloc_count = 0;
|
||||
|
||||
template<class T> struct allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
allocator() = default;
|
||||
|
||||
template<class U> allocator( allocator<U> const & ) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<class U> bool operator==( allocator<U> const & ) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U> bool operator!=( allocator<U> const& ) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate( std::size_t n ) const
|
||||
{
|
||||
s_alloc_bytes += n * sizeof(T);
|
||||
s_alloc_count++;
|
||||
|
||||
return std::allocator<T>().allocate( n );
|
||||
}
|
||||
|
||||
void deallocate( T* p, std::size_t n ) const noexcept
|
||||
{
|
||||
s_alloc_bytes -= n * sizeof(T);
|
||||
s_alloc_count--;
|
||||
|
||||
std::allocator<T>().deallocate( p, n );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
std::size_t bytes_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
s_alloc_bytes = 0;
|
||||
s_alloc_count = 0;
|
||||
|
||||
Map<std::uint64_t, std::uint64_t> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
|
||||
|
||||
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
|
||||
|
||||
test_lookup( map, t1 );
|
||||
test_iteration( map, t1 );
|
||||
test_lookup( map, t1 );
|
||||
test_erase( map, t1 );
|
||||
|
||||
auto tN = std::chrono::steady_clock::now();
|
||||
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
|
||||
|
||||
rec.time_ = ( tN - t0 ) / 1ms;
|
||||
times.push_back( rec );
|
||||
}
|
||||
|
||||
// multi_index emulation of unordered_map
|
||||
|
||||
template<class K, class V> struct pair
|
||||
{
|
||||
K first;
|
||||
mutable V second;
|
||||
};
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
template<class K, class V> using multi_index_map = multi_index_container<
|
||||
pair<K, V>,
|
||||
indexed_by<
|
||||
hashed_unique< member<pair<K, V>, K, &pair<K, V>::first> >
|
||||
>,
|
||||
::allocator< pair<K, V> >
|
||||
>;
|
||||
|
||||
// aliases using the counting allocator
|
||||
|
||||
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
|
||||
|
||||
template<class K, class V> using std_unordered_map =
|
||||
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using boost_unordered_map =
|
||||
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
template<class K, class V> using absl_node_hash_map =
|
||||
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
template<class K, class V> using absl_flat_hash_map =
|
||||
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
|
||||
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
test<std_unordered_map>( "std::unordered_map" );
|
||||
test<boost_unordered_map>( "boost::unordered_map" );
|
||||
test<multi_index_map>( "multi_index_map" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test<absl_node_hash_map>( "absl::node_hash_map" );
|
||||
test<absl_flat_hash_map>( "absl::flat_hash_map" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 25 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/container/internal/raw_hash_set.cc"
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
+9
-60
@@ -1,72 +1,21 @@
|
||||
|
||||
# Copyright 2005 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)
|
||||
|
||||
using boostbook ;
|
||||
using quickbook ;
|
||||
import asciidoctor ;
|
||||
|
||||
path-constant images_location : ../ ;
|
||||
path-constant admonishment_location : ../../../../doc/src/images ;
|
||||
html unordered.html : unordered.adoc ;
|
||||
|
||||
xml unordered : unordered.qbk :
|
||||
<xsl:param>generate.consistent.ids=1 ;
|
||||
install html_ : unordered.html : <location>html ;
|
||||
|
||||
boostbook standalone : unordered :
|
||||
<xsl:param>chunk.first.sections=1
|
||||
<xsl:param>chunk.section.depth=2
|
||||
<xsl:param>generate.section.toc.level=2
|
||||
<xsl:param>toc.section.depth=1
|
||||
<xsl:param>toc.max.depth=1
|
||||
pdf unordered.pdf : unordered.adoc ;
|
||||
explicit unordered.pdf ;
|
||||
|
||||
<xsl:param>boost.compact.typedef=0
|
||||
<xsl:param>boost.compact.function=0
|
||||
<xsl:param>boost.compact.enum=0
|
||||
|
||||
<xsl:param>generate.consistent.ids=1
|
||||
|
||||
# HTML Options:
|
||||
|
||||
<format>html:<xsl:param>boost.root=../../../..
|
||||
<format>html:<xsl:param>img.src.path=../../../../doc/html/
|
||||
<format>xhtml:<xsl:param>boost.root=../../../..
|
||||
<format>xhtml:<xsl:param>img.src.path=../../../../doc/html/
|
||||
|
||||
# PDF Options:
|
||||
|
||||
# TOC Generation: this is needed for FOP-0.9 and later:
|
||||
<xsl:param>fop1.extensions=0
|
||||
<format>pdf:<xsl:param>xep.extensions=1
|
||||
# TOC generation: this is needed for FOP 0.2, but must not be set to zero for FOP-0.9!
|
||||
<format>pdf:<xsl:param>fop.extensions=0
|
||||
# No indent on body text:
|
||||
<format>pdf:<xsl:param>body.start.indent=0pt
|
||||
# Margin size:
|
||||
<format>pdf:<xsl:param>page.margin.inner=0.5in
|
||||
# Margin size:
|
||||
<format>pdf:<xsl:param>page.margin.outer=0.5in
|
||||
# Paper type = A4
|
||||
<format>pdf:<xsl:param>paper.type=A4
|
||||
# Yes, we want graphics for admonishments:
|
||||
<xsl:param>admon.graphics=1
|
||||
# Set this one for PDF generation *only*:
|
||||
# default png graphics are awful in PDF form,
|
||||
# better use SVG's instead:
|
||||
<format>pdf:<xsl:param>admon.graphics.extension=".svg"
|
||||
<format>pdf:<xsl:param>use.role.for.mediaobject=1
|
||||
<format>pdf:<xsl:param>preferred.mediaobject.role=print
|
||||
<format>pdf:<xsl:param>img.src.path=$(images_location)/
|
||||
#<format>pdf:<xsl:param>admon.graphics.path=$(admonishment_location)
|
||||
<format>pdf:<xsl:param>draft.mode="no"
|
||||
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/libs/unordered/doc/html
|
||||
;
|
||||
install pdf_ : unordered.pdf : <location>pdf ;
|
||||
explicit pdf_ ;
|
||||
|
||||
###############################################################################
|
||||
alias boostdoc
|
||||
: unordered
|
||||
:
|
||||
:
|
||||
: ;
|
||||
alias boostdoc ;
|
||||
explicit boostdoc ;
|
||||
alias boostrelease ;
|
||||
alias boostrelease : html_ ;
|
||||
explicit boostrelease ;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<!--
|
||||
Copyright Daniel James 2008-2009
|
||||
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)
|
||||
-->
|
||||
<section id="unordered.bibliography">
|
||||
<title>Bibliography</title>
|
||||
<bibliography>
|
||||
<biblioentry>
|
||||
<biblioset relation="journal">
|
||||
<title>C/C++ Users Journal</title>
|
||||
<date>February, 2006</date>
|
||||
</biblioset>
|
||||
<biblioset relation="article">
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Pete</firstname>
|
||||
<surname>Becker</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title><ulink url="http://www.ddj.com/cpp/184402066">STL and TR1: Part III - Unordered containers</ulink></title>
|
||||
</biblioset>
|
||||
<para>An introducation to the standard unordered containers.</para>
|
||||
</biblioentry>
|
||||
</bibliography>
|
||||
</section>
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
[/ Copyright 2006-2008 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) ]
|
||||
|
||||
[section:buckets The Data Structure]
|
||||
|
||||
The containers are made up of a number of 'buckets', each of which can contain
|
||||
any number of elements. For example, the following diagram shows an [classref
|
||||
boost::unordered_set unordered_set] with 7 buckets containing 5 elements, `A`,
|
||||
`B`, `C`, `D` and `E` (this is just for illustration, containers will typically
|
||||
have more buckets).
|
||||
|
||||
[diagram buckets]
|
||||
|
||||
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
|
||||
`unordered_multiset` the key is the whole element, but is referred to as the key
|
||||
so that the same terminology can be used for sets and maps). This returns a
|
||||
value of type `std::size_t`. `std::size_t` has a much greater range of values
|
||||
then the number of buckets, so the container applies another transformation to
|
||||
that value to choose a bucket to place the element in.
|
||||
|
||||
Retrieving the elements for a given key is simple. The same process is applied
|
||||
to the key to find the correct bucket. Then the key is compared with the
|
||||
elements in the bucket to find any elements that match (using the equality
|
||||
predicate `Pred`). If the hash function has worked well the elements will be
|
||||
evenly distributed amongst the buckets so only a small number of elements will
|
||||
need to be examined.
|
||||
|
||||
There is [link unordered.hash_equality more information on hash functions and
|
||||
equality predicates in the next section].
|
||||
|
||||
You can see in the diagram that `A` & `D` have been placed in the same bucket.
|
||||
When looking for elements in this bucket up to 2 comparisons are made, making
|
||||
the search slower. This is known as a collision. To keep things fast we try to
|
||||
keep collisions to a minimum.
|
||||
|
||||
'''
|
||||
<table frame="all"><title>Methods for Accessing Buckets</title>
|
||||
<tgroup cols="2">
|
||||
<thead><row>
|
||||
<entry><para>Method</para></entry>
|
||||
<entry><para>Description</para></entry>
|
||||
</row></thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>'''`size_type bucket_count() const`'''</entry>
|
||||
<entry>'''The number of buckets.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`size_type max_bucket_count() const`'''</entry>
|
||||
<entry>'''An upper bound on the number of buckets.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`size_type bucket_size(size_type n) const`'''</entry>
|
||||
<entry>'''The number of elements in bucket `n`.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`size_type bucket(key_type const& k) const`'''</entry>
|
||||
<entry>'''Returns the index of the bucket which would contain `k`.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`local_iterator begin(size_type n);`'''</entry>
|
||||
<entry morerows='5'>'''Return begin and end iterators for bucket `n`.'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`local_iterator end(size_type n);`'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`const_local_iterator begin(size_type n) const;`'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`const_local_iterator end(size_type n) const;`'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`const_local_iterator cbegin(size_type n) const;`'''</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>'''`const_local_iterator cend(size_type n) const;`'''</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
'''
|
||||
|
||||
[h2 Controlling the number of buckets]
|
||||
|
||||
As more elements are added to an unordered associative container, the number
|
||||
of elements in the buckets will increase causing performance to degrade.
|
||||
To combat this the containers increase the bucket count as elements are inserted.
|
||||
You can also tell the container to change the bucket count (if required) by
|
||||
calling `rehash`.
|
||||
|
||||
The standard leaves a lot of freedom to the implementer to decide how the
|
||||
number of buckets is chosen, but it does make some requirements based on the
|
||||
container's 'load factor', the average number of elements per bucket.
|
||||
Containers also have a 'maximum load factor' which they should try to keep the
|
||||
load factor below.
|
||||
|
||||
You can't control the bucket count directly but there are two ways to
|
||||
influence it:
|
||||
|
||||
* Specify the minimum number of buckets when constructing a container or
|
||||
when calling `rehash`.
|
||||
* Suggest a maximum load factor by calling `max_load_factor`.
|
||||
|
||||
`max_load_factor` doesn't let you set the maximum load factor yourself, it just
|
||||
lets you give a /hint/. And even then, the draft standard doesn't actually
|
||||
require the container to pay much attention to this value. The only time the
|
||||
load factor is /required/ to be less than the maximum is following a call to
|
||||
`rehash`. But most implementations will try to keep the number of elements
|
||||
below the max load factor, and set the maximum load factor to be the same as
|
||||
or close to the hint - unless your hint is unreasonably small or large.
|
||||
|
||||
[table:bucket_size Methods for Controlling Bucket Size
|
||||
[[Method] [Description]]
|
||||
|
||||
[
|
||||
[`X(size_type n)`]
|
||||
[Construct an empty container with at least `n` buckets (`X` is the container type).]
|
||||
]
|
||||
[
|
||||
[`X(InputIterator i, InputIterator j, size_type n)`]
|
||||
[Construct an empty container with at least `n` buckets and insert elements
|
||||
from the range \[`i`, `j`) (`X` is the container type).]
|
||||
]
|
||||
[
|
||||
[`float load_factor() const`]
|
||||
[The average number of elements per bucket.]
|
||||
]
|
||||
[
|
||||
[`float max_load_factor() const`]
|
||||
[Returns the current maximum load factor.]
|
||||
]
|
||||
[
|
||||
[`float max_load_factor(float z)`]
|
||||
[Changes the container's maximum load factor, using `z` as a hint.]
|
||||
]
|
||||
[
|
||||
[`void rehash(size_type n)`]
|
||||
[Changes the number of buckets so that there at least `n` buckets, and
|
||||
so that the load factor is less than the maximum load factor.]
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
[h2 Iterator Invalidation]
|
||||
|
||||
It is not specified how member functions other than `rehash` affect
|
||||
the bucket count, although `insert` is only allowed to invalidate iterators
|
||||
when the insertion causes the load factor to be greater than or equal to the
|
||||
maximum load factor. For most implementations this means that `insert` will only
|
||||
change the number of buckets when this happens. While iterators can be
|
||||
invalidated by calls to `insert` and `rehash`, pointers and references to the
|
||||
container's elements are never invalidated.
|
||||
|
||||
In a similar manner to using `reserve` for `vector`s, it can be a good idea
|
||||
to call `rehash` before inserting a large number of elements. This will get
|
||||
the expensive rehashing out of the way and let you store iterators, safe in
|
||||
the knowledge that they won't be invalidated. If you are inserting `n`
|
||||
elements into container `x`, you could first call:
|
||||
|
||||
x.rehash((x.size() + n) / x.max_load_factor());
|
||||
|
||||
[blurb Note: `rehash`'s argument is the minimum number of buckets, not the
|
||||
number of elements, which is why the new size is divided by the maximum load factor.]
|
||||
|
||||
[endsect]
|
||||
@@ -1,161 +0,0 @@
|
||||
[/ Copyright 2006-2011 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) ]
|
||||
|
||||
[section:comparison Comparison with Associative Containers]
|
||||
|
||||
[table:interface_differences Interface differences.
|
||||
[[Associative Containers] [Unordered Associative Containers]]
|
||||
|
||||
[
|
||||
[Parameterized by an ordering relation `Compare`]
|
||||
[Parameterized by a function object `Hash` and an equivalence relation
|
||||
`Pred`]
|
||||
]
|
||||
[
|
||||
[Keys can be compared using `key_compare` which is accessed by member function `key_comp()`,
|
||||
values can be compared using `value_compare` which is accessed by member function `value_comp()`.]
|
||||
[Keys can be hashed using `hasher` which is accessed by member function `hash_function()`,
|
||||
and checked for equality using `key_equal` which is accessed by member function `key_eq()`.
|
||||
There is no function object for compared or hashing values.]
|
||||
]
|
||||
[
|
||||
[Constructors have optional extra parameters for the comparison object.]
|
||||
[Constructors have optional extra parameters for the initial minimum
|
||||
number of buckets, a hash function and an equality object.]
|
||||
]
|
||||
|
||||
[
|
||||
[Keys `k1`, `k2` are considered equivalent if
|
||||
`!Compare(k1, k2) && !Compare(k2, k1)`]
|
||||
[Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`]
|
||||
]
|
||||
[
|
||||
[Member function `lower_bound(k)` and `upper_bound(k)`]
|
||||
[No equivalent. Since the elements aren't ordered `lower_bound` and
|
||||
`upper_bound` would be meaningless.]
|
||||
]
|
||||
[
|
||||
[`equal_range(k)` returns an empty range at the position that k
|
||||
would be inserted if k isn't present in the container.]
|
||||
[`equal_range(k)` returns a range at the end of the container if
|
||||
k isn't present in the container. It can't return a positioned
|
||||
range as k could be inserted into multiple place. To find out the
|
||||
bucket that k would be inserted into use `bucket(k)`. But remember
|
||||
that an insert can cause the container to rehash - meaning that the
|
||||
element can be inserted into a different bucket.]
|
||||
]
|
||||
[
|
||||
[`iterator`, `const_iterator` are of the bidirectional category.]
|
||||
[`iterator`, `const_iterator` are of at least the forward category.]
|
||||
]
|
||||
[
|
||||
[Iterators, pointers and references to the container's elements are
|
||||
never invalidated.]
|
||||
[[link unordered.buckets.iterator_invalidation Iterators can
|
||||
be invalidated by calls to insert or rehash]. Pointers and
|
||||
references to the container's elements are never invalidated.]
|
||||
]
|
||||
[
|
||||
[Iterators iterate through the container in the order defined by
|
||||
the comparison object.]
|
||||
[Iterators iterate through the container in an arbitrary order, that
|
||||
can change as elements are inserted, although equivalent elements
|
||||
are always adjacent.]
|
||||
]
|
||||
[
|
||||
[No equivalent]
|
||||
[Local iterators can be used to iterate through individual buckets.
|
||||
(The order of local iterators and iterators aren't
|
||||
required to have any correspondence.)]
|
||||
]
|
||||
[
|
||||
[Can be compared using the `==`, `!=`, `<`, `<=`, `>`, `>=` operators.]
|
||||
[Can be compared using the `==` and `!=` operators.]
|
||||
]
|
||||
[
|
||||
[]
|
||||
[When inserting with a hint, implementations are permitted to ignore
|
||||
the hint.]
|
||||
]
|
||||
[
|
||||
[`erase` never throws an exception]
|
||||
[The containers' hash or predicate function can throw exceptions
|
||||
from `erase`]
|
||||
]
|
||||
]
|
||||
|
||||
[table:complexity_guarantees Complexity Guarantees
|
||||
[[Operation] [Associative Containers] [Unordered Associative Containers]]
|
||||
[
|
||||
[Construction of empty container]
|
||||
[constant]
|
||||
[O(/n/) where /n/ is the minimum number of buckets.]
|
||||
]
|
||||
[
|
||||
[Construction of container from a range of /N/ elements]
|
||||
[O(/N/ log /N/), O(/N/) if the range is sorted with `value_comp()`]
|
||||
[Average case O(/N/), worst case
|
||||
O(/N/'''<superscript>2</superscript>''')]
|
||||
]
|
||||
[
|
||||
[Insert a single element]
|
||||
[logarithmic]
|
||||
[Average case constant, worst case linear]
|
||||
]
|
||||
[
|
||||
[Insert a single element with a hint]
|
||||
[Amortized constant if t elements inserted right after hint,
|
||||
logarithmic otherwise]
|
||||
[Average case constant, worst case linear (ie. the same as
|
||||
a normal insert).]
|
||||
]
|
||||
[
|
||||
[Inserting a range of /N/ elements]
|
||||
[ /N/ log(`size()`+/N/) ]
|
||||
[Average case O(/N/), worst case O(/N/ * `size()`)]
|
||||
]
|
||||
[
|
||||
[Erase by key, `k`]
|
||||
[O(log(`size()`) + `count(k)`)]
|
||||
[Average case: O(`count(k)`), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[Erase a single element by iterator]
|
||||
[Amortized constant]
|
||||
[Average case: O(1), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[Erase a range of /N/ elements]
|
||||
[O(log(`size()`) + /N/)]
|
||||
[Average case: O(/N/), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[Clearing the container]
|
||||
[O(`size()`)]
|
||||
[O(`size()`)]
|
||||
]
|
||||
[
|
||||
[Find]
|
||||
[logarithmic]
|
||||
[Average case: O(1), Worst case: O(`size()`)]
|
||||
]
|
||||
[/ TODO: Average case is probably wrong. ]
|
||||
[
|
||||
[Count]
|
||||
[O(log(`size()`) + `count(k)`)]
|
||||
[Average case: O(1), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[`equal_range(k)`]
|
||||
[logarithmic]
|
||||
[Average case: O(`count(k)`), Worst case: O(`size()`)]
|
||||
]
|
||||
[
|
||||
[`lower_bound`,`upper_bound`]
|
||||
[logarithmic]
|
||||
[n/a]
|
||||
]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
@@ -1,313 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.0"
|
||||
width="507.85925"
|
||||
height="400.45422"
|
||||
viewBox="1.33 0.95 6.01 4.09"
|
||||
id="svg2">
|
||||
<defs
|
||||
id="defs95" />
|
||||
<rect
|
||||
width="6.0023117"
|
||||
height="4.0815721"
|
||||
x="1.3310578"
|
||||
y="0.95080346"
|
||||
id="rect4"
|
||||
style="fill:#e5e5e5;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="6.0023117"
|
||||
height="4.0815721"
|
||||
x="1.3310578"
|
||||
y="0.95080346"
|
||||
id="rect6"
|
||||
style="opacity:1;fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="1.5711501"
|
||||
y="1.1908962"
|
||||
id="rect8"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="1.5711501"
|
||||
y="1.1908962"
|
||||
id="rect10"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="1.7289008"
|
||||
y="1.4950322"
|
||||
id="text12"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 1</text>
|
||||
<line
|
||||
x1="1.5711501"
|
||||
y1="1.6710808"
|
||||
x2="2.7716124"
|
||||
y2="1.6710808"
|
||||
id="line14"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="3.0117054"
|
||||
y="1.1908962"
|
||||
id="rect16"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="3.0117054"
|
||||
y="1.1908962"
|
||||
id="rect18"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="3.1603069"
|
||||
y="1.4950322"
|
||||
id="text20"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 2</text>
|
||||
<line
|
||||
x1="3.0117054"
|
||||
y1="1.6710808"
|
||||
x2="4.2121677"
|
||||
y2="1.6710808"
|
||||
id="line22"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="4.4522605"
|
||||
y="1.1908962"
|
||||
id="rect24"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="4.4522605"
|
||||
y="1.1908962"
|
||||
id="rect26"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="4.5917125"
|
||||
y="1.4950322"
|
||||
id="text28"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 3</text>
|
||||
<line
|
||||
x1="4.4522605"
|
||||
y1="1.6710808"
|
||||
x2="5.6527228"
|
||||
y2="1.6710808"
|
||||
id="line30"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="5.8928151"
|
||||
y="1.1908962"
|
||||
id="rect32"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="5.8928151"
|
||||
y="1.1908962"
|
||||
id="rect34"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="6.0688629"
|
||||
y="1.4858831"
|
||||
id="text36"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 4</text>
|
||||
<line
|
||||
x1="5.8928151"
|
||||
y1="1.6710808"
|
||||
x2="7.093277"
|
||||
y2="1.6710808"
|
||||
id="line38"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="2.2941716"
|
||||
y="3.1054616"
|
||||
id="rect40"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="2.2941716"
|
||||
y="3.1054616"
|
||||
id="rect42"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="2.4427731"
|
||||
y="3.4187472"
|
||||
id="text44"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 5</text>
|
||||
<line
|
||||
x1="2.2941716"
|
||||
y1="3.5856469"
|
||||
x2="3.4946339"
|
||||
y2="3.5856469"
|
||||
id="line46"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="3.7347264"
|
||||
y="3.1054616"
|
||||
id="rect48"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="3.7347264"
|
||||
y="3.1054616"
|
||||
id="rect50"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="3.8833277"
|
||||
y="3.4187472"
|
||||
id="text52"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 6</text>
|
||||
<line
|
||||
x1="3.7347264"
|
||||
y1="3.5856469"
|
||||
x2="4.9351892"
|
||||
y2="3.5856469"
|
||||
id="line54"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="5.175281"
|
||||
y="3.1054616"
|
||||
id="rect56"
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0" />
|
||||
<rect
|
||||
width="1.2004625"
|
||||
height="1.6806473"
|
||||
x="5.175281"
|
||||
y="3.1054616"
|
||||
id="rect58"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.02400924" />
|
||||
<text
|
||||
x="5.3330317"
|
||||
y="3.4187472"
|
||||
id="text60"
|
||||
style="font-size:0.19207397px;font-style:normal;font-weight:400;text-anchor:start;fill:#000000;font-family:sans">Bucket 7</text>
|
||||
<line
|
||||
x1="5.175281"
|
||||
y1="3.5856469"
|
||||
x2="6.3757439"
|
||||
y2="3.5856469"
|
||||
id="line62"
|
||||
style="stroke:#000000;stroke-width:0.02400924" />
|
||||
<ellipse
|
||||
cx="7.1999998"
|
||||
cy="4.0110002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse64"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="7.1999998"
|
||||
cy="4.0110002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse66"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="6.1443377"
|
||||
y="2.1364057"
|
||||
id="text68"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">A</text>
|
||||
<ellipse
|
||||
cx="3.007"
|
||||
cy="4.0300002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse70"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="3.007"
|
||||
cy="4.0300002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse72"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="3.2742035"
|
||||
y="2.1540098"
|
||||
id="text74"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">B</text>
|
||||
<ellipse
|
||||
cx="4.0599999"
|
||||
cy="6.7820001"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse76"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="4.0599999"
|
||||
cy="6.7820001"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse78"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="3.976877"
|
||||
y="4.0473108"
|
||||
id="text80"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">C</text>
|
||||
<ellipse
|
||||
cx="7.8449998"
|
||||
cy="4.6550002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse82"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="7.8449998"
|
||||
cy="4.6550002"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse84"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="6.5808516"
|
||||
y="2.5937216"
|
||||
id="text86"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">D</text>
|
||||
<ellipse
|
||||
cx="0.87"
|
||||
cy="4.0079999"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse88"
|
||||
style="fill:#ffffff;stroke:none" />
|
||||
<ellipse
|
||||
cx="0.87"
|
||||
cy="4.0079999"
|
||||
rx="0.308"
|
||||
ry="0.308"
|
||||
transform="matrix(0.6859785,0,0,0.6859785,1.3310577,-0.7298436)"
|
||||
id="ellipse90"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.035" />
|
||||
<text
|
||||
x="1.7991183"
|
||||
y="2.1403852"
|
||||
id="text92"
|
||||
style="font-size:0.34038281px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Sans;-inkscape-font-specification:Sans">E</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.1 KiB |
@@ -1,86 +0,0 @@
|
||||
[/ Copyright 2006-2008 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) ]
|
||||
|
||||
[section:hash_equality Equality Predicates and Hash Functions]
|
||||
|
||||
While the associative containers use an ordering relation to specify how the
|
||||
elements are stored, the unordered associative containers use an equality
|
||||
predicate and a hash function. For example, [classref boost::unordered_map]
|
||||
is declared as:
|
||||
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class ``[classref boost::unordered_map unordered_map]``;
|
||||
|
||||
The hash function comes first as you might want to change the hash function
|
||||
but not the equality predicate. For example, if you wanted to use the
|
||||
[@http://www.isthe.com/chongo/tech/comp/fnv/ FNV-1 hash] you could write:
|
||||
|
||||
[import src_code/dictionary.cpp]
|
||||
[case_sensitive_dictionary_fnv]
|
||||
|
||||
There is an [@boost:/libs/unordered/examples/fnv1.hpp implementation
|
||||
of FNV-1] in the examples directory.
|
||||
|
||||
If you wish to use a different equality function,
|
||||
you will also need to use a matching hash function. For
|
||||
example, to implement a case insensitive dictionary you need to define a
|
||||
case insensitive equality predicate and hash function:
|
||||
|
||||
[case_insensitive_functions]
|
||||
|
||||
Which you can then use in a case insensitive dictionary:
|
||||
|
||||
[case_insensitive_dictionary]
|
||||
|
||||
This is a simplified version of the example at
|
||||
[@boost:/libs/unordered/examples/case_insensitive.hpp /libs/unordered/examples/case_insensitive.hpp]
|
||||
which supports other locales and string types.
|
||||
|
||||
[caution
|
||||
Be careful when using the equality (`==`) operator with custom equality
|
||||
predicates, especially if you're using a function pointer. If you compare two
|
||||
containers with different equality predicates then the result is undefined.
|
||||
For most stateless function objects this is impossible - since you can only
|
||||
compare objects with the same equality predicate you know the equality
|
||||
predicates must be equal. But if you're using function pointers or a stateful
|
||||
equality predicate (e.g. boost::function) then you can get into trouble.
|
||||
]
|
||||
|
||||
[h2 Custom Types]
|
||||
|
||||
Similarly, a custom hash function can be used for custom types:
|
||||
|
||||
[import src_code/point1.cpp]
|
||||
[point_example1]
|
||||
|
||||
Since the default hash function is [link hash Boost.Hash],
|
||||
we can [link hash.custom extend it to support the type]
|
||||
so that the hash function doesn't need to be explicitly given:
|
||||
|
||||
[import src_code/point2.cpp]
|
||||
[point_example2]
|
||||
|
||||
See the [link hash.custom Boost.Hash documentation] for more detail on how to
|
||||
do this. Remember that it relies on extensions to the draft standard - so it
|
||||
won't work for other implementations of the unordered associative containers,
|
||||
you'll need to explicitly use Boost.Hash.
|
||||
|
||||
[table:access_methods Methods for accessing the hash and equality functions.
|
||||
[[Method] [Description]]
|
||||
|
||||
[
|
||||
[`hasher hash_function() const`]
|
||||
[Returns the container's hash function.]
|
||||
]
|
||||
[
|
||||
[`key_equal key_eq() const`]
|
||||
[Returns the container's key equality function.]
|
||||
]
|
||||
]
|
||||
|
||||
[endsect]
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
[/ Copyright 2006-2008 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) ]
|
||||
|
||||
[def __hash-table__ [@http://en.wikipedia.org/wiki/Hash_table
|
||||
hash table]]
|
||||
[def __hash-function__ [@http://en.wikipedia.org/wiki/Hash_function
|
||||
hash function]]
|
||||
|
||||
[section:intro Introduction]
|
||||
|
||||
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 [link unordered.compliance deviations from the standard] in
|
||||
order to work with non-C++11 compilers and libraries.
|
||||
|
||||
`unordered_set` and `unordered_multiset` are defined in the header
|
||||
<[headerref boost/unordered_set.hpp]>
|
||||
|
||||
namespace boost {
|
||||
template <
|
||||
class Key,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class ``[classref boost::unordered_set unordered_set]``;
|
||||
|
||||
template<
|
||||
class Key,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class ``[classref boost::unordered_multiset unordered_multiset]``;
|
||||
}
|
||||
|
||||
`unordered_map` and `unordered_multimap` are defined in the header
|
||||
<[headerref boost/unordered_map.hpp]>
|
||||
|
||||
namespace boost {
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class ``[classref boost::unordered_map unordered_map]``;
|
||||
|
||||
template<
|
||||
class Key, class Mapped,
|
||||
class Hash = ``[classref boost::hash]``<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class ``[classref boost::unordered_multimap unordered_multimap]``;
|
||||
}
|
||||
|
||||
When using Boost.TR1, these classes are included from `<unordered_set>` and
|
||||
`<unordered_map>`, with the classes added to the `std::tr1` namespace.
|
||||
|
||||
The containers are used in a similar manner to the normal associative
|
||||
containers:
|
||||
|
||||
[import src_code/intro.cpp]
|
||||
[intro_example1_2]
|
||||
|
||||
But since the elements aren't ordered, the output of:
|
||||
|
||||
[intro_example1_3]
|
||||
|
||||
can be in any order. For example, it might be:
|
||||
|
||||
two,2
|
||||
one,1
|
||||
three,3
|
||||
|
||||
To store an object in an unordered associative container requires both a
|
||||
key equality function and a hash function. The default function objects in
|
||||
the standard containers support a few basic types including integer types,
|
||||
floating point types, pointer types, and the standard strings. Since
|
||||
Boost.Unordered uses [classref boost::hash] it also supports some other types,
|
||||
including standard containers. To use any types not supported by these methods
|
||||
you have to [link hash.custom extend Boost.Hash to support the type] or use
|
||||
your own custom equality predicates and hash functions. See the
|
||||
[link unordered.hash_equality Equality Predicates and Hash Functions] section
|
||||
for more details.
|
||||
|
||||
There are other differences, which are listed in the
|
||||
[link unordered.comparison Comparison with Associative Containers] section.
|
||||
|
||||
[endsect]
|
||||
@@ -1,111 +0,0 @@
|
||||
[/ Copyright 2006-2008 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) ]
|
||||
|
||||
[def __wang__
|
||||
[@http://web.archive.org/web/20121102023700/http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||
Thomas Wang's article on integer hash functions]]
|
||||
|
||||
[section:rationale Implementation Rationale]
|
||||
|
||||
The intent of this library is to implement the unordered
|
||||
containers in the draft standard, so the interface was fixed. But there are
|
||||
still some implementation decisions to make. The priorities are
|
||||
conformance to the standard and portability.
|
||||
|
||||
The [@http://en.wikipedia.org/wiki/Hash_table Wikipedia article on hash tables]
|
||||
has a good summary of the implementation issues for hash tables in general.
|
||||
|
||||
[h2 Data Structure]
|
||||
|
||||
By specifying an interface for accessing the buckets of the container the
|
||||
standard pretty much requires that the hash table uses chained addressing.
|
||||
|
||||
It would be conceivable to write a hash table that uses another method. For
|
||||
example, it could use open addressing, and use the lookup chain to act as a
|
||||
bucket but there are some serious problems with this:
|
||||
|
||||
* The draft standard requires that pointers to elements aren't invalidated, so
|
||||
the elements can't be stored in one array, but will need a layer of
|
||||
indirection instead - losing the efficiency and most of the memory gain,
|
||||
the main advantages of open addressing.
|
||||
|
||||
* Local iterators would be very inefficient and may not be able to
|
||||
meet the complexity requirements.
|
||||
|
||||
* There are also the restrictions on when iterators can be invalidated. Since
|
||||
open addressing degrades badly when there are a high number of collisions the
|
||||
restrictions could prevent a rehash when it's really needed. The maximum load
|
||||
factor could be set to a fairly low value to work around this - but the
|
||||
standard requires that it is initially set to 1.0.
|
||||
|
||||
* And since the standard is written with a eye towards chained
|
||||
addressing, users will be surprised if the performance doesn't reflect that.
|
||||
|
||||
So chained addressing is used.
|
||||
|
||||
[/ (Removing for now as this is out of date)
|
||||
|
||||
For containers with unique keys I store the buckets in a single-linked list.
|
||||
There are other possible data structures (such as a double-linked list)
|
||||
that allow for some operations to be faster (such as erasing and iteration)
|
||||
but the possible gain seems small compared to the extra memory needed.
|
||||
The most commonly used operations (insertion and lookup) would not be improved
|
||||
at all.
|
||||
|
||||
But for containers with equivalent keys a single-linked list can degrade badly
|
||||
when a large number of elements with equivalent keys are inserted. I think it's
|
||||
reasonable to assume that users who choose to use `unordered_multiset` or
|
||||
`unordered_multimap` do so because they are likely to insert elements with
|
||||
equivalent keys. So I have used an alternative data structure that doesn't
|
||||
degrade, at the expense of an extra pointer per node.
|
||||
|
||||
This works by adding storing a circular linked list for each group of equivalent
|
||||
nodes in reverse order. This allows quick navigation to the end of a group (since
|
||||
the first element points to the last) and can be quickly updated when elements
|
||||
are inserted or erased. The main disadvantage of this approach is some hairy code
|
||||
for erasing elements.
|
||||
]
|
||||
|
||||
[/ (Starting to write up new structure, might not be ready in time)
|
||||
The node used to be stored in a linked list for each bucket but that
|
||||
didn't meet the complexity requirements for C++11, so now the nodes
|
||||
are stored in one long single linked list. But there needs a way to get
|
||||
the bucket from the node, to do that a copy of the key's hash value is
|
||||
stored in the node. Another possibility would be to store a pointer to
|
||||
the bucket, or the bucket's index, but storing the hash value allows
|
||||
some operations to be faster.
|
||||
]
|
||||
|
||||
[h2 Number of Buckets]
|
||||
|
||||
There are two popular methods for choosing the number of buckets in a hash
|
||||
table. One is to have a prime number of buckets, another is to use a power
|
||||
of 2.
|
||||
|
||||
Using a prime number of buckets, and choosing a bucket by using the modulus
|
||||
of the hash function's result will usually give a good result. The downside
|
||||
is that the required modulus operation is fairly expensive. This is what the
|
||||
containers do in most cases.
|
||||
|
||||
Using a power of 2 allows for much quicker selection of the bucket
|
||||
to use, but at the expense of losing the upper bits of the hash value.
|
||||
For some specially designed hash functions it is possible to do this and
|
||||
still get a good result but as the containers can take arbitrary hash
|
||||
functions this can't be relied on.
|
||||
|
||||
To avoid this a transformation could be applied to the hash function, for an
|
||||
example see __wang__. Unfortunately, a transformation like Wang's requires
|
||||
knowledge of the number of bits in the hash value, so it isn't portable enough
|
||||
to use as a default. It can applicable in certain cases so the containers
|
||||
have a policy based implementation that can use this alternative technique.
|
||||
|
||||
Currently this is only done on 64 bit architectures, where prime number
|
||||
modulus can be expensive. Although this varies depending on the architecture,
|
||||
so I probably should revisit it.
|
||||
|
||||
I'm also thinking of introducing a mechanism whereby a hash function can
|
||||
indicate that it's safe to be used directly with power of 2 buckets, in
|
||||
which case a faster plain power of 2 implementation can be used.
|
||||
|
||||
[endsect]
|
||||
-1788
File diff suppressed because it is too large
Load Diff
-5889
File diff suppressed because it is too large
Load Diff
@@ -1,101 +0,0 @@
|
||||
|
||||
// Copyright 2006-2007 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)
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include "../../examples/fnv1.hpp"
|
||||
|
||||
//[case_insensitive_functions
|
||||
struct iequal_to
|
||||
{
|
||||
bool operator()(std::string const& x,
|
||||
std::string const& y) const
|
||||
{
|
||||
return boost::algorithm::iequals(x, y, std::locale());
|
||||
}
|
||||
};
|
||||
|
||||
struct ihash
|
||||
{
|
||||
std::size_t operator()(std::string const& x) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
std::locale locale;
|
||||
|
||||
for(std::string::const_iterator it = x.begin();
|
||||
it != x.end(); ++it)
|
||||
{
|
||||
boost::hash_combine(seed, std::toupper(*it, locale));
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
//]
|
||||
|
||||
int main() {
|
||||
//[case_sensitive_dictionary_fnv
|
||||
boost::unordered_map<std::string, int, hash::fnv_1>
|
||||
dictionary;
|
||||
//]
|
||||
|
||||
BOOST_TEST(dictionary.empty());
|
||||
|
||||
dictionary["one"] = 1;
|
||||
BOOST_TEST(dictionary.size() == 1);
|
||||
BOOST_TEST(dictionary.find("ONE") == dictionary.end());
|
||||
|
||||
dictionary.insert(std::make_pair("ONE", 2));
|
||||
BOOST_TEST(dictionary.size() == 2);
|
||||
BOOST_TEST(dictionary.find("ONE") != dictionary.end() &&
|
||||
dictionary.find("ONE")->first == "ONE" &&
|
||||
dictionary.find("ONE")->second == 2);
|
||||
|
||||
dictionary["One"] = 3;
|
||||
BOOST_TEST(dictionary.size() == 3);
|
||||
BOOST_TEST(dictionary.find("One") != dictionary.end() &&
|
||||
dictionary.find("One")->first == "One" &&
|
||||
dictionary.find("One")->second == 3);
|
||||
|
||||
dictionary["two"] = 4;
|
||||
BOOST_TEST(dictionary.size() == 4);
|
||||
BOOST_TEST(dictionary.find("Two") == dictionary.end() &&
|
||||
dictionary.find("two") != dictionary.end() &&
|
||||
dictionary.find("two")->second == 4);
|
||||
|
||||
|
||||
//[case_insensitive_dictionary
|
||||
boost::unordered_map<std::string, int, ihash, iequal_to>
|
||||
idictionary;
|
||||
//]
|
||||
|
||||
BOOST_TEST(idictionary.empty());
|
||||
|
||||
idictionary["one"] = 1;
|
||||
BOOST_TEST(idictionary.size() == 1);
|
||||
BOOST_TEST(idictionary.find("ONE") != idictionary.end() &&
|
||||
idictionary.find("ONE") == idictionary.find("one"));
|
||||
|
||||
idictionary.insert(std::make_pair("ONE", 2));
|
||||
BOOST_TEST(idictionary.size() == 1);
|
||||
BOOST_TEST(idictionary.find("ONE") != idictionary.end() &&
|
||||
idictionary.find("ONE")->first == "one" &&
|
||||
idictionary.find("ONE")->second == 1);
|
||||
|
||||
idictionary["One"] = 3;
|
||||
BOOST_TEST(idictionary.size() == 1);
|
||||
BOOST_TEST(idictionary.find("ONE") != idictionary.end() &&
|
||||
idictionary.find("ONE")->first == "one" &&
|
||||
idictionary.find("ONE")->second == 3);
|
||||
|
||||
idictionary["two"] = 4;
|
||||
BOOST_TEST(idictionary.size() == 2);
|
||||
BOOST_TEST(idictionary.find("two") != idictionary.end() &&
|
||||
idictionary.find("TWO")->first == "two" &&
|
||||
idictionary.find("Two")->second == 4);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
// Copyright 2006-2009 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)
|
||||
|
||||
//[intro_example1_1
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
//]
|
||||
|
||||
int main() {
|
||||
//[intro_example1_2
|
||||
typedef boost::unordered_map<std::string, int> map;
|
||||
map x;
|
||||
x["one"] = 1;
|
||||
x["two"] = 2;
|
||||
x["three"] = 3;
|
||||
|
||||
assert(x.at("one") == 1);
|
||||
assert(x.find("missing") == x.end());
|
||||
//]
|
||||
|
||||
//[intro_example1_3
|
||||
BOOST_FOREACH(map::value_type i, x) {
|
||||
std::cout<<i.first<<","<<i.second<<"\n";
|
||||
}
|
||||
//]
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
// Copyright 2006-2009 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)
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
//[point_example1
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
bool operator==(point const& p1, point const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
struct point_hash
|
||||
{
|
||||
std::size_t operator()(point const& p) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, p.x);
|
||||
boost::hash_combine(seed, p.y);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
boost::unordered_multiset<point, point_hash> points;
|
||||
//]
|
||||
|
||||
int main() {
|
||||
point x[] = {{1,2}, {3,4}, {1,5}, {1,2}};
|
||||
for(int i = 0; i < sizeof(x) / sizeof(point); ++i)
|
||||
points.insert(x[i]);
|
||||
BOOST_TEST(points.count(x[0]) == 2);
|
||||
BOOST_TEST(points.count(x[1]) == 1);
|
||||
point y = {10, 2};
|
||||
BOOST_TEST(points.count(y) == 0);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
// Copyright 2006-2009 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)
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
//[point_example2
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
bool operator==(point const& p1, point const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
std::size_t hash_value(point const& p) {
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, p.x);
|
||||
boost::hash_combine(seed, p.y);
|
||||
return seed;
|
||||
}
|
||||
|
||||
// Now the default function objects work.
|
||||
boost::unordered_multiset<point> points;
|
||||
//]
|
||||
|
||||
int main() {
|
||||
point x[] = {{1,2}, {3,4}, {1,5}, {1,2}};
|
||||
for(int i = 0; i < sizeof(x) / sizeof(point); ++i)
|
||||
points.insert(x[i]);
|
||||
BOOST_TEST(points.count(x[0]) == 2);
|
||||
BOOST_TEST(points.count(x[1]) == 1);
|
||||
point y = {10, 2};
|
||||
BOOST_TEST(points.count(y) == 0);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
= Boost.Unordered
|
||||
:toc: left
|
||||
:toclevels: 3
|
||||
:idprefix:
|
||||
:docinfo: private-footer
|
||||
:source-highlighter: rouge
|
||||
:source-language: c++
|
||||
:nofooter:
|
||||
:sectlinks:
|
||||
|
||||
:leveloffset: +1
|
||||
|
||||
include::unordered/intro.adoc[]
|
||||
include::unordered/buckets.adoc[]
|
||||
include::unordered/hash_equality.adoc[]
|
||||
include::unordered/comparison.adoc[]
|
||||
include::unordered/compliance.adoc[]
|
||||
include::unordered/rationale.adoc[]
|
||||
include::unordered/ref.adoc[]
|
||||
include::unordered/changes.adoc[]
|
||||
include::unordered/bibliography.adoc[]
|
||||
include::unordered/copyright.adoc[]
|
||||
@@ -1,39 +0,0 @@
|
||||
[/ Copyright 2006-2008 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) ]
|
||||
|
||||
[library Boost.Unordered
|
||||
[quickbook 1.7]
|
||||
[compatibility-mode 1.5]
|
||||
[authors [James, Daniel]]
|
||||
[copyright 2003 2004 Jeremy B. Maitin-Shepard]
|
||||
[copyright 2005 2006 2007 2008 Daniel James]
|
||||
[purpose std::tr1 compliant hash containers]
|
||||
[id unordered]
|
||||
[dirname unordered]
|
||||
[license
|
||||
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])
|
||||
]
|
||||
]
|
||||
|
||||
[template diagram[name] '''<inlinemediaobject>
|
||||
<imageobject role="html">
|
||||
<imagedata align = "center" fileref="../../libs/unordered/doc/diagrams/'''[name]'''.png"></imagedata>
|
||||
</imageobject>
|
||||
<imageobject role="print">
|
||||
<imagedata align = "center" fileref="../../libs/unordered/doc/diagrams/'''[name]'''.svg"></imagedata>
|
||||
</imageobject>
|
||||
</inlinemediaobject>''']
|
||||
|
||||
|
||||
[include:unordered intro.qbk]
|
||||
[include:unordered buckets.qbk]
|
||||
[include:unordered hash_equality.qbk]
|
||||
[include:unordered comparison.qbk]
|
||||
[include:unordered compliance.qbk]
|
||||
[include:unordered rationale.qbk]
|
||||
[include:unordered changes.qbk]
|
||||
[xinclude ref.xml]
|
||||
[xinclude bibliography.xml]
|
||||
@@ -0,0 +1,10 @@
|
||||
[#bibliography]
|
||||
|
||||
:idprefix: bibliography_
|
||||
|
||||
= Bibliography
|
||||
|
||||
* _C/C++ Users Journal_. February, 2006. Pete Becker. http://www.ddj.com/cpp/184402066[STL and TR1: Part III - Unordered containers^]. +
|
||||
An introducation to the standard unordered containers.
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
[#buckets]
|
||||
:idprefix: buckets_
|
||||
|
||||
= The Data Structure
|
||||
|
||||
The containers are made up of a number of 'buckets', each of which can contain
|
||||
any number of elements. For example, the following diagram shows an <<unordered_set,unordered_set>> with 7 buckets containing 5 elements, `A`,
|
||||
`B`, `C`, `D` and `E` (this is just for illustration, containers will typically
|
||||
have more buckets).
|
||||
|
||||
image::../diagrams/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
|
||||
`unordered_multiset` the key is the whole element, but is referred to as the key
|
||||
so that the same terminology can be used for sets and maps). This returns a
|
||||
value of type `std::size_t`. `std::size_t` has a much greater range of values
|
||||
then the number of buckets, so the container applies another transformation to
|
||||
that value to choose a bucket to place the element in.
|
||||
|
||||
Retrieving the elements for a given key is simple. The same process is applied
|
||||
to the key to find the correct bucket. Then the key is compared with the
|
||||
elements in the bucket to find any elements that match (using the equality
|
||||
predicate `Pred`). If the hash function has worked well the elements will be
|
||||
evenly distributed amongst the buckets so only a small number of elements will
|
||||
need to be examined.
|
||||
|
||||
There is <<hash_equality, more information on hash functions and
|
||||
equality predicates in the next section>>.
|
||||
|
||||
You can see in the diagram that `A` & `D` have been placed in the same bucket.
|
||||
When looking for elements in this bucket up to 2 comparisons are made, making
|
||||
the search slower. This is known as a collision. To keep things fast we try to
|
||||
keep collisions to a minimum.
|
||||
|
||||
[caption=, title='Table {counter:table-counter}. Methods for Accessing Buckets']
|
||||
[cols="1,.^1", frame=all, grid=rows]
|
||||
|===
|
||||
|Method |Description
|
||||
|
||||
|`size_type bucket_count() const`
|
||||
|The number of buckets.
|
||||
|
||||
|`size_type max_bucket_count() const`
|
||||
|An upper bound on the number of buckets.
|
||||
|
||||
|`size_type bucket_size(size_type n) const`
|
||||
|The number of elements in bucket `n`.
|
||||
|
||||
|`size_type bucket(key_type const& k) const`
|
||||
|Returns the index of the bucket which would contain `k`.
|
||||
|
||||
|`local_iterator begin(size_type n)`
|
||||
1.6+|Return begin and end iterators for bucket `n`.
|
||||
|
||||
|`local_iterator end(size_type n)`
|
||||
|
||||
|`const_local_iterator begin(size_type n) const`
|
||||
|
||||
|`const_local_iterator end(size_type n) const`
|
||||
|
||||
|`const_local_iterator cbegin(size_type n) const`
|
||||
|
||||
|`const_local_iterator cend(size_type n) const`
|
||||
|
||||
|===
|
||||
|
||||
== Controlling the number of buckets
|
||||
|
||||
As more elements are added to an unordered associative container, the number
|
||||
of elements in the buckets will increase causing performance to degrade.
|
||||
To combat this the containers increase the bucket count as elements are inserted.
|
||||
You can also tell the container to change the bucket count (if required) by
|
||||
calling `rehash`.
|
||||
|
||||
The standard leaves a lot of freedom to the implementer to decide how the
|
||||
number of buckets is chosen, but it does make some requirements based on the
|
||||
container's 'load factor', the average number of elements per bucket.
|
||||
Containers also have a 'maximum load factor' which they should try to keep the
|
||||
load factor below.
|
||||
|
||||
You can't control the bucket count directly but there are two ways to
|
||||
influence it:
|
||||
|
||||
* Specify the minimum number of buckets when constructing a container or when calling `rehash`.
|
||||
* Suggest a maximum load factor by calling `max_load_factor`.
|
||||
|
||||
`max_load_factor` doesn't let you set the maximum load factor yourself, it just
|
||||
lets you give a _hint_. And even then, the standard doesn't actually
|
||||
require the container to pay much attention to this value. The only time the
|
||||
load factor is _required_ to be less than the maximum is following a call to
|
||||
`rehash`. But most implementations will try to keep the number of elements
|
||||
below the max load factor, and set the maximum load factor to be the same as
|
||||
or close to the hint - unless your hint is unreasonably small or large.
|
||||
|
||||
[caption=, title='Table {counter:table-counter}. Methods for Controlling Bucket Size']
|
||||
[cols="1,.^1", frame=all, grid=rows]
|
||||
|===
|
||||
|Method |Description
|
||||
|
||||
|`X(size_type n)`
|
||||
|Construct an empty container with at least `n` buckets (`X` is the container type).
|
||||
|
||||
|`X(InputIterator i, InputIterator j, size_type n)`
|
||||
|Construct an empty container with at least `n` buckets and insert elements from the range `[i, j)` (`X` is the container type).
|
||||
|
||||
|`float load_factor() const`
|
||||
|The average number of elements per bucket.
|
||||
|
||||
|`float max_load_factor() const`
|
||||
|Returns the current maximum load factor.
|
||||
|
||||
|`float max_load_factor(float z)`
|
||||
|Changes the container's maximum load factor, using `z` as a hint.
|
||||
|
||||
|`void rehash(size_type n)`
|
||||
|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
|
||||
|
||||
|===
|
||||
|
||||
== Iterator Invalidation
|
||||
|
||||
It is not specified how member functions other than `rehash` and `reserve` affect
|
||||
the bucket count, although `insert` is only allowed to invalidate iterators
|
||||
when the insertion causes the load factor to be greater than or equal to the
|
||||
maximum load factor. For most implementations this means that `insert` will only
|
||||
change the number of buckets when this happens. While iterators can be
|
||||
invalidated by calls to `insert`, `rehash` and `reserve`, pointers and references to the
|
||||
container's elements are never invalidated.
|
||||
|
||||
In a similar manner to using `reserve` for ``vector``s, it can be a good idea
|
||||
to call `reserve` before inserting a large number of elements. This will get
|
||||
the expensive rehashing out of the way and let you store iterators, safe in
|
||||
the knowledge that they won't be invalidated. If you are inserting `n`
|
||||
elements into container `x`, you could first call:
|
||||
|
||||
```
|
||||
x.reserve(n);
|
||||
```
|
||||
|
||||
Note:: `reserve(n)` reserves space for at least `n` elements, allocating enough buckets
|
||||
so as to not exceed the maximum load factor.
|
||||
+
|
||||
Because the maximum load factor is defined as the number of elements divided by the total
|
||||
number of available buckets, this function is logically equivalent to:
|
||||
+
|
||||
```
|
||||
x.rehash(std::ceil(n / x.max_load_factor()))
|
||||
```
|
||||
+
|
||||
See the <<unordered_map_rehash,reference for more details>> on the `rehash` function.
|
||||
|
||||
@@ -1,158 +1,225 @@
|
||||
[#changes]
|
||||
= Change Log
|
||||
|
||||
[/ Copyright 2008 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) ]
|
||||
:idprefix: changes_
|
||||
:svn-ticket-url: https://svn.boost.org/trac/boost/ticket
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
[template ticket[number]'''<ulink
|
||||
url="https://svn.boost.org/trac/boost/ticket/'''[number]'''">'''#[number]'''</ulink>''']
|
||||
== Release 1.79.0
|
||||
|
||||
[template pull_request[number][@https://github.com/boostorg/unordered/pull/[number] GitHub #[number]]]
|
||||
* Improved {cpp}20 support:
|
||||
** All containers have been updated to support
|
||||
heterogeneous `count`, `equal_range` and `find`.
|
||||
** All containers now implement the member function `contains`.
|
||||
** `erase_if` has been implemented for all containers.
|
||||
* Improved {cpp}23 support:
|
||||
** All containers have been updated to support
|
||||
heterogeneous `erase` and `extract`.
|
||||
* Changed behavior of `reserve` to eagerly
|
||||
allocate ({github-pr-url}/59[PR#59^]).
|
||||
* Various warning fixes in the test suite.
|
||||
* Update code to internally use `boost::allocator_traits`.
|
||||
* Switch to Fibonacci hashing.
|
||||
* Update documentation to be written in AsciiDoc instead of QuickBook.
|
||||
|
||||
[section:changes Change Log]
|
||||
== Release 1.67.0
|
||||
|
||||
[h2 Review Version]
|
||||
* Improved {cpp}17 support:
|
||||
** Add template deduction guides from the standard.
|
||||
** Use a simple implementation of `optional` in node handles, so
|
||||
that they're closer to the standard.
|
||||
** Add missing `noexcept` specifications to `swap`, `operator=`
|
||||
and node handles, and change the implementation to match.
|
||||
Using `std::allocator_traits::is_always_equal`, or our own
|
||||
implementation when not available, and
|
||||
`boost::is_nothrow_swappable` in the implementation.
|
||||
* Improved {cpp}20 support:
|
||||
** Use `boost::to_address`, which has the proposed {cpp}20 semantics,
|
||||
rather than the old custom implementation.
|
||||
* Add `element_type` to iterators, so that `std::pointer_traits`
|
||||
will work.
|
||||
* Use `std::piecewise_construct` on recent versions of Visual {cpp},
|
||||
and other uses of the Dinkumware standard library,
|
||||
now using Boost.Predef to check compiler and library versions.
|
||||
* Use `std::iterator_traits` rather than the boost iterator traits
|
||||
in order to remove dependency on Boost.Iterator.
|
||||
* Remove iterators' inheritance from `std::iterator`, which is
|
||||
deprecated in {cpp}17, thanks to Daniela Engert
|
||||
({github-pr-url}/7[PR#7^]).
|
||||
* Stop using `BOOST_DEDUCED_TYPENAME`.
|
||||
* Update some Boost include paths.
|
||||
* Rename some internal methods, and variables.
|
||||
* Various testing improvements.
|
||||
* Miscellaneous internal changes.
|
||||
|
||||
Initial review version, for the review conducted from 7th December 2007 to
|
||||
16th December 2007.
|
||||
== Release 1.66.0
|
||||
|
||||
[h2 1.35.0 Add-on - 31st March 2008]
|
||||
* Simpler move construction implementation.
|
||||
* Documentation fixes ({github-pr-url}/6[GitHub #6^]).
|
||||
|
||||
Unofficial release uploaded to vault, to be used with Boost 1.35.0. Incorporated
|
||||
many of the suggestions from the review.
|
||||
== Release 1.65.0
|
||||
|
||||
* Improved portability thanks to Boost regression testing.
|
||||
* Fix lots of typos, and clearer text in the documentation.
|
||||
* Fix floating point to `std::size_t` conversion when calculating sizes from
|
||||
the max load factor, and use `double` in the calculation for greater accuracy.
|
||||
* Fix some errors in the examples.
|
||||
* Add deprecated attributes to `quick_erase` and `erase_return_void`.
|
||||
I really will remove them in a future version this time.
|
||||
* Small standards compliance fixes:
|
||||
** `noexpect` specs for `swap` free functions.
|
||||
** Add missing `insert(P&&)` methods.
|
||||
|
||||
[h2 Boost 1.36.0]
|
||||
== Release 1.64.0
|
||||
|
||||
First official release.
|
||||
* Initial support for new {cpp}17 member functions:
|
||||
`insert_or_assign` and `try_emplace` in `unordered_map`,
|
||||
* Initial support for `merge` and `extract`.
|
||||
Does not include transferring nodes between
|
||||
`unordered_map` and `unordered_multimap` or between `unordered_set` and
|
||||
`unordered_multiset` yet. That will hopefully be in the next version of
|
||||
Boost.
|
||||
|
||||
* Rearrange the internals.
|
||||
* Move semantics - full support when rvalue references are available, emulated
|
||||
using a cut down version of the Adobe move library when they are not.
|
||||
* Emplace support when rvalue references and variadic template are available.
|
||||
* More efficient node allocation when rvalue references and variadic template
|
||||
are available.
|
||||
* Added equality operators.
|
||||
== Release 1.63.0
|
||||
|
||||
[h2 Boost 1.37.0]
|
||||
* Check hint iterator in `insert`/`emplace_hint`.
|
||||
* Fix some warnings, mostly in the tests.
|
||||
* Manually write out `emplace_args` for small numbers of arguments -
|
||||
should make template error messages a little more bearable.
|
||||
* Remove superfluous use of `boost::forward` in emplace arguments,
|
||||
which fixes emplacing string literals in old versions of Visual {cpp}.
|
||||
* Fix an exception safety issue in assignment. If bucket allocation
|
||||
throws an exception, it can overwrite the hash and equality functions while
|
||||
leaving the existing elements in place. This would mean that the function
|
||||
objects wouldn't match the container elements, so elements might be in the
|
||||
wrong bucket and equivalent elements would be incorrectly handled.
|
||||
* Various reference documentation improvements.
|
||||
* Better allocator support ({svn-ticket-url}/12459[#12459^]).
|
||||
* Make the no argument constructors implicit.
|
||||
* Implement missing allocator aware constructors.
|
||||
* Fix assigning the hash/key equality functions for empty containers.
|
||||
* Remove unary/binary_function from the examples in the documentation.
|
||||
They are removed in {cpp}17.
|
||||
* Support 10 constructor arguments in emplace. It was meant to support up to 10
|
||||
arguments, but an off by one error in the preprocessor code meant it only
|
||||
supported up to 9.
|
||||
|
||||
* Rename overload of `emplace` with hint, to `emplace_hint` as specified in
|
||||
[@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2691.pdf n2691].
|
||||
* Provide forwarding headers at `<boost/unordered/unordered_map_fwd.hpp>` and
|
||||
`<boost/unordered/unordered_set_fwd.hpp>`.
|
||||
* Move all the implementation inside `boost/unordered`, to assist
|
||||
modularization and hopefully make it easier to track changes in subversion.
|
||||
== Release 1.62.0
|
||||
|
||||
[h2 Boost 1.38.0]
|
||||
* Remove use of deprecated `boost::iterator`.
|
||||
* Remove `BOOST_NO_STD_DISTANCE` workaround.
|
||||
* Remove `BOOST_UNORDERED_DEPRECATED_EQUALITY` warning.
|
||||
* Simpler implementation of assignment, fixes an exception safety issue
|
||||
for `unordered_multiset` and `unordered_multimap`. Might be a little slower.
|
||||
* Stop using return value SFINAE which some older compilers have issues
|
||||
with.
|
||||
|
||||
* Use [@boost:/libs/core/swap.html `boost::swap`].
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2237 Ticket 2237]:
|
||||
Document that the equality and inequality operators are undefined for two
|
||||
objects if their equality predicates aren't equivalent. Thanks to Daniel
|
||||
Krügler.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/1710 Ticket 1710]:
|
||||
Use a larger prime number list. Thanks to Thorsten Ottosen and Hervé
|
||||
Brönnimann.
|
||||
* Use
|
||||
[@boost:/libs/type_traits/doc/html/boost_typetraits/category/alignment.html
|
||||
aligned storage] to store the types. This changes the way the allocator is
|
||||
used to construct nodes. It used to construct the node with two calls to
|
||||
the allocator's `construct` method - once for the pointers and once for the
|
||||
value. It now constructs the node with a single call to construct and
|
||||
then constructs the value using in place construction.
|
||||
* Add support for C++0x initializer lists where they're available (currently
|
||||
only g++ 4.4 in C++0x mode).
|
||||
== Release 1.58.0
|
||||
|
||||
[h2 Boost 1.39.0]
|
||||
* Remove unnecessary template parameter from const iterators.
|
||||
* Rename private `iterator` typedef in some iterator classes, as it
|
||||
confuses some traits classes.
|
||||
* Fix move assignment with stateful, propagate_on_container_move_assign
|
||||
allocators ({svn-ticket-url}/10777[#10777^]).
|
||||
* Fix rare exception safety issue in move assignment.
|
||||
* Fix potential overflow when calculating number of buckets to allocate
|
||||
({github-pr-url}/4[GitHub #4^]).
|
||||
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2756 Ticket 2756]: Avoid a warning
|
||||
on Visual C++ 2009.
|
||||
* Some other minor internal changes to the implementation, tests and
|
||||
documentation.
|
||||
* Avoid an unnecessary copy in `operator[]`.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2975 Ticket 2975]: Fix length of
|
||||
prime number list.
|
||||
== Release 1.57.0
|
||||
|
||||
[h2 Boost 1.40.0]
|
||||
* Fix the `pointer` typedef in iterators ({svn-ticket-url}/10672[#10672^]).
|
||||
* Fix Coverity warning
|
||||
({github-pr-url}/2[GitHub #2^]).
|
||||
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2975 Ticket 2975]:
|
||||
Store the prime list as a preprocessor sequence - so that it will always get
|
||||
the length right if it changes again in the future.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/1978 Ticket 1978]:
|
||||
Implement `emplace` for all compilers.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/2908 Ticket 2908],
|
||||
[@https://svn.boost.org/trac/boost/ticket/3096 Ticket 3096]:
|
||||
Some workarounds for old versions of borland, including adding explicit
|
||||
destructors to all containers.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/3082 Ticket 3082]:
|
||||
Disable incorrect Visual C++ warnings.
|
||||
* Better configuration for C++0x features when the headers aren't available.
|
||||
* Create less buckets by default.
|
||||
== Release 1.56.0
|
||||
|
||||
[h2 Boost 1.41.0 - Major update]
|
||||
* Fix some shadowed variable warnings ({svn-ticket-url}/9377[#9377^]).
|
||||
* Fix allocator use in documentation ({svn-ticket-url}/9719[#9719^]).
|
||||
* Always use prime number of buckets for integers. Fixes performance
|
||||
regression when inserting consecutive integers, although makes other
|
||||
uses slower ({svn-ticket-url}/9282[#9282^]).
|
||||
* Only construct elements using allocators, as specified in {cpp}11 standard.
|
||||
|
||||
* The original version made heavy use of macros to sidestep some of the older
|
||||
compilers' poor template support. But since I no longer support those
|
||||
compilers and the macro use was starting to become a maintenance burden it
|
||||
has been rewritten to use templates instead of macros for the implementation
|
||||
classes.
|
||||
== Release 1.55.0
|
||||
|
||||
* The container object is now smaller thanks to using `boost::compressed_pair`
|
||||
for EBO and a slightly different function buffer - now using a bool instead
|
||||
of a member pointer.
|
||||
* Avoid some warnings ({svn-ticket-url}/8851[#8851^], {svn-ticket-url}/8874[#8874^]).
|
||||
* Avoid exposing some detail functions via. ADL on the iterators.
|
||||
* Follow the standard by only using the allocators' construct and destroy
|
||||
methods to construct and destroy stored elements. Don't use them for internal
|
||||
data like pointers.
|
||||
|
||||
* Buckets are allocated lazily which means that constructing an empty container
|
||||
will not allocate any memory.
|
||||
== Release 1.54.0
|
||||
|
||||
[h2 Boost 1.42.0]
|
||||
* Mark methods specified in standard as `noexpect`. More to come in the next
|
||||
release.
|
||||
* If the hash function and equality predicate are known to both have nothrow
|
||||
move assignment or construction then use them.
|
||||
|
||||
* Support instantiating the containers with incomplete value types.
|
||||
* Reduced the number of warnings (mostly in tests).
|
||||
* Improved codegear compatibility.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/3693 Ticket 3693]:
|
||||
Add `erase_return_void` as a temporary workaround for the current
|
||||
`erase` which can be inefficient because it has to find the next
|
||||
element to return an iterator.
|
||||
* Add templated find overload for compatible keys.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/3773 Ticket 3773]:
|
||||
Add missing `std` qualifier to `ptrdiff_t`.
|
||||
* Some code formatting changes to fit almost all lines into 80 characters.
|
||||
== Release 1.53.0
|
||||
|
||||
[h2 Boost 1.43.0]
|
||||
* Remove support for the old pre-standard variadic pair constructors, and
|
||||
equality implementation. Both have been deprecated since Boost 1.48.
|
||||
* Remove use of deprecated config macros.
|
||||
* More internal implementation changes, including a much simpler
|
||||
implementation of `erase`.
|
||||
|
||||
* [@http://svn.boost.org/trac/boost/ticket/3966 Ticket 3966]:
|
||||
`erase_return_void` is now `quick_erase`, which is the
|
||||
[@http://home.roadrunner.com/~hinnant/issue_review/lwg-active.html#579
|
||||
current forerunner for resolving the slow erase by iterator], although
|
||||
there's a strong possibility that this may change in the future. The old
|
||||
method name remains for backwards compatibility but is considered deprecated
|
||||
and will be removed in a future release.
|
||||
* Use Boost.Exception.
|
||||
* Stop using deprecated `BOOST_HAS_*` macros.
|
||||
== Release 1.52.0
|
||||
|
||||
[h2 Boost 1.45.0]
|
||||
* Faster assign, which assigns to existing nodes where possible, rather than
|
||||
creating entirely new nodes and copy constructing.
|
||||
* Fixed bug in `erase_range` ({svn-ticket-url}/7471[#7471^]).
|
||||
* Reverted some of the internal changes to how nodes are created, especially
|
||||
for {cpp}11 compilers. 'construct' and 'destroy' should work a little better
|
||||
for {cpp}11 allocators.
|
||||
* Simplified the implementation a bit. Hopefully more robust.
|
||||
|
||||
* Fix a bug when inserting into an `unordered_map` or `unordered_set` using
|
||||
iterators which returns `value_type` by copy.
|
||||
== Release 1.51.0
|
||||
|
||||
[h2 Boost 1.48.0 - Major update]
|
||||
* Fix construction/destruction issue when using a {cpp}11 compiler with a
|
||||
{cpp}03 allocator ({svn-ticket-url}/7100[#7100^]).
|
||||
* Remove a `try..catch` to support compiling without exceptions.
|
||||
* Adjust SFINAE use to try to support g++ 3.4 ({svn-ticket-url}/7175[#7175^]).
|
||||
* Updated to use the new config macros.
|
||||
|
||||
== Release 1.50.0
|
||||
|
||||
* Fix equality for `unordered_multiset` and `unordered_multimap`.
|
||||
* {svn-ticket-url}/6857[Ticket 6857^]:
|
||||
Implement `reserve`.
|
||||
* {svn-ticket-url}/6771[Ticket 6771^]:
|
||||
Avoid gcc's `-Wfloat-equal` warning.
|
||||
* {svn-ticket-url}/6784[Ticket 6784^]:
|
||||
Fix some Sun specific code.
|
||||
* {svn-ticket-url}/6190[Ticket 6190^]:
|
||||
Avoid gcc's `-Wshadow` warning.
|
||||
* {svn-ticket-url}/6905[Ticket 6905^]:
|
||||
Make namespaces in macros compatible with `bcp` custom namespaces.
|
||||
Fixed by Luke Elliott.
|
||||
* Remove some of the smaller prime number of buckets, as they may make
|
||||
collisions quite probable (e.g. multiples of 5 are very common because
|
||||
we used base 10).
|
||||
* On old versions of Visual {cpp}, use the container library's implementation
|
||||
of `allocator_traits`, as it's more likely to work.
|
||||
* On machines with 64 bit std::size_t, use power of 2 buckets, with Thomas
|
||||
Wang's hash function to pick which one to use. As modulus is very slow
|
||||
for 64 bit values.
|
||||
* Some internal changes.
|
||||
|
||||
== Release 1.49.0
|
||||
|
||||
* Fix warning due to accidental odd assignment.
|
||||
* Slightly better error messages.
|
||||
|
||||
== Release 1.48.0 - Major update
|
||||
|
||||
This is major change which has been converted to use Boost.Move's move
|
||||
emulation, and be more compliant with the C++11 standard. See the
|
||||
[link unordered.compliance compliance section] for details.
|
||||
emulation, and be more compliant with the {cpp}11 standard. See the
|
||||
xref:unordered.adoc#compliance[compliance section] for details.
|
||||
|
||||
The container now meets C++11's complexity requirements, but to do so
|
||||
The container now meets {cpp}11's complexity requirements, but to do so
|
||||
uses a little more memory. This means that `quick_erase` and
|
||||
`erase_return_void` are no longer required, they'll be removed in a
|
||||
future version.
|
||||
|
||||
C++11 support has resulted in some breaking changes:
|
||||
{cpp}11 support has resulted in some breaking changes:
|
||||
|
||||
* Equality comparison has been changed to the C++11 specification.
|
||||
* Equality comparison has been changed to the {cpp}11 specification.
|
||||
In a container with equivalent keys, elements in a group with equal
|
||||
keys used to have to be in the same order to be considered equal,
|
||||
now they can be a permutation of each other. To use the old
|
||||
@@ -168,193 +235,140 @@ C++11 support has resulted in some breaking changes:
|
||||
pointers, rather than the allocator's `pointer` type.
|
||||
|
||||
* `emplace` used to emulate the variadic pair constructors that
|
||||
appeared in early C++0x drafts. Since they were removed it no
|
||||
appeared in early {cpp}0x drafts. Since they were removed it no
|
||||
longer does so. It does emulate the new `piecewise_construct`
|
||||
pair constructors - only you need to use
|
||||
`boost::piecewise_construct`. To use the old emulation of
|
||||
the variadic constructors define
|
||||
`BOOST_UNORDERED_DEPRECATED_PAIR_CONSTRUCT`.
|
||||
|
||||
[h2 Boost 1.49.0]
|
||||
== Release 1.45.0
|
||||
|
||||
* Fix warning due to accidental odd assignment.
|
||||
* Slightly better error messages.
|
||||
* Fix a bug when inserting into an `unordered_map` or `unordered_set` using
|
||||
iterators which returns `value_type` by copy.
|
||||
|
||||
[h2 Boost 1.50.0]
|
||||
== Release 1.43.0
|
||||
|
||||
* Fix equality for `unordered_multiset` and `unordered_multimap`.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6857 Ticket 6857]:
|
||||
Implement `reserve`.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6771 Ticket 6771]:
|
||||
Avoid gcc's `-Wfloat-equal` warning.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6784 Ticket 6784]:
|
||||
Fix some Sun specific code.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6190 Ticket 6190]:
|
||||
Avoid gcc's `-Wshadow` warning.
|
||||
* [@https://svn.boost.org/trac/boost/ticket/6905 Ticket 6905]:
|
||||
Make namespaces in macros compatible with `bcp` custom namespaces.
|
||||
Fixed by Luke Elliott.
|
||||
* Remove some of the smaller prime number of buckets, as they may make
|
||||
collisions quite probable (e.g. multiples of 5 are very common because
|
||||
we used base 10).
|
||||
* On old versions of Visual C++, use the container library's implementation
|
||||
of `allocator_traits`, as it's more likely to work.
|
||||
* On machines with 64 bit std::size_t, use power of 2 buckets, with Thomas
|
||||
Wang's hash function to pick which one to use. As modulus is very slow
|
||||
for 64 bit values.
|
||||
* Some internal changes.
|
||||
* {svn-ticket-url}/3966[Ticket 3966^]:
|
||||
`erase_return_void` is now `quick_erase`, which is the
|
||||
http://home.roadrunner.com/~hinnant/issue_review/lwg-active.html#579[
|
||||
current forerunner for resolving the slow erase by iterator^], although
|
||||
there's a strong possibility that this may change in the future. The old
|
||||
method name remains for backwards compatibility but is considered deprecated
|
||||
and will be removed in a future release.
|
||||
* Use Boost.Exception.
|
||||
* Stop using deprecated `BOOST_HAS_*` macros.
|
||||
|
||||
[h2 Boost 1.51.0]
|
||||
== Release 1.42.0
|
||||
|
||||
* Fix construction/destruction issue when using a C++11 compiler with a
|
||||
C++03 allocator ([ticket 7100]).
|
||||
* Remove a `try..catch` to support compiling without exceptions.
|
||||
* Adjust SFINAE use to try to support g++ 3.4 ([ticket 7175]).
|
||||
* Updated to use the new config macros.
|
||||
* Support instantiating the containers with incomplete value types.
|
||||
* Reduced the number of warnings (mostly in tests).
|
||||
* Improved codegear compatibility.
|
||||
* {svn-ticket-url}/3693[Ticket 3693^]:
|
||||
Add `erase_return_void` as a temporary workaround for the current
|
||||
`erase` which can be inefficient because it has to find the next
|
||||
element to return an iterator.
|
||||
* Add templated find overload for compatible keys.
|
||||
* {svn-ticket-url}/3773[Ticket 3773^]:
|
||||
Add missing `std` qualifier to `ptrdiff_t`.
|
||||
* Some code formatting changes to fit almost all lines into 80 characters.
|
||||
|
||||
[h2 Boost 1.52.0]
|
||||
== Release 1.41.0 - Major update
|
||||
|
||||
* Faster assign, which assigns to existing nodes where possible, rather than
|
||||
creating entirely new nodes and copy constructing.
|
||||
* Fixed bug in `erase_range` ([ticket 7471]).
|
||||
* Reverted some of the internal changes to how nodes are created, especially
|
||||
for C++11 compilers. 'construct' and 'destroy' should work a little better
|
||||
for C++11 allocators.
|
||||
* Simplified the implementation a bit. Hopefully more robust.
|
||||
* The original version made heavy use of macros to sidestep some of the older
|
||||
compilers' poor template support. But since I no longer support those
|
||||
compilers and the macro use was starting to become a maintenance burden it
|
||||
has been rewritten to use templates instead of macros for the implementation
|
||||
classes.
|
||||
|
||||
[h2 Boost 1.53.0]
|
||||
* The container object is now smaller thanks to using `boost::compressed_pair`
|
||||
for EBO and a slightly different function buffer - now using a bool instead
|
||||
of a member pointer.
|
||||
|
||||
* Remove support for the old pre-standard variadic pair constructors, and
|
||||
equality implementation. Both have been deprecated since Boost 1.48.
|
||||
* Remove use of deprecated config macros.
|
||||
* More internal implementation changes, including a much simpler
|
||||
implementation of `erase`.
|
||||
* Buckets are allocated lazily which means that constructing an empty container
|
||||
will not allocate any memory.
|
||||
|
||||
[h2 Boost 1.54.0]
|
||||
== Release 1.40.0
|
||||
|
||||
* Mark methods specified in standard as `noexpect`. More to come in the next
|
||||
release.
|
||||
* If the hash function and equality predicate are known to both have nothrow
|
||||
move assignment or construction then use them.
|
||||
* {svn-ticket-url}/2975[Ticket 2975^]:
|
||||
Store the prime list as a preprocessor sequence - so that it will always get
|
||||
the length right if it changes again in the future.
|
||||
* {svn-ticket-url}/1978[Ticket 1978^]:
|
||||
Implement `emplace` for all compilers.
|
||||
* {svn-ticket-url}/2908[Ticket 2908^],
|
||||
{svn-ticket-url}/3096[Ticket 3096^]:
|
||||
Some workarounds for old versions of borland, including adding explicit
|
||||
destructors to all containers.
|
||||
* {svn-ticket-url}/3082[Ticket 3082^]:
|
||||
Disable incorrect Visual {cpp} warnings.
|
||||
* Better configuration for {cpp}0x features when the headers aren't available.
|
||||
* Create less buckets by default.
|
||||
|
||||
[h2 Boost 1.55.0]
|
||||
== Release 1.39.0
|
||||
|
||||
* Avoid some warnings ([ticket 8851], [ticket 8874]).
|
||||
* Avoid exposing some detail functions via. ADL on the iterators.
|
||||
* Follow the standard by only using the allocators' construct and destroy
|
||||
methods to construct and destroy stored elements. Don't use them for internal
|
||||
data like pointers.
|
||||
* {svn-ticket-url}/2756[Ticket 2756^]: Avoid a warning
|
||||
on Visual {cpp} 2009.
|
||||
* Some other minor internal changes to the implementation, tests and
|
||||
documentation.
|
||||
* Avoid an unnecessary copy in `operator[]`.
|
||||
* {svn-ticket-url}/2975[Ticket 2975^]: Fix length of
|
||||
prime number list.
|
||||
|
||||
[h2 Boost 1.56.0]
|
||||
== Release 1.38.0
|
||||
|
||||
* Fix some shadowed variable warnings ([ticket 9377]).
|
||||
* Fix allocator use in documentation ([ticket 9719]).
|
||||
* Always use prime number of buckets for integers. Fixes performance
|
||||
regression when inserting consecutive integers, although makes other
|
||||
uses slower ([ticket 9282]).
|
||||
* Only construct elements using allocators, as specified in C++11 standard.
|
||||
* Use link:../../../core/swap.html[`boost::swap`^].
|
||||
* {svn-ticket-url}/2237[Ticket 2237^]:
|
||||
Document that the equality and inequality operators are undefined for two
|
||||
objects if their equality predicates aren't equivalent. Thanks to Daniel
|
||||
Krügler.
|
||||
* {svn-ticket-url}/1710[Ticket 1710^]:
|
||||
Use a larger prime number list. Thanks to Thorsten Ottosen and Hervé
|
||||
Brönnimann.
|
||||
* Use
|
||||
link:../../../type_traits/index.html[aligned storage^] to store the types.
|
||||
This changes the way the allocator is used to construct nodes. It used to
|
||||
construct the node with two calls to the allocator's `construct`
|
||||
method - once for the pointers and once for the value. It now constructs
|
||||
the node with a single call to construct and then constructs the value using
|
||||
in place construction.
|
||||
* Add support for {cpp}0x initializer lists where they're available (currently
|
||||
only g++ 4.4 in {cpp}0x mode).
|
||||
|
||||
[h2 Boost 1.57.0]
|
||||
== Release 1.37.0
|
||||
|
||||
* Fix the `pointer` typedef in iterators ([ticket 10672]).
|
||||
* Fix Coverity warning
|
||||
([@https://github.com/boostorg/unordered/pull/2 GitHub #2]).
|
||||
* Rename overload of `emplace` with hint, to `emplace_hint` as specified in
|
||||
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2691.pdf[n2691^].
|
||||
* Provide forwarding headers at `<boost/unordered/unordered_map_fwd.hpp>` and
|
||||
`<boost/unordered/unordered_set_fwd.hpp>`.
|
||||
* Move all the implementation inside `boost/unordered`, to assist
|
||||
modularization and hopefully make it easier to track Release subversion.
|
||||
|
||||
[h2 Boost 1.58.0]
|
||||
== Release 1.36.0
|
||||
|
||||
* Remove unnecessary template parameter from const iterators.
|
||||
* Rename private `iterator` typedef in some iterator classes, as it
|
||||
confuses some traits classes.
|
||||
* Fix move assignment with stateful, propagate_on_container_move_assign
|
||||
allocators ([ticket 10777]).
|
||||
* Fix rare exception safety issue in move assignment.
|
||||
* Fix potential overflow when calculating number of buckets to allocate
|
||||
([@https://github.com/boostorg/unordered/pull/4 GitHub #4]).
|
||||
First official release.
|
||||
|
||||
[h2 Boost 1.62.0]
|
||||
* Rearrange the internals.
|
||||
* Move semantics - full support when rvalue references are available, emulated
|
||||
using a cut down version of the Adobe move library when they are not.
|
||||
* Emplace support when rvalue references and variadic template are available.
|
||||
* More efficient node allocation when rvalue references and variadic template
|
||||
are available.
|
||||
* Added equality operators.
|
||||
|
||||
* Remove use of deprecated `boost::iterator`.
|
||||
* Remove `BOOST_NO_STD_DISTANCE` workaround.
|
||||
* Remove `BOOST_UNORDERED_DEPRECATED_EQUALITY` warning.
|
||||
* Simpler implementation of assignment, fixes an exception safety issue
|
||||
for `unordered_multiset` and `unordered_multimap`. Might be a little slower.
|
||||
* Stop using return value SFINAE which some older compilers have issues
|
||||
with.
|
||||
== Boost 1.35.0 Add-on - 31st March 2008
|
||||
|
||||
[h2 Boost 1.63.0]
|
||||
Unofficial release uploaded to vault, to be used with Boost 1.35.0. Incorporated
|
||||
many of the suggestions from the review.
|
||||
|
||||
* Check hint iterator in `insert`/`emplace_hint`.
|
||||
* Fix some warnings, mostly in the tests.
|
||||
* Manually write out `emplace_args` for small numbers of arguments -
|
||||
should make template error messages a little more bearable.
|
||||
* Remove superfluous use of `boost::forward` in emplace arguments,
|
||||
which fixes emplacing string literals in old versions of Visual C++.
|
||||
* Fix an exception safety issue in assignment. If bucket allocation
|
||||
throws an exception, it can overwrite the hash and equality functions while
|
||||
leaving the existing elements in place. This would mean that the function
|
||||
objects wouldn't match the container elements, so elements might be in the
|
||||
wrong bucket and equivalent elements would be incorrectly handled.
|
||||
* Various reference documentation improvements.
|
||||
* Better allocator support ([ticket 12459]).
|
||||
* Make the no argument constructors implicit.
|
||||
* Implement missing allocator aware constructors.
|
||||
* Fix assigning the hash/key equality functions for empty containers.
|
||||
* Remove unary/binary_function from the examples in the documentation.
|
||||
They are removed in C++17.
|
||||
* Support 10 constructor arguments in emplace. It was meant to support up to 10
|
||||
arguments, but an off by one error in the preprocessor code meant it only
|
||||
supported up to 9.
|
||||
* Improved portability thanks to Boost regression testing.
|
||||
* Fix lots of typos, and clearer text in the documentation.
|
||||
* Fix floating point to `std::size_t` conversion when calculating sizes from
|
||||
the max load factor, and use `double` in the calculation for greater accuracy.
|
||||
* Fix some errors in the examples.
|
||||
|
||||
[h2 Boost 1.64.0]
|
||||
* Initial support for new C++17 member functions:
|
||||
`insert_or_assign` and `try_emplace` in `unordered_map`,
|
||||
* Initial support for `merge` and `extract`.
|
||||
Does not include transferring nodes between
|
||||
`unordered_map` and `unordered_multimap` or between `unordered_set` and
|
||||
`unordered_multiset` yet. That will hopefully be in the next version of
|
||||
Boost.
|
||||
== Review Version
|
||||
|
||||
[h2 Boost 1.65.0]
|
||||
Initial review version, for the review conducted from 7th December 2007 to
|
||||
16th December 2007.
|
||||
|
||||
* Add deprecated attributes to `quick_erase` and `erase_return_void`.
|
||||
I really will remove them in a future version this time.
|
||||
* Small standards compliance fixes:
|
||||
* `noexpect` specs for `swap` free functions.
|
||||
* Add missing `insert(P&&)` methods.
|
||||
|
||||
[h2 Boost 1.66.0]
|
||||
|
||||
* Simpler move construction implementation.
|
||||
* Documentation fixes ([pull_request 6]).
|
||||
|
||||
[h2 Boost 1.67.0]
|
||||
|
||||
* Improved C++17 support:
|
||||
* Add template deduction guides from the standard.
|
||||
* Use a simple implementation of `optional` in node handles, so
|
||||
that they're closer to the standard.
|
||||
* Add missing `noexcept` specifications to `swap`, `operator=`
|
||||
and node handles, and change the implementation to match.
|
||||
Using `std::allocator_traits::is_always_equal`, or our own
|
||||
implementation when not available, and
|
||||
`boost::is_nothrow_swappable` in the implementation.
|
||||
* Improved C++20 support:
|
||||
* Use `boost::to_address`, which has the proposed C++20 semantics,
|
||||
rather than the old custom implementation.
|
||||
* Add `element_type` to iterators, so that `std::pointer_traits`
|
||||
will work.
|
||||
* Use `std::piecewise_construct` on recent versions of Visual C++,
|
||||
and other uses of the Dinkumware standard library,
|
||||
now using Boost.Predef to check compiler and library versions.
|
||||
* Use `std::iterator_traits` rather than the boost iterator traits
|
||||
in order to remove dependency on Boost.Iterator.
|
||||
* Remove iterators' inheritance from `std::iterator`, which is
|
||||
deprecated in C++17, thanks to Daniela Engert
|
||||
([@https://github.com/boostorg/unordered/pull/7 PR#7]).
|
||||
* Stop using `BOOST_DEDUCED_TYPENAME`.
|
||||
* Update some Boost include paths.
|
||||
* Rename some internal methods, and variables.
|
||||
* Various testing improvements.
|
||||
* Miscellaneous internal changes.
|
||||
|
||||
[endsect]
|
||||
@@ -0,0 +1,112 @@
|
||||
[#comparison]
|
||||
|
||||
:idprefix: comparison_
|
||||
|
||||
= Comparison with Associative Containers
|
||||
|
||||
[caption=, title='Table {counter:table-counter} Interface differences']
|
||||
[cols="1,1", frame=all, grid=rows]
|
||||
|===
|
||||
|Associative Containers |Unordered Associative Containers
|
||||
|
||||
|Parameterized by an ordering relation `Compare`
|
||||
|Parameterized by a function object `Hash` and an equivalence relation `Pred`
|
||||
|
||||
|Keys can be compared using `key_compare` which is accessed by member function `key_comp()`, values can be compared using `value_compare` which is accessed by member function `value_comp()`.
|
||||
|Keys can be hashed using `hasher` which is accessed by member function `hash_function()`, and checked for equality using `key_equal` which is accessed by member function `key_eq()`. There is no function object for compared or hashing values.
|
||||
|
||||
|Constructors have optional extra parameters for the comparison object.
|
||||
|Constructors have optional extra parameters for the initial minimum number of buckets, a hash function and an equality object.
|
||||
|
||||
|Keys `k1`, `k2` are considered equivalent if `!Compare(k1, k2) && !Compare(k2, k1)`.
|
||||
|Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`
|
||||
|
||||
|Member function `lower_bound(k)` and `upper_bound(k)`
|
||||
|No equivalent. Since the elements aren't ordered `lower_bound` and `upper_bound` would be meaningless.
|
||||
|
||||
|`equal_range(k)` returns an empty range at the position that `k` would be inserted if `k` isn't present in the container.
|
||||
|`equal_range(k)` returns a range at the end of the container if `k` isn't present in the container. It can't return a positioned range as `k` could be inserted into multiple place. To find out the bucket that `k` would be inserted into use `bucket(k)`. But remember that an insert can cause the container to rehash - meaning that the element can be inserted into a different bucket.
|
||||
|
||||
|`iterator`, `const_iterator` are of the bidirectional category.
|
||||
|`iterator`, `const_iterator` are of at least the forward category.
|
||||
|
||||
|Iterators, pointers and references to the container's elements are never invalidated.
|
||||
|<<buckets_iterator_invalidation,Iterators can be invalidated by calls to insert or rehash>>. Pointers and references to the container's elements are never invalidated.
|
||||
|
||||
|Iterators iterate through the container in the order defined by the comparison object.
|
||||
|Iterators iterate through the container in an arbitrary order, that can change as elements are inserted, although equivalent elements are always adjacent.
|
||||
|
||||
|No equivalent
|
||||
|Local iterators can be used to iterate through individual buckets. (The order of local iterators and iterators aren't required to have any correspondence.)
|
||||
|
||||
|Can be compared using the `==`, `!=`, `<`, `\<=`, `>`, `>=` operators.
|
||||
|Can be compared using the `==` and `!=` operators.
|
||||
|
||||
|
|
||||
|When inserting with a hint, implementations are permitted to ignore the hint.
|
||||
|
||||
|`erase` never throws an exception
|
||||
|The containers' hash or predicate function can throw exceptions from `erase`.
|
||||
|
||||
|===
|
||||
|
||||
---
|
||||
|
||||
[caption=, title='Table {counter:table-counter} Complexity Guarantees']
|
||||
[cols="1,1,1", frame=all, grid=rows]
|
||||
|===
|
||||
|Operation |Associative Containers |Unordered Associative Containers
|
||||
|
||||
|Construction of empty container
|
||||
|constant
|
||||
|O(_n_) where _n_ is the minimum number of buckets.
|
||||
|
||||
|Construction of container from a range of _N_ elements
|
||||
|O(_N log N_), O(_N_) if the range is sorted with `value_comp()`
|
||||
|Average case O(_N_), worst case O(_N^2^_)
|
||||
|
||||
|Insert a single element
|
||||
|logarithmic
|
||||
|Average case constant, worst case linear
|
||||
|
||||
|Insert a single element with a hint
|
||||
|Amortized constant if `t` elements inserted right after hint, logarithmic otherwise
|
||||
|Average case constant, worst case linear (ie. the same as a normal insert).
|
||||
|
||||
|Inserting a range of _N_ elements
|
||||
|_N_ log(`size()` + _N_)
|
||||
|Average case O(_N_), worst case O(_N_ * `size()`)
|
||||
|
||||
|Erase by key, `k`
|
||||
|O(log(`size()`) + `count(k)`)
|
||||
|Average case: O(`count(k)`), Worst case: O(`size()`)
|
||||
|
||||
|Erase a single element by iterator
|
||||
|Amortized constant
|
||||
|Average case: O(1), Worst case: O(`size()`)
|
||||
|
||||
|Erase a range of _N_ elements
|
||||
|O(log(`size()`) + _N_)
|
||||
|Average case: O(_N_), Worst case: O(`size()`)
|
||||
|
||||
|Clearing the container
|
||||
|O(`size()`)
|
||||
|O(`size()`)
|
||||
|
||||
|Find
|
||||
|logarithmic
|
||||
|Average case: O(1), Worst case: O(`size()`)
|
||||
|
||||
|Count
|
||||
|O(log(`size()`) + `count(k)`)
|
||||
|Average case: O(1), Worst case: O(`size()`)
|
||||
|
||||
|`equal_range(k)`
|
||||
|logarithmic
|
||||
|Average case: O(`count(k)`), Worst case: O(`size()`)
|
||||
|
||||
|`lower_bound`,`upper_bound`
|
||||
|logarithmic
|
||||
|n/a
|
||||
|
||||
|===
|
||||
@@ -1,16 +1,17 @@
|
||||
[/ Copyright 2011 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) ]
|
||||
[#compliance]
|
||||
= Standard Compliance
|
||||
|
||||
[section:compliance Standard Compliance]
|
||||
:idprefix: compliance_
|
||||
|
||||
:cpp: C++
|
||||
|
||||
The intent of Boost.Unordered is to implement a close (but imperfect)
|
||||
implementation of the C++17 standard, that will work with C++98 upwards.
|
||||
implementation of the {cpp}17 standard, that will work with {cpp}98 upwards.
|
||||
The wide compatibility does mean some comprimises have to be made.
|
||||
With a compiler and library that fully support C++11, the differences should
|
||||
With a compiler and library that fully support {cpp}11, the differences should
|
||||
be minor.
|
||||
|
||||
[section:move Move emulation]
|
||||
== Move emulation
|
||||
|
||||
Support for move semantics is implemented using Boost.Move. If rvalue
|
||||
references are available it will use them, but if not it uses a close,
|
||||
@@ -22,11 +23,9 @@ but imperfect emulation. On such compilers:
|
||||
* The containers themselves are not movable.
|
||||
* Argument forwarding is not perfect.
|
||||
|
||||
[endsect]
|
||||
== Use of allocators
|
||||
|
||||
[section:allocator_compliance Use of allocators]
|
||||
|
||||
C++11 introduced a new allocator system. It's backwards compatible due to
|
||||
{cpp}11 introduced a new allocator system. It's backwards compatible due to
|
||||
the lax requirements for allocators in the old standard, but might need
|
||||
some changes for allocators which worked with the old versions of the
|
||||
unordered containers.
|
||||
@@ -57,11 +56,9 @@ Due to imperfect move emulation, some assignments might check
|
||||
`propagate_on_container_copy_assignment` on some compilers and
|
||||
`propagate_on_container_move_assignment` on others.
|
||||
|
||||
[endsect]
|
||||
== Construction/Destruction using allocators
|
||||
|
||||
[section:construction Construction/Destruction using allocators]
|
||||
|
||||
The following support is required for full use of C++11 style
|
||||
The following support is required for full use of {cpp}11 style
|
||||
construction/destruction:
|
||||
|
||||
* Variadic templates.
|
||||
@@ -74,15 +71,12 @@ otherwise.
|
||||
|
||||
When this is the case `allocator_traits::construct` and
|
||||
`allocator_traits::destroy` will always be used, apart from when piecewise
|
||||
constructing a `std::pair` using `boost::tuple` (see [link
|
||||
unordered.compliance.pairs below]), but that should be easily avoided.
|
||||
constructing a `std::pair` using `boost::tuple` (see <<compliance_pairs,below>>), but that should be easily avoided.
|
||||
|
||||
When support is not available `allocator_traits::construct` and
|
||||
`allocator_traits::destroy` are never called.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:pointer_traits Pointer Traits]
|
||||
== Pointer Traits
|
||||
|
||||
`pointer_traits` aren't used. Instead, pointer types are obtained from
|
||||
rebound allocators, this can cause problems if the allocator can't be
|
||||
@@ -90,30 +84,28 @@ used with incomplete types. If `const_pointer` is not defined in the
|
||||
allocator, `boost::pointer_to_other<pointer, const value_type>::type`
|
||||
is used to obtain a const pointer.
|
||||
|
||||
[endsect]
|
||||
|
||||
[#unordered.compliance.pairs]
|
||||
[section:pairs Pairs]
|
||||
== Pairs
|
||||
|
||||
Since the containers use `std::pair` they're limited to the version
|
||||
from the current standard library. But since C++11 `std::pair`'s
|
||||
from the current standard library. But since {cpp}11 ``std::pair``'s
|
||||
`piecewise_construct` based constructor is very useful, `emplace`
|
||||
emulates it with a `piecewise_construct` in the `boost::unordered`
|
||||
namespace. So for example, the following will work:
|
||||
|
||||
boost::unordered_multimap<std::string, std::complex> x;
|
||||
[source,c++]
|
||||
----
|
||||
boost::unordered_multimap<std::string, std::complex> x;
|
||||
|
||||
x.emplace(
|
||||
boost::unordered::piecewise_construct,
|
||||
boost::make_tuple("key"), boost::make_tuple(1, 2));
|
||||
x.emplace(
|
||||
boost::unordered::piecewise_construct,
|
||||
boost::make_tuple("key"), boost::make_tuple(1, 2));
|
||||
----
|
||||
|
||||
Older drafts of the standard also supported variadic constructors
|
||||
for `std::pair`, where the first argument would be used for the
|
||||
first part of the pair, and the remaining for the second part.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:misc Miscellaneous]
|
||||
== Miscellaneous
|
||||
|
||||
When swapping, `Pred` and `Hash` are not currently swapped by calling
|
||||
`swap`, their copy constructors are used. As a consequence when swapping
|
||||
@@ -122,7 +114,3 @@ an exception may be thrown from their copy constructor.
|
||||
Variadic constructor arguments for `emplace` are only used when both
|
||||
rvalue references and variadic template parameters are available.
|
||||
Otherwise `emplace` can only take up to 10 constructors arguments.
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
@@ -0,0 +1,12 @@
|
||||
[#copyright]
|
||||
= Copyright and License
|
||||
|
||||
:idprefix: copyright_
|
||||
|
||||
*Daniel James*
|
||||
|
||||
Copyright (C) 2003, 2004 Jeremy B. Maitin-Shepard
|
||||
|
||||
Copyright (C) 2005-2008 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)
|
||||
@@ -0,0 +1,149 @@
|
||||
[#hash_equality]
|
||||
|
||||
:idprefix: hash_equality_
|
||||
|
||||
= Equality Predicates and Hash Functions
|
||||
|
||||
While the associative containers use an ordering relation to specify how the
|
||||
elements are stored, the unordered associative containers use an equality
|
||||
predicate and a hash function. For example, <<unordered_map,boost::unordered_map>>
|
||||
is declared as:
|
||||
|
||||
```
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class unordered_map;
|
||||
```
|
||||
|
||||
The hash function comes first as you might want to change the hash function
|
||||
but not the equality predicate. For example, if you wanted to use the
|
||||
http://www.isthe.com/chongo/tech/comp/fnv/[FNV-1 hash^] you could write:
|
||||
|
||||
```
|
||||
boost::unordered_map<std::string, int, hash::fnv_1>
|
||||
dictionary;
|
||||
```
|
||||
|
||||
There is an link:../../examples/fnv1.hpp[implementation of FNV-1^] in the examples directory.
|
||||
|
||||
If you wish to use a different equality function, you will also need to use a matching hash function. For example, to implement a case insensitive dictionary you need to define a case insensitive equality predicate and hash function:
|
||||
|
||||
```
|
||||
struct iequal_to
|
||||
{
|
||||
bool operator()(std::string const& x,
|
||||
std::string const& y) const
|
||||
{
|
||||
return boost::algorithm::iequals(x, y, std::locale());
|
||||
}
|
||||
};
|
||||
|
||||
struct ihash
|
||||
{
|
||||
std::size_t operator()(std::string const& x) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
std::locale locale;
|
||||
|
||||
for(std::string::const_iterator it = x.begin();
|
||||
it != x.end(); ++it)
|
||||
{
|
||||
boost::hash_combine(seed, std::toupper(*it, locale));
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Which you can then use in a case insensitive dictionary:
|
||||
```
|
||||
boost::unordered_map<std::string, int, ihash, iequal_to>
|
||||
idictionary;
|
||||
```
|
||||
|
||||
This is a simplified version of the example at
|
||||
link:../../examples/case_insensitive.hpp[/libs/unordered/examples/case_insensitive.hpp^] which supports other locales and string types.
|
||||
|
||||
CAUTION: Be careful when using the equality (`==`) operator with custom equality
|
||||
predicates, especially if you're using a function pointer. If you compare two
|
||||
containers with different equality predicates then the result is undefined.
|
||||
For most stateless function objects this is impossible - since you can only
|
||||
compare objects with the same equality predicate you know the equality
|
||||
predicates must be equal. But if you're using function pointers or a stateful
|
||||
equality predicate (e.g. `boost::function`) then you can get into trouble.
|
||||
|
||||
== Custom Types
|
||||
|
||||
Similarly, a custom hash function can be used for custom types:
|
||||
|
||||
```
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
bool operator==(point const& p1, point const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
struct point_hash
|
||||
{
|
||||
std::size_t operator()(point const& p) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, p.x);
|
||||
boost::hash_combine(seed, p.y);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
boost::unordered_multiset<point, point_hash> points;
|
||||
```
|
||||
|
||||
Since the default hash function is link:../../../container_hash/index.html[Boost.Hash^],
|
||||
we can extend it to support the type so that the hash function doesn't need to be explicitly given:
|
||||
|
||||
```
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
bool operator==(point const& p1, point const& p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
std::size_t hash_value(point const& p) {
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, p.x);
|
||||
boost::hash_combine(seed, p.y);
|
||||
return seed;
|
||||
}
|
||||
|
||||
// Now the default function objects work.
|
||||
boost::unordered_multiset<point> points;
|
||||
```
|
||||
|
||||
See the link:../../../container_hash/index.html[Boost.Hash documentation^] for more detail on how to
|
||||
do this. Remember that it relies on extensions to the standard - so it
|
||||
won't work for other implementations of the unordered associative containers,
|
||||
you'll need to explicitly use Boost.Hash.
|
||||
|
||||
[caption=, title='Table {counter:table-counter} Methods for accessing the hash and equality functions']
|
||||
[cols="1,.^1", frame=all, grid=rows]
|
||||
|===
|
||||
|Method |Description
|
||||
|
||||
|`hasher hash_function() const`
|
||||
|Returns the container's hash function.
|
||||
|
||||
|`key_equal key_eq() const`
|
||||
|Returns the container's key equality function..
|
||||
|
||||
|===
|
||||
@@ -0,0 +1,116 @@
|
||||
[#intro]
|
||||
= Introduction
|
||||
|
||||
:idprefix: intro_
|
||||
:cpp: C++
|
||||
|
||||
For accessing data based on key lookup, the {cpp} 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
|
||||
link:https://en.wikipedia.org/wiki/Hash_table[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 {cpp}
|
||||
standard. This is an implementation of the containers described in {cpp}11,
|
||||
with some <<compliance,deviations from the standard>> in
|
||||
order to work with non-{cpp}11 compilers and libraries.
|
||||
|
||||
`unordered_set` and `unordered_multiset` are defined in the header
|
||||
`<boost/unordered_set.hpp>`
|
||||
[source,c++]
|
||||
----
|
||||
namespace boost {
|
||||
template <
|
||||
class Key,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_set;
|
||||
|
||||
template<
|
||||
class Key,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_multiset;
|
||||
}
|
||||
----
|
||||
|
||||
`unordered_map` and `unordered_multimap` are defined in the header
|
||||
`<boost/unordered_map.hpp>`
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
namespace boost {
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class unordered_map;
|
||||
|
||||
template<
|
||||
class Key, class Mapped,
|
||||
class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class unordered_multimap;
|
||||
}
|
||||
----
|
||||
|
||||
When using Boost.TR1, these classes are included from `<unordered_set>` and
|
||||
`<unordered_map>`, with the classes added to the `std::tr1` namespace.
|
||||
|
||||
The containers are used in a similar manner to the normal associative
|
||||
containers:
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
typedef boost::unordered_map<std::string, int> map;
|
||||
map x;
|
||||
x["one"] = 1;
|
||||
x["two"] = 2;
|
||||
x["three"] = 3;
|
||||
|
||||
assert(x.at("one") == 1);
|
||||
assert(x.find("missing") == x.end());
|
||||
----
|
||||
|
||||
But since the elements aren't ordered, the output of:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
BOOST_FOREACH(map::value_type i, x) {
|
||||
std::cout<<i.first<<","<<i.second<<"\n";
|
||||
}
|
||||
----
|
||||
|
||||
can be in any order. For example, it might be:
|
||||
|
||||
[source]
|
||||
----
|
||||
two,2
|
||||
one,1
|
||||
three,3
|
||||
----
|
||||
|
||||
To store an object in an unordered associative container requires both a
|
||||
key equality function and a hash function. The default function objects in
|
||||
the standard containers support a few basic types including integer types,
|
||||
floating point types, pointer types, and the standard strings. Since
|
||||
Boost.Unordered uses link:../../../container_hash/index.html[boost::hash^] it also supports some other types,
|
||||
including standard containers. To use any types not supported by these methods
|
||||
you have to extend Boost.Hash to support the type or use
|
||||
your own custom equality predicates and hash functions. See the
|
||||
<<hash_equality,Equality Predicates and Hash Functions>> section
|
||||
for more details.
|
||||
|
||||
There are other differences, which are listed in the
|
||||
<<comparison,Comparison with Associative Containers>> section.
|
||||
@@ -0,0 +1,68 @@
|
||||
[#rationale]
|
||||
|
||||
:idprefix: rationale_
|
||||
|
||||
= Implementation Rationale
|
||||
|
||||
The intent of this library is to implement the unordered
|
||||
containers in the standard, so the interface was fixed. But there are
|
||||
still some implementation decisions to make. The priorities are
|
||||
conformance to the standard and portability.
|
||||
|
||||
The http://en.wikipedia.org/wiki/Hash_table[Wikipedia article on hash tables^]
|
||||
has a good summary of the implementation issues for hash tables in general.
|
||||
|
||||
== Data Structure
|
||||
|
||||
By specifying an interface for accessing the buckets of the container the
|
||||
standard pretty much requires that the hash table uses chained addressing.
|
||||
|
||||
It would be conceivable to write a hash table that uses another method. For
|
||||
example, it could use open addressing, and use the lookup chain to act as a
|
||||
bucket but there are some serious problems with this:
|
||||
|
||||
* The standard requires that pointers to elements aren't invalidated, so
|
||||
the elements can't be stored in one array, but will need a layer of
|
||||
indirection instead - losing the efficiency and most of the memory gain,
|
||||
the main advantages of open addressing.
|
||||
* Local iterators would be very inefficient and may not be able to
|
||||
meet the complexity requirements.
|
||||
* There are also the restrictions on when iterators can be invalidated. Since
|
||||
open addressing degrades badly when there are a high number of collisions the
|
||||
restrictions could prevent a rehash when it's really needed. The maximum load
|
||||
factor could be set to a fairly low value to work around this - but the
|
||||
standard requires that it is initially set to 1.0.
|
||||
* And since the standard is written with a eye towards chained
|
||||
addressing, users will be surprised if the performance doesn't reflect that.
|
||||
|
||||
So chained addressing is used.
|
||||
|
||||
== Number of Buckets
|
||||
|
||||
There are two popular methods for choosing the number of buckets in a hash
|
||||
table. One is to have a prime number of buckets, another is to use a power
|
||||
of 2.
|
||||
|
||||
Using a prime number of buckets, and choosing a bucket by using the modulus
|
||||
of the hash function's result will usually give a good result. The downside
|
||||
is that the required modulus operation is fairly expensive. This is what the
|
||||
containers used to do in most cases.
|
||||
|
||||
Using a power of 2 allows for much quicker selection of the bucket to use,
|
||||
but at the expense of losing the upper bits of the hash value. For some
|
||||
specially designed hash functions it is possible to do this and still get a
|
||||
good result but as the containers can take arbitrary hash functions this can't
|
||||
be relied on.
|
||||
|
||||
To avoid this a transformation could be applied to the hash function, for an
|
||||
example see
|
||||
http://web.archive.org/web/20121102023700/http://www.concentric.net/~Ttwang/tech/inthash.htm[Thomas Wang's article on integer hash functions^].
|
||||
Unfortunately, a transformation like Wang's requires knowledge of the number
|
||||
of bits in the hash value, so it was only used when `size_t` was 64 bit.
|
||||
|
||||
Since release 1.79.0, https://en.wikipedia.org/wiki/Hash_function#Fibonacci_hashing[Fibonacci hashing]
|
||||
is used instead. With this implementation, the bucket number is determined
|
||||
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.
|
||||
@@ -0,0 +1,7 @@
|
||||
[#reference]
|
||||
= Reference
|
||||
|
||||
include::unordered_map.adoc[]
|
||||
include::unordered_multimap.adoc[]
|
||||
include::unordered_set.adoc[]
|
||||
include::unordered_multiset.adoc[]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -210,7 +210,10 @@ namespace boost {
|
||||
|
||||
// size and capacity
|
||||
|
||||
bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; }
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const BOOST_NOEXCEPT
|
||||
{
|
||||
return table_.size_ == 0;
|
||||
}
|
||||
|
||||
size_type size() const BOOST_NOEXCEPT { return table_.size_; }
|
||||
|
||||
@@ -385,10 +388,9 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class P2>
|
||||
std::pair<iterator, bool> insert(BOOST_RV_REF(P2) obj,
|
||||
typename boost::enable_if_c<
|
||||
boost::is_constructible<value_type, BOOST_RV_REF(P2)>::value,
|
||||
void*>::type = 0)
|
||||
typename boost::enable_if<
|
||||
boost::is_constructible<value_type, BOOST_RV_REF(P2)>,
|
||||
std::pair<iterator, bool> >::type insert(BOOST_RV_REF(P2) obj)
|
||||
{
|
||||
return this->emplace(boost::forward<P2>(obj));
|
||||
}
|
||||
@@ -404,10 +406,9 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class P2>
|
||||
iterator insert(const_iterator hint, BOOST_RV_REF(P2) obj,
|
||||
typename boost::enable_if_c<
|
||||
boost::is_constructible<value_type, BOOST_RV_REF(P2)>::value,
|
||||
void*>::type = 0)
|
||||
typename boost::enable_if<
|
||||
boost::is_constructible<value_type, BOOST_RV_REF(P2)>, iterator>::type
|
||||
insert(const_iterator hint, BOOST_RV_REF(P2) obj)
|
||||
{
|
||||
return this->emplace_hint(hint, boost::forward<P2>(obj));
|
||||
}
|
||||
@@ -428,7 +429,17 @@ 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>
|
||||
typename boost::enable_if_c<
|
||||
detail::transparent_non_iterable<Key, unordered_map>::value,
|
||||
node_type>::type
|
||||
extract(BOOST_FWD_REF(Key) k)
|
||||
{
|
||||
return node_type(table_.extract_by_key_impl(boost::forward<Key>(k)),
|
||||
table_.node_alloc());
|
||||
}
|
||||
|
||||
insert_return_type insert(BOOST_RV_REF(node_type) np)
|
||||
@@ -710,6 +721,17 @@ namespace boost {
|
||||
iterator erase(const_iterator);
|
||||
size_type erase(const key_type&);
|
||||
iterator erase(const_iterator, const_iterator);
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<
|
||||
detail::transparent_non_iterable<Key, unordered_map>::value,
|
||||
size_type>::type
|
||||
erase(BOOST_FWD_REF(Key) k)
|
||||
{
|
||||
return table_.erase_key_unique_impl(
|
||||
this->key_eq(), boost::forward<Key>(k));
|
||||
}
|
||||
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
void quick_erase(const_iterator it) { erase(it); }
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
@@ -747,6 +769,26 @@ namespace boost {
|
||||
iterator find(const key_type&);
|
||||
const_iterator find(const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
iterator>::type
|
||||
find(const Key& key)
|
||||
{
|
||||
return iterator(table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq()));
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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()));
|
||||
}
|
||||
|
||||
template <class CompatibleKey, class CompatibleHash,
|
||||
class CompatiblePredicate>
|
||||
iterator find(CompatibleKey const&, CompatibleHash const&,
|
||||
@@ -757,12 +799,70 @@ namespace boost {
|
||||
const_iterator find(CompatibleKey const&, CompatibleHash const&,
|
||||
CompatiblePredicate const&) const;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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());
|
||||
}
|
||||
|
||||
size_type count(const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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);
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const key_type&);
|
||||
std::pair<const_iterator, const_iterator> equal_range(
|
||||
const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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_node(p) : p));
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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());
|
||||
|
||||
return std::make_pair(
|
||||
const_iterator(p), const_iterator(p ? table::next_node(p) : p));
|
||||
}
|
||||
|
||||
mapped_type& operator[](const key_type&);
|
||||
mapped_type& operator[](BOOST_RV_REF(key_type));
|
||||
mapped_type& at(const key_type&);
|
||||
@@ -1080,7 +1180,10 @@ namespace boost {
|
||||
|
||||
// size and capacity
|
||||
|
||||
bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; }
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const BOOST_NOEXCEPT
|
||||
{
|
||||
return table_.size_ == 0;
|
||||
}
|
||||
|
||||
size_type size() const BOOST_NOEXCEPT { return table_.size_; }
|
||||
|
||||
@@ -1249,10 +1352,9 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class P2>
|
||||
iterator insert(BOOST_RV_REF(P2) obj,
|
||||
typename boost::enable_if_c<
|
||||
boost::is_constructible<value_type, BOOST_RV_REF(P2)>::value,
|
||||
void*>::type = 0)
|
||||
typename boost::enable_if<
|
||||
boost::is_constructible<value_type, BOOST_RV_REF(P2)>,
|
||||
iterator>::type insert(BOOST_RV_REF(P2) obj)
|
||||
{
|
||||
return this->emplace(boost::forward<P2>(obj));
|
||||
}
|
||||
@@ -1268,10 +1370,10 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class P2>
|
||||
iterator insert(const_iterator hint, BOOST_RV_REF(P2) obj,
|
||||
typename boost::enable_if_c<
|
||||
boost::is_constructible<value_type, BOOST_RV_REF(P2)>::value,
|
||||
void*>::type = 0)
|
||||
typename boost::enable_if<
|
||||
boost::is_constructible<value_type, BOOST_RV_REF(P2)>,
|
||||
iterator>::type
|
||||
insert(const_iterator hint, BOOST_RV_REF(P2) obj)
|
||||
{
|
||||
return this->emplace_hint(hint, boost::forward<P2>(obj));
|
||||
}
|
||||
@@ -1295,6 +1397,15 @@ namespace boost {
|
||||
return node_type(table_.extract_by_key(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<
|
||||
detail::transparent_non_iterable<Key, unordered_multimap>::value,
|
||||
node_type>::type
|
||||
extract(const Key& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
iterator insert(BOOST_RV_REF(node_type) np)
|
||||
{
|
||||
return table_.move_insert_node_type_equiv(np);
|
||||
@@ -1319,6 +1430,17 @@ namespace boost {
|
||||
iterator erase(const_iterator);
|
||||
size_type erase(const key_type&);
|
||||
iterator erase(const_iterator, const_iterator);
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<
|
||||
detail::transparent_non_iterable<Key, unordered_multimap>::value,
|
||||
size_type>::type
|
||||
erase(BOOST_FWD_REF(Key) k)
|
||||
{
|
||||
return table_.erase_key_equiv_impl(
|
||||
this->key_eq(), boost::forward<Key>(k));
|
||||
}
|
||||
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
void quick_erase(const_iterator it) { erase(it); }
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
@@ -1356,6 +1478,26 @@ namespace boost {
|
||||
iterator find(const key_type&);
|
||||
const_iterator find(const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
iterator>::type
|
||||
find(const Key& key)
|
||||
{
|
||||
return iterator(table_.find_node_impl(
|
||||
table::policy::apply_hash(this->hash_function(), key), key,
|
||||
this->key_eq()));
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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()));
|
||||
}
|
||||
|
||||
template <class CompatibleKey, class CompatibleHash,
|
||||
class CompatiblePredicate>
|
||||
iterator find(CompatibleKey const&, CompatibleHash const&,
|
||||
@@ -1366,12 +1508,67 @@ namespace boost {
|
||||
const_iterator find(CompatibleKey const&, CompatibleHash const&,
|
||||
CompatiblePredicate const&) const;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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());
|
||||
}
|
||||
|
||||
size_type count(const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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;
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const key_type&);
|
||||
std::pair<const_iterator, const_iterator> equal_range(
|
||||
const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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));
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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());
|
||||
|
||||
return std::make_pair(
|
||||
const_iterator(p), const_iterator(p ? table_.next_group(p) : p));
|
||||
}
|
||||
|
||||
// bucket interface
|
||||
|
||||
size_type bucket_count() const BOOST_NOEXCEPT
|
||||
@@ -1719,7 +1916,7 @@ namespace boost {
|
||||
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(k);
|
||||
return table_.erase_key_unique_impl(this->key_eq(), k);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@@ -1977,6 +2174,13 @@ namespace boost {
|
||||
m1.swap(m2);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_map<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_map<K, T, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return detail::erase_if(c, pred);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@@ -2422,6 +2626,13 @@ namespace boost {
|
||||
m1.swap(m2);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_multimap<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_multimap<K, T, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return detail::erase_if(c, pred);
|
||||
}
|
||||
|
||||
template <typename N, class K, class T, class A> class node_handle_map
|
||||
{
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(node_handle_map)
|
||||
@@ -2514,7 +2725,10 @@ namespace boost {
|
||||
|
||||
bool operator!() const BOOST_NOEXCEPT { return ptr_ ? 0 : 1; }
|
||||
|
||||
bool empty() const BOOST_NOEXCEPT { return ptr_ ? 0 : 1; }
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const BOOST_NOEXCEPT
|
||||
{
|
||||
return ptr_ ? 0 : 1;
|
||||
}
|
||||
|
||||
void swap(node_handle_map& n) BOOST_NOEXCEPT_IF(
|
||||
value_allocator_traits::propagate_on_container_swap::value ||
|
||||
|
||||
@@ -34,6 +34,10 @@ namespace boost {
|
||||
unordered_map<K, T, H, P, A>& m1, unordered_map<K, T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_map<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_map<K, T, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class K, class T, class H = boost::hash<K>,
|
||||
class P = std::equal_to<K>,
|
||||
class A = std::allocator<std::pair<const K, T> > >
|
||||
@@ -50,6 +54,10 @@ namespace boost {
|
||||
unordered_multimap<K, T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_multimap<K, T, H, P, A>::size_type erase_if(
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -208,7 +208,10 @@ namespace boost {
|
||||
|
||||
// size and capacity
|
||||
|
||||
bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; }
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const BOOST_NOEXCEPT
|
||||
{
|
||||
return table_.size_ == 0;
|
||||
}
|
||||
|
||||
size_type size() const BOOST_NOEXCEPT { return table_.size_; }
|
||||
|
||||
@@ -411,6 +414,15 @@ namespace boost {
|
||||
return node_type(table_.extract_by_key(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<
|
||||
detail::transparent_non_iterable<Key, unordered_set>::value,
|
||||
node_type>::type
|
||||
extract(const Key& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
insert_return_type insert(BOOST_RV_REF(node_type) np)
|
||||
{
|
||||
insert_return_type result;
|
||||
@@ -436,6 +448,17 @@ namespace boost {
|
||||
iterator erase(const_iterator);
|
||||
size_type erase(const key_type&);
|
||||
iterator erase(const_iterator, const_iterator);
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<
|
||||
detail::transparent_non_iterable<Key, unordered_set>::value,
|
||||
size_type>::type
|
||||
erase(BOOST_FWD_REF(Key) k)
|
||||
{
|
||||
return table_.erase_key_unique_impl(
|
||||
this->key_eq(), boost::forward<Key>(k));
|
||||
}
|
||||
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
void quick_erase(const_iterator it) { erase(it); }
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
@@ -472,16 +495,68 @@ namespace boost {
|
||||
|
||||
const_iterator find(const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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()));
|
||||
}
|
||||
|
||||
template <class CompatibleKey, class CompatibleHash,
|
||||
class CompatiblePredicate>
|
||||
const_iterator find(CompatibleKey const&, CompatibleHash const&,
|
||||
CompatiblePredicate const&) const;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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());
|
||||
}
|
||||
|
||||
size_type count(const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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;
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(
|
||||
const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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());
|
||||
|
||||
return std::make_pair(
|
||||
const_iterator(n), const_iterator(n ? table::next_node(n) : n));
|
||||
}
|
||||
|
||||
// bucket interface
|
||||
|
||||
size_type bucket_count() const BOOST_NOEXCEPT
|
||||
@@ -763,7 +838,10 @@ namespace boost {
|
||||
|
||||
// size and capacity
|
||||
|
||||
bool empty() const BOOST_NOEXCEPT { return table_.size_ == 0; }
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const BOOST_NOEXCEPT
|
||||
{
|
||||
return table_.size_ == 0;
|
||||
}
|
||||
|
||||
size_type size() const BOOST_NOEXCEPT { return table_.size_; }
|
||||
|
||||
@@ -960,6 +1038,15 @@ namespace boost {
|
||||
return node_type(table_.extract_by_key(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<
|
||||
detail::transparent_non_iterable<Key, unordered_multiset>::value,
|
||||
node_type>::type
|
||||
extract(const Key& k)
|
||||
{
|
||||
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
|
||||
}
|
||||
|
||||
iterator insert(BOOST_RV_REF(node_type) np)
|
||||
{
|
||||
return table_.move_insert_node_type_equiv(np);
|
||||
@@ -982,6 +1069,16 @@ namespace boost {
|
||||
|
||||
iterator erase(const_iterator);
|
||||
size_type erase(const key_type&);
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<
|
||||
detail::transparent_non_iterable<Key, unordered_multiset>::value,
|
||||
size_type>::type
|
||||
erase(const Key& k)
|
||||
{
|
||||
return table_.erase_key_equiv_impl(this->key_eq(), k);
|
||||
}
|
||||
|
||||
iterator erase(const_iterator, const_iterator);
|
||||
BOOST_UNORDERED_DEPRECATED("Use erase instead")
|
||||
void quick_erase(const_iterator it) { erase(it); }
|
||||
@@ -1019,16 +1116,68 @@ namespace boost {
|
||||
|
||||
const_iterator find(const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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()));
|
||||
}
|
||||
|
||||
template <class CompatibleKey, class CompatibleHash,
|
||||
class CompatiblePredicate>
|
||||
const_iterator find(CompatibleKey const&, CompatibleHash const&,
|
||||
CompatiblePredicate const&) const;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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());
|
||||
}
|
||||
|
||||
size_type count(const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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;
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(
|
||||
const key_type&) const;
|
||||
|
||||
template <class Key>
|
||||
typename boost::enable_if_c<detail::are_transparent<Key, H, P>::value,
|
||||
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));
|
||||
}
|
||||
|
||||
// bucket interface
|
||||
|
||||
size_type bucket_count() const BOOST_NOEXCEPT
|
||||
@@ -1345,7 +1494,7 @@ namespace boost {
|
||||
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(k);
|
||||
return table_.erase_key_unique_impl(this->key_eq(), k);
|
||||
}
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@@ -1533,6 +1682,13 @@ namespace boost {
|
||||
m1.swap(m2);
|
||||
}
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_set<K, H, P, A>::size_type erase_if(
|
||||
unordered_set<K, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return detail::erase_if(c, pred);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@@ -1936,6 +2092,13 @@ namespace boost {
|
||||
m1.swap(m2);
|
||||
}
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_multiset<K, H, P, A>::size_type erase_if(
|
||||
unordered_multiset<K, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return detail::erase_if(c, pred);
|
||||
}
|
||||
|
||||
template <typename N, typename T, typename A> class node_handle_set
|
||||
{
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(node_handle_set)
|
||||
@@ -2026,7 +2189,10 @@ namespace boost {
|
||||
|
||||
bool operator!() const BOOST_NOEXCEPT { return ptr_ ? 0 : 1; }
|
||||
|
||||
bool empty() const BOOST_NOEXCEPT { return ptr_ ? 0 : 1; }
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const BOOST_NOEXCEPT
|
||||
{
|
||||
return ptr_ ? 0 : 1;
|
||||
}
|
||||
|
||||
void swap(node_handle_set& n) BOOST_NOEXCEPT_IF(
|
||||
value_allocator_traits::propagate_on_container_swap::value ||
|
||||
|
||||
@@ -33,6 +33,10 @@ namespace boost {
|
||||
unordered_set<T, H, P, A>& m1, unordered_set<T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_set<K, H, P, A>::size_type erase_if(
|
||||
unordered_set<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class T, class H = boost::hash<T>, class P = std::equal_to<T>,
|
||||
class A = std::allocator<T> >
|
||||
class unordered_multiset;
|
||||
@@ -48,6 +52,10 @@ namespace boost {
|
||||
unordered_multiset<T, H, P, A>& m1, unordered_multiset<T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_multiset<K, H, P, A>::size_type erase_if(
|
||||
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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -7,10 +7,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; URL=../../doc/html/unordered.html">
|
||||
<meta http-equiv="refresh" content="0; URL=doc/html/unordered.html">
|
||||
</head>
|
||||
<body>
|
||||
Automatic redirection failed, please go to
|
||||
<a href="../../doc/html/unordered.html">../../doc/html/unordered.html</a>
|
||||
<a href="doc/html/unordered.html">doc/html/unordered.html</a>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+2
-1
@@ -13,5 +13,6 @@
|
||||
],
|
||||
"category": [
|
||||
"Containers"
|
||||
]
|
||||
],
|
||||
"cxxstd": "03"
|
||||
}
|
||||
+25
-12
@@ -5,17 +5,27 @@
|
||||
|
||||
import testing ;
|
||||
|
||||
project unordered-test/unordered
|
||||
# Adding -Wundef is blocked on (at least)
|
||||
# https://github.com/boostorg/type_traits/issues/165
|
||||
|
||||
local gcc-flags = -Wsign-promo -Wconversion -Wsign-conversion -Wfloat-equal -Wshadow -Wno-variadic-macros ;
|
||||
local clang-flags = $(gcc-flags) -Wno-c99-extensions ;
|
||||
local msvc-flags = /wd4494 ;
|
||||
|
||||
project
|
||||
: requirements
|
||||
<warnings>all
|
||||
|
||||
<warnings>pedantic
|
||||
<toolset>intel:<warnings>on
|
||||
# Would be nice to define -Wundef, but I'm getting warnings from
|
||||
# Boost.Preprocessor on trunk.
|
||||
<toolset>gcc:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wconversion -Wfloat-equal -Wshadow"
|
||||
<toolset>darwin:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wconversion -Wfloat-equal -Wshadow"
|
||||
<toolset>clang:<cxxflags>"-pedantic -Wstrict-aliasing -fstrict-aliasing -Wextra -Wsign-promo -Wunused-parameter -Wsign-conversion -Wconversion -Wfloat-equal -Wshadow"
|
||||
<toolset>msvc:<cxxflags>"/wd4494"
|
||||
<toolset>gcc:<c++-template-depth>500
|
||||
|
||||
<toolset>gcc:<cxxflags>$(gcc-flags)
|
||||
<toolset>darwin:<cxxflags>$(gcc-flags)
|
||||
<toolset>clang:<cxxflags>$(clang-flags)
|
||||
<toolset>msvc:<cxxflags>$(msvc-flags)
|
||||
|
||||
<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 ;
|
||||
@@ -29,9 +39,6 @@ test-suite unordered
|
||||
[ run unordered/minimal_allocator.cpp ]
|
||||
[ run unordered/compile_set.cpp ]
|
||||
[ run unordered/compile_map.cpp ]
|
||||
[ run unordered/compile_map.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_ALLOCATOR_TRAITS=0
|
||||
: compile_map_unordered_allocator ]
|
||||
[ run unordered/noexcept_tests.cpp ]
|
||||
[ run unordered/link_test_1.cpp unordered/link_test_2.cpp ]
|
||||
[ run unordered/incomplete_test.cpp ]
|
||||
@@ -64,6 +71,12 @@ test-suite unordered
|
||||
[ 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
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
#pragma warning(disable : 4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) && defined(__has_warning)
|
||||
#if __has_warning("-Wself-assign-overloaded")
|
||||
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
test::seed_t initialize_seed(12847);
|
||||
|
||||
template <class T> struct self_assign_base : public test::exception_base
|
||||
|
||||
@@ -113,6 +113,10 @@ namespace test {
|
||||
ptr_ = ptr_->next_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(list_iterator y) const { return ptr_ == y.ptr_; }
|
||||
bool operator!=(list_iterator y) const { return ptr_ != y.ptr_; }
|
||||
|
||||
bool operator==(const_iterator y) const { return ptr_ == y.ptr_; }
|
||||
bool operator!=(const_iterator y) const { return ptr_ != y.ptr_; }
|
||||
};
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace test {
|
||||
{
|
||||
}
|
||||
|
||||
~memory_tracker() { BOOST_TEST(count_allocators == 0); }
|
||||
~memory_tracker() { BOOST_ASSERT(count_allocators == 0); }
|
||||
|
||||
void allocator_ref()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/type_traits/declval.hpp>
|
||||
|
||||
namespace test {
|
||||
template <class Container>
|
||||
@@ -23,7 +24,9 @@ namespace test {
|
||||
BOOST_STATIC_CONSTANT(bool,
|
||||
value = sizeof(long) ==
|
||||
sizeof(flip(
|
||||
((Container*)0)->insert(*(typename Container::value_type*)0))));
|
||||
(boost::declval<Container*>())
|
||||
->insert(
|
||||
boost::declval<typename Container::value_type const&>()))));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
|
||||
// Include this after the boost headers, but before other test headers.
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
|
||||
@@ -137,8 +137,6 @@ namespace test {
|
||||
<< BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT << "\n" \
|
||||
<< "BOOST_UNORDERED_EMPLACE_LIMIT: " << BOOST_UNORDERED_EMPLACE_LIMIT \
|
||||
<< "\n" \
|
||||
<< "BOOST_UNORDERED_USE_ALLOCATOR_TRAITS: " \
|
||||
<< BOOST_UNORDERED_USE_ALLOCATOR_TRAITS << "\n" \
|
||||
<< "BOOST_UNORDERED_CXX11_CONSTRUCTION: " \
|
||||
<< BOOST_UNORDERED_CXX11_CONSTRUCTION << "\n\n" \
|
||||
<< std::flush; \
|
||||
|
||||
@@ -30,6 +30,11 @@ namespace test {
|
||||
typedef test::less type;
|
||||
};
|
||||
|
||||
template<class T> struct equals_to_compare< std::equal_to<T> >
|
||||
{
|
||||
typedef std::less<T> type;
|
||||
};
|
||||
|
||||
template <class X1, class X2> void compare_range(X1 const& x1, X2 const& x2)
|
||||
{
|
||||
typedef test::list<typename X1::value_type> value_list;
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
#include "../helpers/fwd.hpp"
|
||||
#include "../helpers/memory.hpp"
|
||||
|
||||
namespace test {
|
||||
namespace test
|
||||
{
|
||||
struct allocator_false
|
||||
{
|
||||
enum
|
||||
@@ -180,6 +181,10 @@ namespace test {
|
||||
|
||||
~cxx11_allocator_base() { detail::tracker.allocator_unref(); }
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
|
||||
cxx11_allocator_base& operator=(cxx11_allocator_base const& x) = default;
|
||||
#endif
|
||||
|
||||
pointer address(reference r) { return pointer(&r); }
|
||||
|
||||
const_pointer address(const_reference r) { return const_pointer(&r); }
|
||||
@@ -264,6 +269,10 @@ namespace test {
|
||||
|
||||
cxx11_allocator(cxx11_allocator const& x) : cxx11_allocator_base<T>(x) {}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
|
||||
cxx11_allocator& operator=(cxx11_allocator const& x) = default;
|
||||
#endif
|
||||
|
||||
// When not propagating swap, allocators are always equal
|
||||
// to avoid undefined behaviour.
|
||||
bool operator==(cxx11_allocator const& x) const
|
||||
@@ -307,6 +316,10 @@ namespace test {
|
||||
|
||||
cxx11_allocator(cxx11_allocator const& x) : cxx11_allocator_base<T>(x) {}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
|
||||
cxx11_allocator& operator=(cxx11_allocator const& x) = default;
|
||||
#endif
|
||||
|
||||
// When not propagating swap, allocators are always equal
|
||||
// to avoid undefined behaviour.
|
||||
bool operator==(cxx11_allocator const& x) const
|
||||
|
||||
@@ -194,18 +194,19 @@ namespace test {
|
||||
|
||||
std::size_t hash_impl(object const& x) const
|
||||
{
|
||||
int result;
|
||||
unsigned result;
|
||||
switch (tag_) {
|
||||
case 1:
|
||||
result = x.tag1_;
|
||||
result = static_cast<unsigned>(x.tag1_);
|
||||
break;
|
||||
case 2:
|
||||
result = x.tag2_;
|
||||
result = static_cast<unsigned>(x.tag2_);
|
||||
break;
|
||||
default:
|
||||
result = x.tag1_ + x.tag2_;
|
||||
result =
|
||||
static_cast<unsigned>(x.tag1_) + static_cast<unsigned>(x.tag2_);
|
||||
}
|
||||
return static_cast<std::size_t>(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
friend bool operator==(hash const& x1, hash const& x2)
|
||||
|
||||
+12
-10
@@ -195,34 +195,36 @@ namespace test {
|
||||
|
||||
std::size_t operator()(object const& x) const
|
||||
{
|
||||
int result;
|
||||
unsigned result;
|
||||
switch (type_) {
|
||||
case 1:
|
||||
result = x.tag1_;
|
||||
result = static_cast<unsigned>(x.tag1_);
|
||||
break;
|
||||
case 2:
|
||||
result = x.tag2_;
|
||||
result = static_cast<unsigned>(x.tag2_);
|
||||
break;
|
||||
default:
|
||||
result = x.tag1_ + x.tag2_;
|
||||
result =
|
||||
static_cast<unsigned>(x.tag1_) + static_cast<unsigned>(x.tag2_);
|
||||
}
|
||||
return static_cast<std::size_t>(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t operator()(movable const& x) const
|
||||
{
|
||||
int result;
|
||||
unsigned result;
|
||||
switch (type_) {
|
||||
case 1:
|
||||
result = x.tag1_;
|
||||
result = static_cast<unsigned>(x.tag1_);
|
||||
break;
|
||||
case 2:
|
||||
result = x.tag2_;
|
||||
result = static_cast<unsigned>(x.tag2_);
|
||||
break;
|
||||
default:
|
||||
result = x.tag1_ + x.tag2_;
|
||||
result =
|
||||
static_cast<unsigned>(x.tag1_) + static_cast<unsigned>(x.tag2_);
|
||||
}
|
||||
return static_cast<std::size_t>(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t operator()(int x) const
|
||||
|
||||
@@ -65,9 +65,9 @@
|
||||
{ \
|
||||
return (std::numeric_limits<size_type>::max)(); \
|
||||
} \
|
||||
bool operator==(name<T> const&) { return true; } \
|
||||
bool operator!=(name<T> const&) { return false; } \
|
||||
/**/
|
||||
bool operator==(name<T> const&) const { return true; } \
|
||||
bool operator!=(name<T> const&) const { return false; } \
|
||||
/**/
|
||||
|
||||
struct yes_type
|
||||
{
|
||||
@@ -111,7 +111,7 @@ void test_empty_allocator()
|
||||
{
|
||||
typedef empty_allocator<int> allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<allocator> traits;
|
||||
#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS == 1
|
||||
#if !defined(BOOST_NO_CXX11_ALLOCATOR)
|
||||
BOOST_STATIC_ASSERT((boost::is_same<traits::size_type,
|
||||
std::make_unsigned<std::ptrdiff_t>::type>::value));
|
||||
#else
|
||||
@@ -153,7 +153,7 @@ void test_allocator1()
|
||||
{
|
||||
typedef allocator1<int> allocator;
|
||||
typedef boost::unordered::detail::allocator_traits<allocator> traits;
|
||||
#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS == 1
|
||||
#if !defined(BOOST_NO_CXX11_ALLOCATOR)
|
||||
BOOST_STATIC_ASSERT((boost::is_same<traits::size_type,
|
||||
std::make_unsigned<std::ptrdiff_t>::type>::value));
|
||||
#else
|
||||
@@ -213,7 +213,15 @@ void test_allocator2()
|
||||
BOOST_TEST(!traits::propagate_on_container_move_assignment::value);
|
||||
BOOST_TEST(!traits::propagate_on_container_swap::value);
|
||||
BOOST_TEST(!traits::is_always_equal::value);
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_ALLOCATOR)
|
||||
// conditionally compile this assertion as all C++03 emulations of expression
|
||||
// SFINAE are broken one way or another and the benefits of using Core's
|
||||
// `allocator_traits` outweigh the costs of breaking this kind of code (i.e.
|
||||
// inheriting SOCCC via a base)
|
||||
//
|
||||
BOOST_TEST(call_select<allocator>() == 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
// allocator 3
|
||||
@@ -255,7 +263,9 @@ template <typename T> struct allocator3
|
||||
allocator3<T> select_on_container_copy_construction() const
|
||||
{
|
||||
++selected;
|
||||
return allocator3<T>();
|
||||
allocator3<T> a;
|
||||
a.x = 0;
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
#pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) && defined(__has_warning)
|
||||
#if __has_warning("-Wself-assign-overloaded")
|
||||
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace assign_tests {
|
||||
|
||||
test::seed_t initialize_seed(96785);
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
// Copyright 2021 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"
|
||||
|
||||
struct key
|
||||
{
|
||||
int x_;
|
||||
static int count_;
|
||||
|
||||
key(int x) : x_(x) { ++count_; }
|
||||
key(key const& k) : x_(k.x_) { ++count_; }
|
||||
};
|
||||
|
||||
int key::count_;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, key const& k)
|
||||
{
|
||||
os << "key { x_: " << k.x_ << " }";
|
||||
return os;
|
||||
}
|
||||
|
||||
bool operator==(key const& k, int const x) { return k.x_ == x; }
|
||||
bool operator==(int const x, key const& k) { return k.x_ == x; }
|
||||
|
||||
struct transparent_hasher
|
||||
{
|
||||
typedef void is_transparent;
|
||||
|
||||
std::size_t operator()(key const& k) const
|
||||
{
|
||||
return boost::hash<int>()(k.x_);
|
||||
}
|
||||
|
||||
std::size_t operator()(int const k) const { return boost::hash<int>()(k); }
|
||||
};
|
||||
|
||||
struct transparent_key_equal
|
||||
{
|
||||
typedef void is_transparent;
|
||||
|
||||
bool operator()(key const& k1, key const& k2) const { return k1.x_ == k2.x_; }
|
||||
bool operator()(int const x, key const& k1) const { return k1 == x; }
|
||||
bool operator()(key const& k1, int const x) const { return k1 == x; }
|
||||
};
|
||||
|
||||
struct hasher
|
||||
{
|
||||
std::size_t operator()(key const& k) const
|
||||
{
|
||||
return boost::hash<int>()(k.x_);
|
||||
}
|
||||
};
|
||||
|
||||
struct key_equal
|
||||
{
|
||||
bool operator()(key const& k1, key const& k2) const { return k1.x_ == k2.x_; }
|
||||
};
|
||||
|
||||
void count_reset() { key::count_ = 0; }
|
||||
|
||||
template <class UnorderedMap> void test_map_transparent_contains()
|
||||
{
|
||||
count_reset();
|
||||
|
||||
UnorderedMap map;
|
||||
bool contains = map.contains(0);
|
||||
BOOST_TEST(!contains);
|
||||
|
||||
BOOST_TEST_EQ(key::count_, 0);
|
||||
|
||||
map.insert(std::make_pair(0, 1337));
|
||||
map.insert(std::make_pair(0, 1338));
|
||||
map.insert(std::make_pair(0, 1339));
|
||||
map.insert(std::make_pair(1, 1340));
|
||||
|
||||
int const expected_key_count = key::count_;
|
||||
|
||||
contains = map.contains(0);
|
||||
BOOST_TEST(contains);
|
||||
|
||||
contains = map.contains(1);
|
||||
BOOST_TEST(contains);
|
||||
|
||||
contains = map.contains(2);
|
||||
BOOST_TEST(!contains);
|
||||
|
||||
BOOST_TEST_EQ(key::count_, expected_key_count);
|
||||
}
|
||||
|
||||
template <class UnorderedMap> void test_map_non_transparent_contains()
|
||||
{
|
||||
count_reset();
|
||||
|
||||
UnorderedMap map;
|
||||
bool contains = map.contains(0);
|
||||
BOOST_TEST(!contains);
|
||||
BOOST_TEST_EQ(key::count_, 1);
|
||||
|
||||
map.insert(std::make_pair(0, 1337));
|
||||
map.insert(std::make_pair(0, 1338));
|
||||
map.insert(std::make_pair(0, 1339));
|
||||
map.insert(std::make_pair(1, 1340));
|
||||
|
||||
int key_count = key::count_;
|
||||
|
||||
contains = map.contains(0);
|
||||
++key_count;
|
||||
BOOST_TEST(contains);
|
||||
|
||||
contains = map.contains(1);
|
||||
++key_count;
|
||||
BOOST_TEST(contains);
|
||||
|
||||
contains = map.contains(2);
|
||||
++key_count;
|
||||
BOOST_TEST(!contains);
|
||||
|
||||
BOOST_TEST_EQ(key::count_, key_count);
|
||||
}
|
||||
|
||||
void test_map()
|
||||
{
|
||||
typedef boost::unordered_map<key, int, transparent_hasher,
|
||||
transparent_key_equal>
|
||||
transparent_map;
|
||||
|
||||
typedef boost::unordered_map<key, int, transparent_hasher, key_equal>
|
||||
non_transparent_map1;
|
||||
|
||||
typedef boost::unordered_map<key, int, hasher, transparent_key_equal>
|
||||
non_transparent_map2;
|
||||
|
||||
typedef boost::unordered_map<key, int, hasher, key_equal>
|
||||
non_transparent_map3;
|
||||
|
||||
test_map_transparent_contains<transparent_map>();
|
||||
test_map_non_transparent_contains<non_transparent_map1>();
|
||||
test_map_non_transparent_contains<non_transparent_map2>();
|
||||
test_map_non_transparent_contains<non_transparent_map3>();
|
||||
}
|
||||
|
||||
void test_multimap()
|
||||
{
|
||||
typedef boost::unordered_multimap<key, int, transparent_hasher,
|
||||
transparent_key_equal>
|
||||
transparent_multimap;
|
||||
|
||||
typedef boost::unordered_multimap<key, int, transparent_hasher, key_equal>
|
||||
non_transparent_multimap1;
|
||||
|
||||
typedef boost::unordered_multimap<key, int, hasher, transparent_key_equal>
|
||||
non_transparent_multimap2;
|
||||
|
||||
typedef boost::unordered_multimap<key, int, hasher, key_equal>
|
||||
non_transparent_multimap3;
|
||||
|
||||
test_map_transparent_contains<transparent_multimap>();
|
||||
test_map_non_transparent_contains<non_transparent_multimap1>();
|
||||
test_map_non_transparent_contains<non_transparent_multimap2>();
|
||||
test_map_non_transparent_contains<non_transparent_multimap3>();
|
||||
}
|
||||
|
||||
template <class UnorderedSet> void test_set_transparent_contains()
|
||||
{
|
||||
count_reset();
|
||||
|
||||
UnorderedSet set;
|
||||
bool contains = set.contains(0);
|
||||
BOOST_TEST(!contains);
|
||||
|
||||
BOOST_TEST_EQ(key::count_, 0);
|
||||
|
||||
set.insert(0);
|
||||
set.insert(0);
|
||||
set.insert(0);
|
||||
set.insert(1);
|
||||
|
||||
int const expected_key_count = key::count_;
|
||||
|
||||
contains = set.contains(0);
|
||||
BOOST_TEST(contains);
|
||||
|
||||
contains = set.contains(1);
|
||||
BOOST_TEST(contains);
|
||||
|
||||
contains = set.contains(2);
|
||||
BOOST_TEST(!contains);
|
||||
|
||||
BOOST_TEST_EQ(key::count_, expected_key_count);
|
||||
}
|
||||
|
||||
template <class UnorderedSet> void test_set_non_transparent_contains()
|
||||
{
|
||||
count_reset();
|
||||
|
||||
UnorderedSet set;
|
||||
bool contains = set.contains(0);
|
||||
BOOST_TEST(!contains);
|
||||
BOOST_TEST_EQ(key::count_, 1);
|
||||
|
||||
set.insert(0);
|
||||
set.insert(0);
|
||||
set.insert(0);
|
||||
set.insert(1);
|
||||
|
||||
int key_count = key::count_;
|
||||
|
||||
contains = set.contains(0);
|
||||
++key_count;
|
||||
BOOST_TEST(contains);
|
||||
|
||||
contains = set.contains(1);
|
||||
++key_count;
|
||||
BOOST_TEST(contains);
|
||||
|
||||
contains = set.contains(2);
|
||||
++key_count;
|
||||
BOOST_TEST(!contains);
|
||||
|
||||
BOOST_TEST_EQ(key::count_, key_count);
|
||||
}
|
||||
|
||||
void test_set()
|
||||
{
|
||||
typedef boost::unordered_set<key, transparent_hasher, transparent_key_equal>
|
||||
transparent_set;
|
||||
|
||||
typedef boost::unordered_set<key, transparent_hasher, key_equal>
|
||||
non_transparent_set1;
|
||||
typedef boost::unordered_set<key, hasher, transparent_key_equal>
|
||||
non_transparent_set2;
|
||||
typedef boost::unordered_set<key, hasher, key_equal> non_transparent_set3;
|
||||
|
||||
test_set_transparent_contains<transparent_set>();
|
||||
test_set_non_transparent_contains<non_transparent_set1>();
|
||||
test_set_non_transparent_contains<non_transparent_set2>();
|
||||
test_set_non_transparent_contains<non_transparent_set3>();
|
||||
}
|
||||
|
||||
void test_multiset()
|
||||
{
|
||||
typedef boost::unordered_multiset<key, transparent_hasher,
|
||||
transparent_key_equal>
|
||||
transparent_multiset;
|
||||
|
||||
typedef boost::unordered_multiset<key, transparent_hasher, key_equal>
|
||||
non_transparent_multiset1;
|
||||
typedef boost::unordered_multiset<key, hasher, transparent_key_equal>
|
||||
non_transparent_multiset2;
|
||||
typedef boost::unordered_multiset<key, hasher, key_equal>
|
||||
non_transparent_multiset3;
|
||||
|
||||
test_set_transparent_contains<transparent_multiset>();
|
||||
test_set_non_transparent_contains<non_transparent_multiset1>();
|
||||
test_set_non_transparent_contains<non_transparent_multiset2>();
|
||||
test_set_non_transparent_contains<non_transparent_multiset3>();
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (contains_) { // avoid -Wshadow warning with `bool contains`
|
||||
test_map();
|
||||
test_multimap();
|
||||
test_set();
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -30,8 +30,8 @@ template <typename T> struct test_allocator
|
||||
template <typename T2> test_allocator(test_allocator<T2> const&) {}
|
||||
T* allocate(std::size_t n) const { return (T*)malloc(sizeof(T) * n); }
|
||||
void deallocate(T* ptr, std::size_t) const { free(ptr); }
|
||||
bool operator==(test_allocator const&) { return true; }
|
||||
bool operator!=(test_allocator const&) { return false; }
|
||||
bool operator==(test_allocator const&) const { return true; }
|
||||
bool operator!=(test_allocator const&) const { return false; }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace emplace_tests {
|
||||
BOOST_TEST(!r2.second);
|
||||
BOOST_TEST(i1 == r2.first);
|
||||
// The container has to construct an object in order to check
|
||||
// whether it can emplace, so there's an extra cosntruction
|
||||
// whether it can emplace, so there's an extra construction
|
||||
// here.
|
||||
BOOST_TEST_EQ(check_.instances(), 6);
|
||||
BOOST_TEST_EQ(check_.constructions(), 7);
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright 2021 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 <string>
|
||||
|
||||
#if BOOST_CXX_VERSION >= 201103L
|
||||
#define UNORDERED_LVALUE_QUAL &
|
||||
#else
|
||||
#define UNORDERED_LVALUE_QUAL
|
||||
#endif
|
||||
|
||||
namespace test {
|
||||
struct is_even
|
||||
{
|
||||
is_even() {}
|
||||
|
||||
#if BOOST_CXX_VERSION >= 201703L
|
||||
// immovable for C++17
|
||||
is_even(is_even const&) = delete;
|
||||
is_even(is_even&&) = delete;
|
||||
|
||||
is_even& operator=(is_even const&) = delete;
|
||||
is_even& operator=(is_even&&) = delete;
|
||||
#elif BOOST_CXX_VERSION >= 201103L
|
||||
// move-only for C++11
|
||||
is_even(is_even const&) = delete;
|
||||
is_even(is_even&&) = default;
|
||||
|
||||
is_even& operator=(is_even const&) = delete;
|
||||
is_even& operator=(is_even&&) = default;
|
||||
#else
|
||||
// copyable otherwise
|
||||
is_even(is_even const&) {}
|
||||
is_even& operator=(is_even const&) { return *this; }
|
||||
#endif
|
||||
|
||||
bool operator()(
|
||||
std::pair<std::string const, int>& key_value) UNORDERED_LVALUE_QUAL
|
||||
{
|
||||
int const v = key_value.second;
|
||||
return (v % 2 == 0);
|
||||
}
|
||||
|
||||
bool operator()(int const& value) UNORDERED_LVALUE_QUAL
|
||||
{
|
||||
int const v = value;
|
||||
return (v % 2 == 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct is_too_large
|
||||
{
|
||||
is_too_large() {}
|
||||
|
||||
#if BOOST_CXX_VERSION >= 201703L
|
||||
// immovable for C++17
|
||||
is_too_large(is_too_large const&) = delete;
|
||||
is_too_large(is_too_large&&) = delete;
|
||||
|
||||
is_too_large& operator=(is_too_large const&) = delete;
|
||||
is_too_large& operator=(is_too_large&&) = delete;
|
||||
#elif BOOST_CXX_VERSION >= 201103L
|
||||
// move-only for C++11
|
||||
is_too_large(is_too_large const&) = delete;
|
||||
is_too_large(is_too_large&&) = default;
|
||||
|
||||
is_too_large& operator=(is_too_large const&) = delete;
|
||||
is_too_large& operator=(is_too_large&&) = default;
|
||||
#else
|
||||
// copyable otherwise
|
||||
is_too_large(is_too_large const&) {}
|
||||
|
||||
is_too_large& operator=(is_too_large const&) { return *this; }
|
||||
#endif
|
||||
|
||||
bool operator()(
|
||||
std::pair<std::string const, int>& key_value) UNORDERED_LVALUE_QUAL
|
||||
{
|
||||
int const v = key_value.second;
|
||||
return v >= 1000;
|
||||
}
|
||||
|
||||
bool operator()(int const& value) UNORDERED_LVALUE_QUAL
|
||||
{
|
||||
int const v = value;
|
||||
return v >= 1000;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
template <class UnorderedMap> void test_map_erase_if()
|
||||
{
|
||||
typedef UnorderedMap map_type;
|
||||
typedef typename map_type::size_type size_type;
|
||||
|
||||
map_type map;
|
||||
size_type num_erased = erase_if(map, test::is_even());
|
||||
BOOST_TEST(map.empty());
|
||||
BOOST_TEST_EQ(num_erased, 0u);
|
||||
|
||||
map.emplace("a", 1);
|
||||
map.emplace("b", 2);
|
||||
map.emplace("b", 4);
|
||||
map.emplace("b", 8);
|
||||
map.emplace("b", 16);
|
||||
map.emplace("c", 3);
|
||||
|
||||
size_type size = map.size();
|
||||
|
||||
num_erased = erase_if(map, test::is_too_large());
|
||||
|
||||
BOOST_TEST_EQ(map.size(), size);
|
||||
BOOST_TEST_EQ(num_erased, 0u);
|
||||
|
||||
num_erased = erase_if(map, test::is_even());
|
||||
BOOST_TEST_EQ(map.size(), 2u);
|
||||
BOOST_TEST_EQ(num_erased, size - map.size());
|
||||
}
|
||||
|
||||
template <class UnorderedSet> void test_set_erase_if()
|
||||
{
|
||||
typedef UnorderedSet set_type;
|
||||
typedef typename set_type::size_type size_type;
|
||||
|
||||
set_type set;
|
||||
size_type num_erased = erase_if(set, test::is_even());
|
||||
BOOST_TEST(set.empty());
|
||||
BOOST_TEST_EQ(num_erased, 0u);
|
||||
|
||||
set.emplace(1);
|
||||
set.emplace(2);
|
||||
set.emplace(2);
|
||||
set.emplace(2);
|
||||
set.emplace(2);
|
||||
set.emplace(3);
|
||||
|
||||
size_type size = set.size();
|
||||
|
||||
num_erased = erase_if(set, test::is_too_large());
|
||||
|
||||
BOOST_TEST_EQ(set.size(), size);
|
||||
BOOST_TEST_EQ(num_erased, 0u);
|
||||
|
||||
num_erased = erase_if(set, test::is_even());
|
||||
BOOST_TEST_EQ(set.size(), 2u);
|
||||
BOOST_TEST_EQ(num_erased, size - set.size());
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (unordered_erase_if) {
|
||||
test_map_erase_if<boost::unordered_map<std::string, int> >();
|
||||
test_map_erase_if<boost::unordered_multimap<std::string, int> >();
|
||||
|
||||
test_set_erase_if<boost::unordered_set<int> >();
|
||||
test_set_erase_if<boost::unordered_multiset<int> >();
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -48,7 +48,7 @@ template <typename T> void test_simple_allocator()
|
||||
BOOST_STATIC_ASSERT(
|
||||
(boost::is_same<typename traits::difference_type, std::ptrdiff_t>::value));
|
||||
|
||||
#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS == 1
|
||||
#if !defined(BOOST_NO_CXX11_ALLOCATOR)
|
||||
BOOST_STATIC_ASSERT((boost::is_same<typename traits::size_type,
|
||||
std::make_unsigned<std::ptrdiff_t>::type>::value));
|
||||
#else
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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();
|
||||
}
|
||||
@@ -245,6 +245,49 @@ UNORDERED_AUTO_TEST (node_handle_tests) {
|
||||
node_handle_tests_impl(x2);
|
||||
}
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
typename boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>::iterator
|
||||
insert_empty_node(boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>& c)
|
||||
{
|
||||
typedef
|
||||
typename boost::unordered_map<Key, T, Hash, KeyEqual, Allocator>::node_type
|
||||
node_type;
|
||||
|
||||
return c.insert(node_type()).position;
|
||||
}
|
||||
|
||||
template <class T, class Hash, class KeyEqual, class Allocator>
|
||||
typename boost::unordered_set<T, Hash, KeyEqual, Allocator>::iterator
|
||||
insert_empty_node(boost::unordered_set<T, Hash, KeyEqual, Allocator>& c)
|
||||
{
|
||||
typedef typename boost::unordered_set<T, Hash, KeyEqual, Allocator>::node_type
|
||||
node_type;
|
||||
|
||||
return c.insert(node_type()).position;
|
||||
}
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
typename boost::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>::iterator
|
||||
insert_empty_node(
|
||||
boost::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& c)
|
||||
{
|
||||
typedef typename boost::unordered_multimap<Key, T, Hash, KeyEqual,
|
||||
Allocator>::node_type node_type;
|
||||
|
||||
return c.insert(node_type());
|
||||
}
|
||||
|
||||
template <class T, class Hash, class KeyEqual, class Allocator>
|
||||
typename boost::unordered_multiset<T, Hash, KeyEqual, Allocator>::iterator
|
||||
insert_empty_node(boost::unordered_multiset<T, Hash, KeyEqual, Allocator>& c)
|
||||
{
|
||||
typedef
|
||||
typename boost::unordered_multiset<T, Hash, KeyEqual, Allocator>::node_type
|
||||
node_type;
|
||||
|
||||
return c.insert(node_type());
|
||||
}
|
||||
|
||||
template <typename Container1, typename Container2>
|
||||
void insert_node_handle_unique(Container1& c1, Container2& c2)
|
||||
{
|
||||
@@ -253,14 +296,12 @@ void insert_node_handle_unique(Container1& c1, Container2& c2)
|
||||
BOOST_STATIC_ASSERT(
|
||||
(boost::is_same<node_type, typename Container2::node_type>::value));
|
||||
|
||||
typedef typename Container1::insert_return_type insert_return_type1;
|
||||
typedef typename Container1::iterator iterator1;
|
||||
typedef typename Container2::insert_return_type insert_return_type2;
|
||||
|
||||
insert_return_type1 r1 = c1.insert(node_type());
|
||||
iterator1 r1 = insert_empty_node(c1);
|
||||
insert_return_type2 r2 = c2.insert(node_type());
|
||||
BOOST_TEST(!r1.inserted);
|
||||
BOOST_TEST(!r1.node);
|
||||
BOOST_TEST(r1.position == c1.end());
|
||||
BOOST_TEST(r1 == c1.end());
|
||||
BOOST_TEST(!r2.inserted);
|
||||
BOOST_TEST(!r2.node);
|
||||
BOOST_TEST(r2.position == c2.end());
|
||||
@@ -332,7 +373,7 @@ void insert_node_handle_equiv(Container1& c1, Container2& c2)
|
||||
typedef typename Container1::iterator iterator1;
|
||||
typedef typename Container2::iterator iterator2;
|
||||
|
||||
iterator1 r1 = c1.insert(node_type());
|
||||
iterator1 r1 = insert_empty_node(c1);
|
||||
iterator2 r2 = c2.insert(node_type());
|
||||
BOOST_TEST(r1 == c1.end());
|
||||
BOOST_TEST(r2 == c2.end());
|
||||
@@ -368,6 +409,17 @@ UNORDERED_AUTO_TEST (insert_node_handle_unique_tests) {
|
||||
BOOST_TEST(x2.size() == 3);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_multiset<int> x1;
|
||||
boost::unordered_set<int> x2;
|
||||
x1.emplace(100);
|
||||
x1.emplace(140);
|
||||
x1.emplace(-55);
|
||||
x2.emplace(140);
|
||||
insert_node_handle_unique(x1, x2);
|
||||
BOOST_TEST(x2.size() == 3);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_map<int, int, hash_thing> x1;
|
||||
boost::unordered_map<int, int> x2;
|
||||
@@ -379,6 +431,18 @@ UNORDERED_AUTO_TEST (insert_node_handle_unique_tests) {
|
||||
insert_node_handle_unique(x1, x2);
|
||||
BOOST_TEST(x2.size() == 4);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_multimap<int, int, hash_thing> x1;
|
||||
boost::unordered_map<int, int> x2;
|
||||
x1.emplace(67, 50);
|
||||
x1.emplace(23, 45);
|
||||
x1.emplace(18, 19);
|
||||
x2.emplace(23, 50);
|
||||
x2.emplace(12, 49);
|
||||
insert_node_handle_unique(x1, x2);
|
||||
BOOST_TEST(x2.size() == 4);
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (insert_node_handle_equiv_tests) {
|
||||
@@ -394,6 +458,45 @@ UNORDERED_AUTO_TEST (insert_node_handle_equiv_tests) {
|
||||
insert_node_handle_equiv(x1, x2);
|
||||
BOOST_TEST(x2.size() == 6);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_map<int, int, hash_thing> x1;
|
||||
boost::unordered_multimap<int, int> x2;
|
||||
x1.emplace(67, 50);
|
||||
x1.emplace(67, 100);
|
||||
x1.emplace(23, 45);
|
||||
x1.emplace(18, 19);
|
||||
x2.emplace(23, 50);
|
||||
x2.emplace(12, 49);
|
||||
insert_node_handle_equiv(x1, x2);
|
||||
BOOST_TEST(x2.size() == 5);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_multiset<int, hash_thing> x1;
|
||||
boost::unordered_multiset<int> x2;
|
||||
x1.emplace(67);
|
||||
x1.emplace(67);
|
||||
x1.emplace(23);
|
||||
x1.emplace(18);
|
||||
x2.emplace(23);
|
||||
x2.emplace(12);
|
||||
insert_node_handle_equiv(x1, x2);
|
||||
BOOST_TEST(x2.size() == 6);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_set<int, hash_thing> x1;
|
||||
boost::unordered_multiset<int> x2;
|
||||
x1.emplace(67);
|
||||
x1.emplace(67);
|
||||
x1.emplace(23);
|
||||
x1.emplace(18);
|
||||
x2.emplace(23);
|
||||
x2.emplace(12);
|
||||
insert_node_handle_equiv(x1, x2);
|
||||
BOOST_TEST(x2.size() == 5);
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (insert_node_handle_unique_tests2) {
|
||||
@@ -408,6 +511,17 @@ UNORDERED_AUTO_TEST (insert_node_handle_unique_tests2) {
|
||||
BOOST_TEST(x2.size() == 3);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_multiset<int> x1;
|
||||
boost::unordered_set<int> x2;
|
||||
x1.emplace(100);
|
||||
x1.emplace(140);
|
||||
x1.emplace(-55);
|
||||
x2.emplace(140);
|
||||
insert_node_handle_unique2(x1, x2);
|
||||
BOOST_TEST(x2.size() == 3);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_map<int, int, hash_thing> x1;
|
||||
boost::unordered_map<int, int> x2;
|
||||
@@ -419,6 +533,18 @@ UNORDERED_AUTO_TEST (insert_node_handle_unique_tests2) {
|
||||
insert_node_handle_unique2(x1, x2);
|
||||
BOOST_TEST(x2.size() == 4);
|
||||
}
|
||||
|
||||
{
|
||||
boost::unordered_multimap<int, int, hash_thing> x1;
|
||||
boost::unordered_map<int, int> x2;
|
||||
x1.emplace(67, 50);
|
||||
x1.emplace(23, 45);
|
||||
x1.emplace(18, 19);
|
||||
x2.emplace(23, 50);
|
||||
x2.emplace(12, 49);
|
||||
insert_node_handle_unique2(x1, x2);
|
||||
BOOST_TEST(x2.size() == 4);
|
||||
}
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright 2021 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 <boost/config.hpp>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
|
||||
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
|
||||
{
|
||||
typedef T value_type;
|
||||
|
||||
A() {}
|
||||
|
||||
template <class U> A(const A<U>&) BOOST_NOEXCEPT {}
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
total_allocation += n * sizeof(T);
|
||||
++num_allocations;
|
||||
return (T*)std::calloc(n, sizeof(T));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t n) BOOST_NOEXCEPT
|
||||
{
|
||||
total_allocation -= n * sizeof(T);
|
||||
std::free(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <class UnorderedContainer> void bucket_count_constructor()
|
||||
{
|
||||
BOOST_TEST_EQ(num_allocations, 0u);
|
||||
BOOST_TEST_EQ(total_allocation, 0u);
|
||||
|
||||
{
|
||||
std::size_t count = 50000;
|
||||
|
||||
UnorderedContainer s(count);
|
||||
|
||||
BOOST_TEST_GE(total_allocation, count * sizeof(void*));
|
||||
BOOST_TEST_GE(s.bucket_count(), count);
|
||||
}
|
||||
|
||||
BOOST_TEST_GT(num_allocations, 0u);
|
||||
BOOST_TEST_EQ(total_allocation, 0u);
|
||||
num_allocations = 0;
|
||||
}
|
||||
|
||||
template <class UnorderedContainer> void range_bucket_constructor()
|
||||
{
|
||||
BOOST_TEST_EQ(num_allocations, 0u);
|
||||
BOOST_TEST_EQ(total_allocation, 0u);
|
||||
|
||||
{
|
||||
UnorderedContainer s1;
|
||||
|
||||
std::size_t count = 50000;
|
||||
|
||||
UnorderedContainer s2(s1.begin(), s1.end(), count);
|
||||
|
||||
BOOST_TEST_GE(total_allocation, count * sizeof(void*));
|
||||
BOOST_TEST_GE(s2.bucket_count(), count);
|
||||
}
|
||||
|
||||
BOOST_TEST_GT(num_allocations, 0u);
|
||||
BOOST_TEST_EQ(total_allocation, 0u);
|
||||
num_allocations = 0;
|
||||
}
|
||||
|
||||
template <class UnorderedContainer> void reserve_tests()
|
||||
{
|
||||
BOOST_TEST_EQ(num_allocations, 0u);
|
||||
BOOST_TEST_EQ(total_allocation, 0u);
|
||||
|
||||
{
|
||||
UnorderedContainer s;
|
||||
|
||||
// simple math for the test:
|
||||
// max_load_factor = max_size / bucket_count, before a rehashing occurs
|
||||
//
|
||||
// reserve() respects max load factor and its argument implies the max size
|
||||
//
|
||||
// reserve(count) => bucket_count = ceil(count / max_load_factor)
|
||||
// internal policies reshape bucket_count accordingly but guarantee count as
|
||||
// a minimum
|
||||
//
|
||||
|
||||
std::size_t count = 50000;
|
||||
|
||||
s.max_load_factor(0.37f);
|
||||
s.reserve(count);
|
||||
|
||||
std::size_t expected_bucket_count =
|
||||
static_cast<std::size_t>(std::ceil(static_cast<float>(count) / 0.37f));
|
||||
|
||||
BOOST_TEST_GE(total_allocation, expected_bucket_count * sizeof(void*));
|
||||
BOOST_TEST_GE(s.bucket_count(), expected_bucket_count);
|
||||
|
||||
std::size_t prev_allocations = num_allocations;
|
||||
s.reserve(count);
|
||||
BOOST_TEST_EQ(num_allocations, prev_allocations);
|
||||
}
|
||||
|
||||
BOOST_TEST_GT(num_allocations, 0u);
|
||||
BOOST_TEST_EQ(total_allocation, 0u);
|
||||
num_allocations = 0;
|
||||
}
|
||||
|
||||
template <class UnorderedContainer> void rehash_tests()
|
||||
{
|
||||
BOOST_TEST_EQ(num_allocations, 0u);
|
||||
BOOST_TEST_EQ(total_allocation, 0u);
|
||||
|
||||
{
|
||||
UnorderedContainer s;
|
||||
|
||||
std::size_t count = 1000;
|
||||
s.rehash(count);
|
||||
|
||||
// test that an initial allocation occurs
|
||||
//
|
||||
BOOST_TEST_GE(total_allocation, count * sizeof(void*));
|
||||
BOOST_TEST_GE(s.bucket_count(), count);
|
||||
|
||||
// prove idempotence, that rehashing with the exact same bucket count causes
|
||||
// no reallocations
|
||||
//
|
||||
std::size_t prev_allocations = num_allocations;
|
||||
|
||||
s.rehash(count);
|
||||
BOOST_TEST_EQ(num_allocations, prev_allocations);
|
||||
|
||||
// prove that when we rehash, exceeding the current bucket count, that we
|
||||
// properly deallocate the current bucket array and then reallocate the
|
||||
// larger one
|
||||
//
|
||||
std::size_t prev_count = s.bucket_count();
|
||||
|
||||
count = s.bucket_count() + 2;
|
||||
s.rehash(count);
|
||||
|
||||
BOOST_TEST_GT(num_allocations, prev_allocations);
|
||||
BOOST_TEST_GE(total_allocation, count * sizeof(void*));
|
||||
BOOST_TEST_GE(s.bucket_count(), count);
|
||||
|
||||
// concurrent memory usage here should be less than the sum of the memory
|
||||
// required for the previous bucket array and our current one
|
||||
// note, the test is vulnerable to cases where the next calculated bucket
|
||||
// count can exceed `prev_count + count`
|
||||
//
|
||||
BOOST_TEST_LT(s.bucket_count(), prev_count + count);
|
||||
BOOST_TEST_LT(total_allocation, (prev_count + count) * sizeof(void*));
|
||||
}
|
||||
|
||||
BOOST_TEST_GT(num_allocations, 0u);
|
||||
BOOST_TEST_EQ(total_allocation, 0u);
|
||||
num_allocations = 0;
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (unordered_set_reserve) {
|
||||
typedef boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
A<int> >
|
||||
unordered_set;
|
||||
|
||||
typedef boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
|
||||
A<int> >
|
||||
unordered_multiset;
|
||||
|
||||
typedef boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
A<std::pair<int const, int> > >
|
||||
unordered_map;
|
||||
|
||||
typedef boost::unordered_multimap<int, int, boost::hash<int>,
|
||||
std::equal_to<int>, A<std::pair<int const, int> > >
|
||||
unordered_multimap;
|
||||
|
||||
bucket_count_constructor<unordered_set>();
|
||||
bucket_count_constructor<unordered_map>();
|
||||
bucket_count_constructor<unordered_multiset>();
|
||||
bucket_count_constructor<unordered_multimap>();
|
||||
|
||||
range_bucket_constructor<unordered_set>();
|
||||
range_bucket_constructor<unordered_map>();
|
||||
range_bucket_constructor<unordered_multiset>();
|
||||
range_bucket_constructor<unordered_multimap>();
|
||||
|
||||
reserve_tests<unordered_set>();
|
||||
reserve_tests<unordered_map>();
|
||||
reserve_tests<unordered_multiset>();
|
||||
reserve_tests<unordered_multimap>();
|
||||
|
||||
rehash_tests<unordered_set>();
|
||||
rehash_tests<unordered_map>();
|
||||
rehash_tests<unordered_multiset>();
|
||||
rehash_tests<unordered_multimap>();
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -0,0 +1,91 @@
|
||||
|
||||
// Copyright 2021 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>
|
||||
|
||||
#if BOOST_CXX_VERSION <= 199711L
|
||||
|
||||
BOOST_PRAGMA_MESSAGE(
|
||||
"scoped allocator adaptor tests only work under C++11 and above")
|
||||
int main() {}
|
||||
|
||||
#else
|
||||
|
||||
// This test is based on a user-submitted issue found here:
|
||||
// https://github.com/boostorg/unordered/issues/22
|
||||
//
|
||||
|
||||
// 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 <boost/cstdint.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <scoped_allocator>
|
||||
|
||||
namespace test {
|
||||
template <class T> struct allocator
|
||||
{
|
||||
typedef T value_type;
|
||||
|
||||
allocator() = delete;
|
||||
allocator(int) {}
|
||||
allocator(allocator const&) = default;
|
||||
allocator(allocator&&) = default;
|
||||
|
||||
template <class U> allocator(allocator<U> const&) {}
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) noexcept { ::operator delete(p); }
|
||||
|
||||
bool operator==(allocator const&) const { return true; }
|
||||
bool operator!=(allocator const&) const { return false; }
|
||||
};
|
||||
} // namespace test
|
||||
|
||||
typedef std::vector<boost::uint64_t, test::allocator<boost::uint64_t> >
|
||||
vector_type;
|
||||
|
||||
typedef std::pair<boost::uint64_t, vector_type> pair_type;
|
||||
|
||||
typedef std::scoped_allocator_adaptor<test::allocator<pair_type>,
|
||||
test::allocator<boost::uint64_t> >
|
||||
allocator_type;
|
||||
|
||||
typedef boost::unordered_map<const boost::uint64_t, vector_type,
|
||||
boost::hash<boost::uint64_t>, std::equal_to<boost::uint64_t>, allocator_type>
|
||||
map_type;
|
||||
|
||||
UNORDERED_AUTO_TEST (scoped_allocator) {
|
||||
allocator_type alloc(
|
||||
test::allocator<pair_type>(1337), test::allocator<boost::uint64_t>(7331));
|
||||
|
||||
map_type map(alloc);
|
||||
|
||||
for (unsigned i = 0; i < 10; ++i) {
|
||||
boost::ignore_unused(map[i]);
|
||||
}
|
||||
|
||||
BOOST_TEST(map.size() == 10);
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -552,8 +552,8 @@ namespace unnecessary_copy_tests {
|
||||
|
||||
std::pair<count_copies const, count_copies> move_source_trial;
|
||||
reset();
|
||||
std::make_tuple(std::move(move_source_trial.first));
|
||||
std::make_tuple(std::move(move_source_trial.second));
|
||||
(void)std::make_tuple(std::move(move_source_trial.first));
|
||||
(void)std::make_tuple(std::move(move_source_trial.second));
|
||||
int tuple_move_cost = ::unnecessary_copy_tests::count_copies::moves;
|
||||
int tuple_copy_cost = ::unnecessary_copy_tests::count_copies::copies;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user