mirror of
https://github.com/boostorg/container_hash.git
synced 2026-03-11 13:31:15 +01:00
Compare commits
166 Commits
feature/as
...
boost-1.90
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
060d4aea6b | ||
|
|
186c38e255 | ||
|
|
c8dab7f85a | ||
|
|
5d8b8ac2b9 | ||
|
|
1e1cf6c46e | ||
|
|
88aacb6d46 | ||
|
|
0a000167b7 | ||
|
|
d8f1075080 | ||
|
|
b8179488b2 | ||
|
|
edd436231a | ||
|
|
d56fc37fe5 | ||
|
|
fa6601f974 | ||
|
|
89e5b98f6b | ||
|
|
1193ed067c | ||
|
|
f85cb77bd3 | ||
|
|
7d68e5f1d2 | ||
|
|
3c9ce69f92 | ||
|
|
aa2e8b3674 | ||
|
|
c3cb5896fc | ||
|
|
58cd5e35b1 | ||
|
|
386d007088 | ||
|
|
b8090f9b87 | ||
|
|
a5c2949670 | ||
|
|
b16f42ef10 | ||
|
|
de85b0c248 | ||
|
|
583e14413c | ||
|
|
ef914e1b34 | ||
|
|
ee5285bfa6 | ||
|
|
0a9803dffe | ||
|
|
881448f75e | ||
|
|
c5b1f4894c | ||
|
|
6d214eb776 | ||
|
|
b0d3107b99 | ||
|
|
e2446b187e | ||
|
|
1dd2f73866 | ||
|
|
1278f1c2b5 | ||
|
|
067f636d6c | ||
|
|
338c053889 | ||
|
|
94d4f7cb88 | ||
|
|
6957a20ab1 | ||
|
|
28cc18a4bc | ||
|
|
7288df8bee | ||
|
|
5c2d5e5b36 | ||
|
|
db8743e1c3 | ||
|
|
48a306dcf2 | ||
|
|
f6279a0615 | ||
|
|
41746c0bdb | ||
|
|
07a0910640 | ||
|
|
4c32071739 | ||
|
|
30c05c93b1 | ||
|
|
197f75845c | ||
|
|
bf8ad2c0ee | ||
|
|
fef60938e5 | ||
|
|
f04d866fa7 | ||
|
|
17201433f8 | ||
|
|
9d09674f08 | ||
|
|
f8ecd66820 | ||
|
|
565bac8d31 | ||
|
|
b8496de2f2 | ||
|
|
641a59342e | ||
|
|
226eb066e9 | ||
|
|
d9cb23d96a | ||
|
|
bd8a5cb867 | ||
|
|
b43ce472fa | ||
|
|
e798972130 | ||
|
|
ebb49fa459 | ||
|
|
1e1c0ea38c | ||
|
|
e62a268566 | ||
|
|
ad89c02360 | ||
|
|
64948e5f57 | ||
|
|
1958e96561 | ||
|
|
4a7287d371 | ||
|
|
5638134081 | ||
|
|
7c49f0bfb1 | ||
|
|
9ae5790657 | ||
|
|
8ce81c361d | ||
|
|
4315faf470 | ||
|
|
eb049e0cae | ||
|
|
6373656710 | ||
|
|
ec1503bbc8 | ||
|
|
ee064dc7f8 | ||
|
|
b505e06b3c | ||
|
|
6075f3e1f5 | ||
|
|
0bfeabfd63 | ||
|
|
758596533d | ||
|
|
640bd48f51 | ||
|
|
b25fd745ca | ||
|
|
6fe3469a8b | ||
|
|
c07630ac60 | ||
|
|
45270ae11e | ||
|
|
5bef4901b9 | ||
|
|
d724bcd0ef | ||
|
|
ceb8303601 | ||
|
|
cf87e304f6 | ||
|
|
4d9f7b8931 | ||
|
|
2f4efbced4 | ||
|
|
bf7a78594e | ||
|
|
c0c70e5b3e | ||
|
|
891a64d45d | ||
|
|
08d69c31b1 | ||
|
|
6526b24900 | ||
|
|
1a8dca4f2c | ||
|
|
8761157a19 | ||
|
|
f2b2ef4ebe | ||
|
|
30560ada18 | ||
|
|
c61a4f691e | ||
|
|
fe28cdbd1f | ||
|
|
f98b943585 | ||
|
|
4aeb7b2285 | ||
|
|
e310932a9c | ||
|
|
558d0ead17 | ||
|
|
d6905ab159 | ||
|
|
251894540d | ||
|
|
ffadafa0c1 | ||
|
|
ce166d1030 | ||
|
|
8ab83c1cd4 | ||
|
|
16546190f6 | ||
|
|
5e26a3b807 | ||
|
|
bd5b7a359c | ||
|
|
f5f5476dcc | ||
|
|
228f7db5d3 | ||
|
|
f50b914456 | ||
|
|
c134ca39e9 | ||
|
|
f51f68fe93 | ||
|
|
0865c1d230 | ||
|
|
0d2266decb | ||
|
|
3c9ba89cf2 | ||
|
|
20ee3b60fd | ||
|
|
bdd3dcf944 | ||
|
|
018fc4e1fa | ||
|
|
7568176bfa | ||
|
|
08864cd1e9 | ||
|
|
5a616b9b54 | ||
|
|
16fb4cc749 | ||
|
|
0171246a61 | ||
|
|
29c85559e4 | ||
|
|
412bd51449 | ||
|
|
87196a503e | ||
|
|
df1671d593 | ||
|
|
1996cf36c1 | ||
|
|
a4b2048a29 | ||
|
|
85f9f8a97a | ||
|
|
e92eae9eb2 | ||
|
|
8a1335458a | ||
|
|
f7e537d1a1 | ||
|
|
607b73f1e0 | ||
|
|
789261c68c | ||
|
|
29ee19ee7f | ||
|
|
8bb7d43646 | ||
|
|
a426a1939f | ||
|
|
034b81594d | ||
|
|
75ef5a14f5 | ||
|
|
b5bb4405a9 | ||
|
|
d43ae22ab4 | ||
|
|
e061b3c4c0 | ||
|
|
9035aa5485 | ||
|
|
3ae0aea360 | ||
|
|
fc249670c0 | ||
|
|
30ffbf9f16 | ||
|
|
c36319c878 | ||
|
|
a2aaefc71a | ||
|
|
e3cd2d7de8 | ||
|
|
4571ec190a | ||
|
|
478730107d | ||
|
|
adcf81c732 | ||
|
|
75a37c2616 |
@@ -16,10 +16,7 @@ branches:
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
TOOLSET: msvc-9.0,msvc-10.0,msvc-11.0
|
||||
ADDRMD: 32
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
TOOLSET: msvc-12.0,msvc-14.0
|
||||
TOOLSET: msvc-14.0
|
||||
ADDRMD: 32,64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
TOOLSET: msvc-14.1
|
||||
@@ -28,7 +25,7 @@ environment:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
TOOLSET: clang-win
|
||||
ADDRMD: 64
|
||||
CXXSTD: 14,17,latest
|
||||
CXXSTD: 14,17
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
TOOLSET: clang-win
|
||||
CXXSTD: 14,17,latest
|
||||
@@ -36,19 +33,19 @@ environment:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
ADDPATH: C:\cygwin\bin;
|
||||
TOOLSET: gcc
|
||||
CXXSTD: 03,11,14,1z
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CXXSTD: 11,14,1z
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
TOOLSET: gcc
|
||||
CXXSTD: 03,11,14,1z
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
ADDPATH: C:\mingw\bin;
|
||||
CXXSTD: 11,14,17
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
|
||||
TOOLSET: gcc
|
||||
CXXSTD: 03,11,14,1z
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
ADDPATH: C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev1\mingw64\bin;
|
||||
CXXSTD: 11,14,17
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
|
||||
TOOLSET: gcc
|
||||
CXXSTD: 03,11,14,1z
|
||||
CXXSTD: 11,14,17
|
||||
|
||||
install:
|
||||
- set BOOST_BRANCH=develop
|
||||
|
||||
423
.drone.jsonnet
Normal file
423
.drone.jsonnet
Normal file
@@ -0,0 +1,423 @@
|
||||
# Copyright 2022 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
local library = "container_hash";
|
||||
|
||||
local triggers =
|
||||
{
|
||||
branch: [ "master", "develop", "feature/*" ]
|
||||
};
|
||||
|
||||
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
|
||||
local asan = { ASAN: '1' };
|
||||
|
||||
local linux_pipeline(name, image, environment, packages = "", sources = [], arch = "amd64") =
|
||||
{
|
||||
name: name,
|
||||
kind: "pipeline",
|
||||
type: "docker",
|
||||
trigger: triggers,
|
||||
platform:
|
||||
{
|
||||
os: "linux",
|
||||
arch: arch
|
||||
},
|
||||
steps:
|
||||
[
|
||||
{
|
||||
name: "everything",
|
||||
image: image,
|
||||
environment: environment,
|
||||
commands:
|
||||
[
|
||||
'set -e',
|
||||
'uname -a',
|
||||
'echo $DRONE_STAGE_MACHINE',
|
||||
'wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -',
|
||||
] +
|
||||
(if sources != [] then [ 'apt-get update' ] + [ ('apt-add-repository "' + source + '"') for source in sources ] else []) +
|
||||
(if packages != "" then [ 'apt-get update', 'apt-get -y install ' + packages ] else []) +
|
||||
[
|
||||
'export LIBRARY=' + library,
|
||||
'./.drone/drone.sh',
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
local macos_pipeline(name, environment, xcode_version = "12.2", osx_version = "catalina", arch = "amd64") =
|
||||
{
|
||||
name: name,
|
||||
kind: "pipeline",
|
||||
type: "exec",
|
||||
trigger: triggers,
|
||||
platform: {
|
||||
"os": "darwin",
|
||||
"arch": arch
|
||||
},
|
||||
node: {
|
||||
"os": osx_version
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: "everything",
|
||||
environment: environment + { "DEVELOPER_DIR": "/Applications/Xcode-" + xcode_version + ".app/Contents/Developer" },
|
||||
commands:
|
||||
[
|
||||
'export LIBRARY=' + library,
|
||||
'./.drone/drone.sh',
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
{
|
||||
name: name,
|
||||
kind: "pipeline",
|
||||
type: "docker",
|
||||
trigger: triggers,
|
||||
platform:
|
||||
{
|
||||
os: "windows",
|
||||
arch: arch
|
||||
},
|
||||
"steps":
|
||||
[
|
||||
{
|
||||
name: "everything",
|
||||
image: image,
|
||||
environment: environment,
|
||||
commands:
|
||||
[
|
||||
'cmd /C .drone\\\\drone.bat ' + library,
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
[
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.7 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.7', CXXSTD: '0x', ADDRMD: '32,64' },
|
||||
"g++-4.7-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.8* 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.9 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '11', ADDRMD: '32,64' },
|
||||
"g++-4.9-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 16.04 GCC 5* 32/64",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 18.04 GCC 6 32/64",
|
||||
"cppalliance/droneubuntu1804:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-6', CXXSTD: '11,14', ADDRMD: '32,64' },
|
||||
"g++-6-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 18.04 GCC 7* 32/64",
|
||||
"cppalliance/droneubuntu1804:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14,17', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 18.04 GCC 8 32/64",
|
||||
"cppalliance/droneubuntu1804:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-8', CXXSTD: '11,14,17', ADDRMD: '32,64' },
|
||||
"g++-8-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* 32/64",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14,17,2a', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* ARM64",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14,17,2a' },
|
||||
arch="arm64",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9* S390x",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14,17,2a' },
|
||||
arch="s390x",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 10 32/64",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-10', CXXSTD: '11,14,17,20', ADDRMD: '32,64' },
|
||||
"g++-10-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 GCC 11* 32/64",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14,17,2a', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 GCC 12 32/64",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '11,14,17,20,2b', ADDRMD: '32,64' },
|
||||
"g++-12-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 GCC 13 32/64 UBSAN",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '11,14,17,20,2b', ADDRMD: '32,64' } + ubsan,
|
||||
"g++-13-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 GCC 13 32 ASAN",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '11,14,17,20,2b', ADDRMD: '32' } + asan,
|
||||
"g++-13-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 GCC 14 UBSAN",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-14', CXXSTD: '11,14,17,20,2b' } + ubsan,
|
||||
"g++-14-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 GCC 14 ASAN",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-14', CXXSTD: '11,14,17,20,2b' } + asan,
|
||||
"g++-14-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 16.04 Clang 3.5",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-3.5', CXXSTD: '11' },
|
||||
"clang-3.5",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 16.04 Clang 3.6",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-3.6', CXXSTD: '11,14' },
|
||||
"clang-3.6",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 16.04 Clang 3.7",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-3.7', CXXSTD: '11,14' },
|
||||
"clang-3.7",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 16.04 Clang 3.8",
|
||||
"cppalliance/droneubuntu1604:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-3.8', CXXSTD: '11,14' },
|
||||
"clang-3.8",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 18.04 Clang 3.9",
|
||||
"cppalliance/droneubuntu1804:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-3.9', CXXSTD: '11,14' },
|
||||
"clang-3.9",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 18.04 Clang 4.0",
|
||||
"cppalliance/droneubuntu1804:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-4.0', CXXSTD: '11,14' },
|
||||
"clang-4.0",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 18.04 Clang 5.0",
|
||||
"cppalliance/droneubuntu1804:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-5.0', CXXSTD: '11,14,1z' },
|
||||
"clang-5.0",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 18.04 Clang 6.0",
|
||||
"cppalliance/droneubuntu1804:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-6.0', CXXSTD: '11,14,17' },
|
||||
"clang-6.0",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 Clang 7",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-7', CXXSTD: '11,14,17' },
|
||||
"clang-7",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 Clang 8",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-8', CXXSTD: '11,14,17' },
|
||||
"clang-8",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 Clang 9",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-9', CXXSTD: '11,14,17,2a' },
|
||||
"clang-9",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 Clang 10",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-10', CXXSTD: '11,14,17,2a' },
|
||||
"clang-10",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 Clang 11",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-11', CXXSTD: '11,14,17,2a' },
|
||||
"clang-11",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 Clang 12",
|
||||
"cppalliance/droneubuntu2004:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-12', CXXSTD: '11,14,17,2a' },
|
||||
"clang-12",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 13",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-13', CXXSTD: '11,14,17,20,2b' },
|
||||
"clang-13",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 14",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '11,14,17,20,2b' },
|
||||
"clang-14",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 15",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-15', CXXSTD: '11,14,17,20,2b' },
|
||||
"clang-15",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 Clang 16",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-16', CXXSTD: '11,14,17,20,2b' },
|
||||
"clang-16",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 Clang 17 UBSAN",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-17', CXXSTD: '11,14,17,20,2b' } + ubsan,
|
||||
"clang-17",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 Clang 17 ASAN",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-17', CXXSTD: '11,14,17,20,2b' } + asan,
|
||||
"clang-17",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 Clang 18 UBSAN",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-18', CXXSTD: '11,14,17,20,2b' } + ubsan,
|
||||
"clang-18",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.04 Clang 18 ASAN",
|
||||
"cppalliance/droneubuntu2404:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-18', CXXSTD: '11,14,17,20,2b' } + asan,
|
||||
"clang-18",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 24.10 Clang 19",
|
||||
"cppalliance/droneubuntu2410:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-19', CXXSTD: '11,14,17,20,2b' },
|
||||
"clang-19",
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 10.15 Xcode 12.2 UBSAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11,14,17,2a' } + ubsan,
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 10.15 Xcode 12.2 ASAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11,14,17,2a' } + asan,
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 12.4 Xcode 13.4.1 UBSAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11,14,17,20,2b' } + ubsan,
|
||||
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 12.4 Xcode 13.4.1 ASAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11,14,17,20,2b' } + asan,
|
||||
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2015 msvc-14.0",
|
||||
"cppalliance/dronevs2015",
|
||||
{ TOOLSET: 'msvc-14.0', CXXSTD: '14,latest', B2_DONT_EMBED_MANIFEST: '1' },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2017 msvc-14.1",
|
||||
"cppalliance/dronevs2017",
|
||||
{ TOOLSET: 'msvc-14.1', CXXSTD: '14,17,latest' },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2019 msvc-14.2",
|
||||
"cppalliance/dronevs2019",
|
||||
{ TOOLSET: 'msvc-14.2', CXXSTD: '14,17,20,latest' },
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2022 msvc-14.3",
|
||||
"cppalliance/dronevs2022:1",
|
||||
{ TOOLSET: 'msvc-14.3', CXXSTD: '14,17,20,latest' },
|
||||
),
|
||||
]
|
||||
24
.drone/drone.bat
Normal file
24
.drone/drone.bat
Normal file
@@ -0,0 +1,24 @@
|
||||
@REM Copyright 2022 Peter Dimov
|
||||
@REM Distributed under the Boost Software License, Version 1.0.
|
||||
@REM https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@ECHO ON
|
||||
|
||||
set LIBRARY=%1
|
||||
set DRONE_BUILD_DIR=%CD%
|
||||
|
||||
set BOOST_BRANCH=develop
|
||||
if "%DRONE_BRANCH%" == "master" set BOOST_BRANCH=master
|
||||
cd ..
|
||||
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
git submodule update --init tools/boostdep
|
||||
xcopy /s /e /q %DRONE_BUILD_DIR% libs\%LIBRARY%\
|
||||
python tools/boostdep/depinst/depinst.py -I examples %LIBRARY%
|
||||
cmd /c bootstrap
|
||||
b2 -d0 headers
|
||||
|
||||
if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
|
||||
if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
|
||||
b2 -j3 --verbose-test libs/%LIBRARY%/test//hash_info toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
b2 -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
|
||||
26
.drone/drone.sh
Executable file
26
.drone/drone.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2022 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
set -ex
|
||||
export PATH=~/.local/bin:/usr/local/bin:$PATH
|
||||
|
||||
DRONE_BUILD_DIR=$(pwd)
|
||||
|
||||
BOOST_BRANCH=develop
|
||||
if [ "$DRONE_BRANCH" = "master" ]; then BOOST_BRANCH=master; fi
|
||||
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
git submodule update --init tools/boostdep
|
||||
cp -r $DRONE_BUILD_DIR/* libs/$LIBRARY
|
||||
python tools/boostdep/depinst/depinst.py -I examples $LIBRARY
|
||||
./bootstrap.sh
|
||||
./b2 -d0 headers
|
||||
|
||||
echo "using $TOOLSET : : $COMPILER ;" > ~/user-config.jam
|
||||
./b2 -j3 --verbose-test libs/$LIBRARY/test//hash_info toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${ADDRMD:+address-model=$ADDRMD} ${UBSAN:+undefined-sanitizer=norecover debug-symbols=on} ${ASAN:+address-sanitizer=norecover debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS}
|
||||
./b2 -j3 libs/$LIBRARY/test toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${ADDRMD:+address-model=$ADDRMD} ${UBSAN:+undefined-sanitizer=norecover debug-symbols=on} ${ASAN:+address-sanitizer=norecover debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS}
|
||||
425
.github/workflows/ci.yml
vendored
425
.github/workflows/ci.yml
vendored
@@ -18,119 +18,225 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- toolset: gcc-4.8
|
||||
cxxstd: "03,11"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11"
|
||||
container: ubuntu:18.04
|
||||
os: ubuntu-latest
|
||||
install: g++-4.8-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-5
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14,1z"
|
||||
container: ubuntu:18.04
|
||||
os: ubuntu-latest
|
||||
install: g++-5-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-6
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14,1z"
|
||||
container: ubuntu:18.04
|
||||
os: ubuntu-latest
|
||||
install: g++-6-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-7
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14,17"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: g++-7-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-8
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14,17,2a"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: g++-8-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-9
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
cxxstd: "11,14,17,2a"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: g++-9-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-10
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
cxxstd: "11,14,17,2a"
|
||||
os: ubuntu-22.04
|
||||
install: g++-10-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-11
|
||||
cxxstd: "03,11,14,17,20"
|
||||
os: ubuntu-20.04
|
||||
cxxstd: "11,14,17,20"
|
||||
os: ubuntu-22.04
|
||||
install: g++-11-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-12
|
||||
cxxstd: "03,11,14,17,20"
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
os: ubuntu-22.04
|
||||
install: g++-12-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-13
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:24.04
|
||||
os: ubuntu-latest
|
||||
install: g++-13-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-14
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:24.04
|
||||
os: ubuntu-latest
|
||||
install: g++-14-multilib
|
||||
address-model: 32,64
|
||||
- toolset: gcc-15
|
||||
cxxstd: "11,14,17,20,23,2c"
|
||||
container: ubuntu:25.04
|
||||
os: ubuntu-latest
|
||||
install: g++-15-multilib
|
||||
address-model: 32,64
|
||||
- toolset: clang
|
||||
compiler: clang++-3.9
|
||||
cxxstd: "03,11,14"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14"
|
||||
container: ubuntu:18.04
|
||||
os: ubuntu-latest
|
||||
install: clang-3.9
|
||||
- toolset: clang
|
||||
compiler: clang++-4.0
|
||||
cxxstd: "03,11,14"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14"
|
||||
container: ubuntu:18.04
|
||||
os: ubuntu-latest
|
||||
install: clang-4.0
|
||||
- toolset: clang
|
||||
compiler: clang++-5.0
|
||||
cxxstd: "03,11,14,1z"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14,1z"
|
||||
container: ubuntu:18.04
|
||||
os: ubuntu-latest
|
||||
install: clang-5.0
|
||||
- toolset: clang
|
||||
compiler: clang++-6.0
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14,17"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: clang-6.0
|
||||
- toolset: clang
|
||||
compiler: clang++-7
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-18.04
|
||||
cxxstd: "11,14,17"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: clang-7
|
||||
- toolset: clang
|
||||
compiler: clang++-8
|
||||
cxxstd: "03,11,14,17"
|
||||
os: ubuntu-20.04
|
||||
cxxstd: "11,14,17"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: clang-8
|
||||
- toolset: clang
|
||||
compiler: clang++-9
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
cxxstd: "11,14,17,2a"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: clang-9
|
||||
- toolset: clang
|
||||
compiler: clang++-10
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
cxxstd: "11,14,17,2a"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: clang-10
|
||||
- toolset: clang
|
||||
compiler: clang++-11
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: ubuntu-20.04
|
||||
cxxstd: "11,14,17,2a"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: clang-11
|
||||
- toolset: clang
|
||||
compiler: clang++-12
|
||||
cxxstd: "03,11,14,17,20"
|
||||
os: ubuntu-20.04
|
||||
cxxstd: "11,14,17,20"
|
||||
container: ubuntu:20.04
|
||||
os: ubuntu-latest
|
||||
install: clang-12
|
||||
- toolset: clang
|
||||
compiler: clang++-13
|
||||
cxxstd: "03,11,14,17,20"
|
||||
os: ubuntu-22.04
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:22.04
|
||||
os: ubuntu-latest
|
||||
install: clang-13
|
||||
- toolset: clang
|
||||
compiler: clang++-14
|
||||
cxxstd: "03,11,14,17,20"
|
||||
os: ubuntu-22.04
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:22.04
|
||||
os: ubuntu-latest
|
||||
install: clang-14
|
||||
- toolset: clang
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
os: macos-11
|
||||
compiler: clang++-15
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:22.04
|
||||
os: ubuntu-latest
|
||||
install: clang-15
|
||||
- toolset: clang
|
||||
compiler: clang++-16
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:24.04
|
||||
os: ubuntu-latest
|
||||
install: clang-16
|
||||
- toolset: clang
|
||||
compiler: clang++-17
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:24.04
|
||||
os: ubuntu-latest
|
||||
install: clang-17
|
||||
- toolset: clang
|
||||
compiler: clang++-18
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:24.04
|
||||
os: ubuntu-latest
|
||||
install: clang-18
|
||||
- toolset: clang
|
||||
compiler: clang++-19
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
container: ubuntu:24.04
|
||||
os: ubuntu-latest
|
||||
install: clang-19
|
||||
- toolset: clang
|
||||
compiler: clang++-20
|
||||
cxxstd: "11,14,17,20,23,2c"
|
||||
container: ubuntu:25.04
|
||||
os: ubuntu-latest
|
||||
install: clang-20
|
||||
- toolset: clang
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
os: macos-13
|
||||
- toolset: clang
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
os: macos-14
|
||||
- toolset: clang
|
||||
cxxstd: "11,14,17,20,2b"
|
||||
os: macos-15
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
container:
|
||||
image: ${{matrix.container}}
|
||||
volumes:
|
||||
- /node20217:/node20217:rw,rshared
|
||||
- ${{ startsWith(matrix.container, 'ubuntu:1') && '/node20217:/__e/node20:ro,rshared' || ' ' }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup container environment
|
||||
if: matrix.container
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get -y install sudo python3 git g++ curl xz-utils
|
||||
|
||||
- name: Install nodejs20glibc2.17
|
||||
if: ${{ startsWith( matrix.container, 'ubuntu:1' ) }}
|
||||
run: |
|
||||
curl -LO https://archives.boost.io/misc/node/node-v20.9.0-linux-x64-glibc-217.tar.xz
|
||||
tar -xf node-v20.9.0-linux-x64-glibc-217.tar.xz --strip-components 1 -C /node20217
|
||||
ldd /__e/node20/bin/node
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
@@ -150,7 +256,7 @@ jobs:
|
||||
cd boost-root
|
||||
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
|
||||
git submodule update --init tools/boostdep
|
||||
python tools/boostdep/depinst/depinst.py -I examples --git_args "--jobs 3" $LIBRARY
|
||||
python3 tools/boostdep/depinst/depinst.py -I examples --git_args "--jobs 3" $LIBRARY
|
||||
./bootstrap.sh
|
||||
./b2 -d0 headers
|
||||
|
||||
@@ -171,14 +277,6 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- toolset: msvc-14.0
|
||||
cxxstd: 14,latest
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.2
|
||||
cxxstd: "14,17,20,latest"
|
||||
addrmd: 32,64
|
||||
os: windows-2019
|
||||
- toolset: msvc-14.3
|
||||
cxxstd: "14,17,20,latest"
|
||||
addrmd: 32,64
|
||||
@@ -188,14 +286,14 @@ jobs:
|
||||
addrmd: 32,64
|
||||
os: windows-2022
|
||||
- toolset: gcc
|
||||
cxxstd: "03,11,14,17,2a"
|
||||
cxxstd: "11,14,17,2a"
|
||||
addrmd: 64
|
||||
os: windows-2019
|
||||
os: windows-2022
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Boost
|
||||
shell: cmd
|
||||
@@ -231,19 +329,20 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
- os: macos-11
|
||||
- os: ubuntu-24.04
|
||||
- os: macos-13
|
||||
- os: macos-14
|
||||
- os: macos-15
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
run: sudo apt-get -y install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
@@ -278,19 +377,20 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
- os: macos-11
|
||||
- os: ubuntu-24.04
|
||||
- os: macos-13
|
||||
- os: macos-14
|
||||
- os: macos-15
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
run: sudo apt-get -y install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
@@ -335,19 +435,20 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
- os: ubuntu-22.04
|
||||
- os: macos-11
|
||||
- os: ubuntu-24.04
|
||||
- os: macos-13
|
||||
- os: macos-14
|
||||
- os: macos-15
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install packages
|
||||
if: matrix.install
|
||||
run: sudo apt install ${{matrix.install}}
|
||||
run: sudo apt-get -y install ${{matrix.install}}
|
||||
|
||||
- name: Setup Boost
|
||||
run: |
|
||||
@@ -384,3 +485,183 @@ jobs:
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
ctest --output-on-failure --no-tests=error
|
||||
|
||||
windows-cmake-subdir:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2022
|
||||
- os: windows-2025
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- 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%
|
||||
|
||||
- name: Use library with add_subdirectory (Debug)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/libs/%LIBRARY%/test/cmake_subdir_test
|
||||
mkdir __build__ && cd __build__
|
||||
cmake ..
|
||||
cmake --build . --config Debug
|
||||
ctest --output-on-failure --no-tests=error -C Debug
|
||||
|
||||
- name: Use library with add_subdirectory (Release)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/libs/%LIBRARY%/test/cmake_subdir_test/__build__
|
||||
cmake --build . --config Release
|
||||
ctest --output-on-failure --no-tests=error -C Release
|
||||
|
||||
windows-cmake-install:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2022
|
||||
- os: windows-2025
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- 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%
|
||||
|
||||
- name: Configure
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root
|
||||
mkdir __build__ && cd __build__
|
||||
cmake -DBOOST_INCLUDE_LIBRARIES=%LIBRARY% -DCMAKE_INSTALL_PREFIX=C:/cmake-prefix ..
|
||||
|
||||
- name: Install (Debug)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target install --config Debug
|
||||
|
||||
- name: Install (Release)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target install --config Release
|
||||
|
||||
- name: Use the installed library (Debug)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/libs/%LIBRARY%/test/cmake_install_test && mkdir __build__ && cd __build__
|
||||
cmake -DCMAKE_INSTALL_PREFIX=C:/cmake-prefix ..
|
||||
cmake --build . --config Debug
|
||||
ctest --output-on-failure --no-tests=error -C Debug
|
||||
|
||||
- name: Use the installed library (Release)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/libs/%LIBRARY%/test/cmake_install_test/__build__
|
||||
cmake --build . --config Release
|
||||
ctest --output-on-failure --no-tests=error -C Release
|
||||
|
||||
windows-cmake-test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2022
|
||||
- os: windows-2025
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- 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%
|
||||
|
||||
- name: Configure
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root
|
||||
mkdir __build__ && cd __build__
|
||||
cmake -DBOOST_INCLUDE_LIBRARIES=%LIBRARY% -DBUILD_TESTING=ON ..
|
||||
|
||||
- name: Build tests (Debug)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target tests --config Debug
|
||||
|
||||
- name: Run tests (Debug)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
ctest --output-on-failure --no-tests=error -C Debug
|
||||
|
||||
- name: Build tests (Release)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
cmake --build . --target tests --config Release
|
||||
|
||||
- name: Run tests (Release)
|
||||
shell: cmd
|
||||
run: |
|
||||
cd ../boost-root/__build__
|
||||
ctest --output-on-failure --no-tests=error -C Release
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.20)
|
||||
cmake_minimum_required(VERSION 3.8...3.20)
|
||||
|
||||
project(boost_container_hash VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||
|
||||
@@ -15,9 +15,12 @@ target_include_directories(boost_container_hash INTERFACE include)
|
||||
target_link_libraries(boost_container_hash
|
||||
INTERFACE
|
||||
Boost::config
|
||||
Boost::type_traits
|
||||
Boost::describe
|
||||
Boost::mp11
|
||||
)
|
||||
|
||||
target_compile_features(boost_container_hash INTERFACE cxx_std_11)
|
||||
|
||||
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
20
README.md
Normal file
20
README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Boost.ContainerHash
|
||||
|
||||
The Boost.ContainerHash library, part of [Boost C++ Libraries](https://boost.org),
|
||||
provides `boost::hash`, an enhanced implementation of the
|
||||
[hash function](https://en.wikipedia.org/wiki/Hash_function) object specified
|
||||
by C++11 as `std::hash`, and several support facilities (`hash_combine`,
|
||||
`hash_range`, `hash_unordered_range`).
|
||||
|
||||
`boost::hash` supports most standard types and some user-defined types out of
|
||||
the box, and is extensible; it's possible for a user-defined type `X` to make
|
||||
iself hashable via `boost::hash<X>` by defining an appropriate overload of the
|
||||
function `hash_value`.
|
||||
|
||||
See [the documentation of the library](https://www.boost.org/libs/container_hash)
|
||||
for more information.
|
||||
|
||||
## License
|
||||
|
||||
Distributed under the
|
||||
[Boost Software License, Version 1.0](http://boost.org/LICENSE_1_0.txt).
|
||||
4
benchmark/.gitignore
vendored
Normal file
4
benchmark/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
enwik8
|
||||
enwik9
|
||||
*.exe
|
||||
*.obj
|
||||
18
benchmark/Jamfile
Normal file
18
benchmark/Jamfile
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
# Copyright Daniel James 2005. Use, modification, and distribution are
|
||||
# subject to 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)
|
||||
|
||||
import testing ;
|
||||
|
||||
project :
|
||||
requirements
|
||||
<library>/boost/container_hash//boost_container_hash
|
||||
<library>/boost/core//boost_core
|
||||
<library>/boost/unordered//boost_unordered
|
||||
;
|
||||
|
||||
link char_seq.cpp ;
|
||||
link unordered.cpp ;
|
||||
link unordered_flat.cpp : <cxxstd>11:<build>no <cxxstd>14:<build>no ;
|
||||
link word_count.cpp : <cxxstd>11:<build>no <cxxstd>14:<build>no ;
|
||||
@@ -79,11 +79,11 @@ int main()
|
||||
|
||||
if( k & 1 )
|
||||
{
|
||||
sprintf( buffer, "prefix_%llu_suffix", k );
|
||||
std::snprintf( buffer, sizeof( buffer ), "prefix_%llu_suffix", k );
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf( buffer, "{%u}", static_cast<unsigned>( k ) );
|
||||
std::snprintf( buffer, sizeof( buffer ), "{%u}", static_cast<unsigned>( k ) );
|
||||
}
|
||||
|
||||
v.push_back( buffer );
|
||||
|
||||
@@ -3,12 +3,22 @@
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/core/type_name.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/hash/hash.h"
|
||||
#endif
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
# include "ankerl/unordered_dense.h"
|
||||
#endif
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
# include "mulxp_hash.hpp"
|
||||
#endif
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
@@ -19,10 +29,8 @@
|
||||
|
||||
// mul31_hash
|
||||
|
||||
class mul31_hash
|
||||
struct mul31_hash
|
||||
{
|
||||
public:
|
||||
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
char const * p = st.data();
|
||||
@@ -43,18 +51,20 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// mul31_unrolled_hash
|
||||
// mul31_x4_hash
|
||||
|
||||
template<int Bits> struct mul31_unrolled_hash_impl;
|
||||
|
||||
template<> struct mul31_unrolled_hash_impl<32>
|
||||
struct mul31_x4_hash
|
||||
{
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
char const * p = st.data();
|
||||
std::size_t n = st.size();
|
||||
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
std::size_t h = 0xCBF29CE484222325ull;
|
||||
#else
|
||||
std::size_t h = 0x811C9DC5u;
|
||||
#endif
|
||||
|
||||
while( n >= 4 )
|
||||
{
|
||||
@@ -80,14 +90,20 @@ template<> struct mul31_unrolled_hash_impl<32>
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct mul31_unrolled_hash_impl<64>
|
||||
// mul31_x8_hash
|
||||
|
||||
struct mul31_x8_hash
|
||||
{
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
char const * p = st.data();
|
||||
std::size_t n = st.size();
|
||||
|
||||
std::size_t h = 0xCBF29CE484222325ull;
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
boost::uint64_t h = 0xCBF29CE484222325ull;
|
||||
#else
|
||||
boost::uint64_t h = 0x811C9DC5u;
|
||||
#endif
|
||||
|
||||
while( n >= 8 )
|
||||
{
|
||||
@@ -113,12 +129,10 @@ template<> struct mul31_unrolled_hash_impl<64>
|
||||
--n;
|
||||
}
|
||||
|
||||
return h;
|
||||
return static_cast<std::size_t>( h );
|
||||
}
|
||||
};
|
||||
|
||||
struct mul31_unrolled_hash: mul31_unrolled_hash_impl< std::numeric_limits<std::size_t>::digits > {};
|
||||
|
||||
// fnv1a_hash
|
||||
|
||||
template<int Bits> struct fnv1a_hash_impl;
|
||||
@@ -163,28 +177,44 @@ template<> struct fnv1a_hash_impl<64>
|
||||
|
||||
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits > {};
|
||||
|
||||
// old_boost_hash
|
||||
// mulxp_hash
|
||||
|
||||
class old_boost_hash
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
|
||||
struct mulxp1_hash_
|
||||
{
|
||||
public:
|
||||
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
char const * p = st.data();
|
||||
std::size_t n = st.size();
|
||||
|
||||
std::size_t h = 0;
|
||||
|
||||
for( std::size_t i = 0; i < n; ++i )
|
||||
{
|
||||
h ^= static_cast<unsigned char>( p[i] ) + 0x9e3779b9 + ( h << 6 ) + ( h >> 2 );
|
||||
}
|
||||
|
||||
return h;
|
||||
return mulxp1_hash( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp3_hash_
|
||||
{
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
return mulxp3_hash( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp1_hash32_
|
||||
{
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
return mulxp1_hash32( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp3_hash32_
|
||||
{
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
return mulxp3_hash32( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// test_hash_speed
|
||||
|
||||
template<class H, class V> void test_hash_speed( int N, V const& v )
|
||||
@@ -210,11 +240,11 @@ template<class H, class V> void test_hash_speed( int N, V const& v )
|
||||
|
||||
#if defined( _MSC_VER )
|
||||
|
||||
std::printf( "%25s : q=%20Iu, %lld ms\n", hash.c_str(), q, ms1 );
|
||||
std::printf( "%57s : q=%20Iu, %lld ms\n", hash.c_str(), q, ms1 );
|
||||
|
||||
#else
|
||||
|
||||
std::printf( "%25s : q=%20zu, %lld ms\n", hash.c_str(), q, ms1 );
|
||||
std::printf( "%57s : q=%20zu, %lld ms\n", hash.c_str(), q, ms1 );
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -235,11 +265,11 @@ template<class H, class V> void test_hash_collision( int N, V const& v, std::siz
|
||||
|
||||
#if defined( _MSC_VER )
|
||||
|
||||
std::printf( "%25s : c=%Iu\n", hash.c_str(), n - s.size() );
|
||||
std::printf( "%57s : c=%Iu\n", hash.c_str(), n - s.size() );
|
||||
|
||||
#else
|
||||
|
||||
std::printf( "%25s : c=%zu\n", hash.c_str(), n - s.size() );
|
||||
std::printf( "%57s : c=%zu\n", hash.c_str(), n - s.size() );
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -292,11 +322,11 @@ template<class V, class S> void test4( int N, V const& v, char const * hash, S s
|
||||
|
||||
#if defined( _MSC_VER )
|
||||
|
||||
std::printf( "%25s : n=%Iu, m=%Iu, c=%Iu, q=%Iu, %lld + %lld ms\n", hash, n, m, c, q, ms1, ms2 );
|
||||
std::printf( "%57s : n=%Iu, m=%Iu, c=%Iu, q=%Iu, %4lld + %4lld = %4lld ms\n", hash, n, m, c, q, ms1, ms2, ms1 + ms2 );
|
||||
|
||||
#else
|
||||
|
||||
std::printf( "%25s : n=%zu, m=%zu, c=%zu, q=%zu, %lld + %lld ms\n", hash, n, m, c, q, ms1, ms2 );
|
||||
std::printf( "%57s : n=%zu, m=%zu, c=%zu, q=%zu, %4lld + %4lld = %4lld ms\n", hash, n, m, c, q, ms1, ms2, ms1 + ms2 );
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -326,11 +356,11 @@ int main()
|
||||
|
||||
if( k & 1 )
|
||||
{
|
||||
sprintf( buffer, "prefix_%llu_suffix", k );
|
||||
std::snprintf( buffer, sizeof( buffer ), "prefix_%llu_suffix", k );
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf( buffer, "{%u}", static_cast<unsigned>( k ) );
|
||||
std::snprintf( buffer, sizeof( buffer ), "{%u}", static_cast<unsigned>( k ) );
|
||||
}
|
||||
|
||||
v.push_back( buffer );
|
||||
@@ -340,11 +370,23 @@ int main()
|
||||
std::puts( "Hash speed test:\n" );
|
||||
|
||||
test_hash_speed<mul31_hash>( N * 16, v );
|
||||
test_hash_speed<mul31_unrolled_hash>( N * 16, v );
|
||||
test_hash_speed<mul31_x4_hash>( N * 16, v );
|
||||
test_hash_speed<mul31_x8_hash>( N * 16, v );
|
||||
test_hash_speed<fnv1a_hash>( N * 16, v );
|
||||
test_hash_speed<old_boost_hash>( N * 16, v );
|
||||
test_hash_speed<boost::hash<std::string> >( N * 16, v );
|
||||
test_hash_speed<std::hash<std::string> >( N * 16, v );
|
||||
#ifdef HAVE_ABSEIL
|
||||
test_hash_speed<absl::Hash<std::string> >( N * 16, v );
|
||||
#endif
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
test_hash_speed<ankerl::unordered_dense::hash<std::string> >( N * 16, v );
|
||||
#endif
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
test_hash_speed<mulxp1_hash_>( N * 16, v );
|
||||
test_hash_speed<mulxp3_hash_>( N * 16, v );
|
||||
test_hash_speed<mulxp1_hash32_>( N * 16, v );
|
||||
test_hash_speed<mulxp3_hash32_>( N * 16, v );
|
||||
#endif
|
||||
|
||||
std::puts( "" );
|
||||
|
||||
@@ -365,25 +407,55 @@ int main()
|
||||
}
|
||||
|
||||
test_hash_collision<mul31_hash>( N * 16, v, n );
|
||||
test_hash_collision<mul31_unrolled_hash>( N * 16, v, n );
|
||||
test_hash_collision<mul31_x4_hash>( N * 16, v, n );
|
||||
test_hash_collision<mul31_x8_hash>( N * 16, v, n );
|
||||
test_hash_collision<fnv1a_hash>( N * 16, v, n );
|
||||
test_hash_collision<old_boost_hash>( N * 16, v, n );
|
||||
test_hash_collision<boost::hash<std::string> >( N * 16, v, n );
|
||||
test_hash_collision<std::hash<std::string> >( N * 16, v, n );
|
||||
#ifdef HAVE_ABSEIL
|
||||
test_hash_collision<absl::Hash<std::string> >( N * 16, v, n );
|
||||
#endif
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
test_hash_collision<ankerl::unordered_dense::hash<std::string> >( N * 16, v, n );
|
||||
#endif
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
test_hash_collision<mulxp1_hash_>( N * 16, v, n );
|
||||
test_hash_collision<mulxp3_hash_>( N * 16, v, n );
|
||||
test_hash_collision<mulxp1_hash32_>( N * 16, v, n );
|
||||
test_hash_collision<mulxp3_hash32_>( N * 16, v, n );
|
||||
#endif
|
||||
}
|
||||
|
||||
std::puts( "" );
|
||||
|
||||
typedef std::string K;
|
||||
|
||||
std::puts( "Container speed test:\n" );
|
||||
std::puts( "Container speed test:\n---\n" );
|
||||
|
||||
test_container_speed<K, mul31_hash>( N, v );
|
||||
test_container_speed<K, mul31_unrolled_hash>( N, v );
|
||||
test_container_speed<K, mul31_x4_hash>( N, v );
|
||||
test_container_speed<K, mul31_x8_hash>( N, v );
|
||||
test_container_speed<K, fnv1a_hash>( N, v );
|
||||
test_container_speed<K, old_boost_hash>( N, v );
|
||||
test_container_speed<K, boost::hash<std::string> >( N, v );
|
||||
test_container_speed<K, std::hash<std::string> >( N, v );
|
||||
#ifdef HAVE_ABSEIL
|
||||
test_container_speed<K, absl::Hash<std::string> >( N, v );
|
||||
#endif
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
test_container_speed<K, ankerl::unordered_dense::hash<std::string> >( N, v );
|
||||
#endif
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
test_container_speed<K, mulxp1_hash_>( N, v );
|
||||
test_container_speed<K, mulxp3_hash_>( N, v );
|
||||
test_container_speed<K, mulxp1_hash32_>( N, v );
|
||||
test_container_speed<K, mulxp3_hash32_>( N, v );
|
||||
#endif
|
||||
|
||||
std::puts( "" );
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
|
||||
386
benchmark/unordered_flat.cpp
Normal file
386
benchmark/unordered_flat.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
// 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
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/core/detail/splitmix64.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/hash/hash.h"
|
||||
#endif
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
# include "ankerl/unordered_dense.h"
|
||||
#endif
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
# include "mulxp_hash.hpp"
|
||||
#endif
|
||||
#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 )
|
||||
{
|
||||
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
|
||||
{
|
||||
map.erase( it++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
it = map.erase( it );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices1[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Consecutive erase", 0, map.size() );
|
||||
|
||||
for( unsigned i = 1; i <= N; ++i )
|
||||
{
|
||||
map.erase( indices2[ i ] );
|
||||
}
|
||||
|
||||
print_time( t1, "Random erase", 0, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<class Hash> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
boost::unordered_flat_map<std::string, std::uint32_t, Hash> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_insert( map, t1 );
|
||||
|
||||
record rec = { label, 0 };
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
// 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 >
|
||||
{
|
||||
using is_avalanching = void;
|
||||
};
|
||||
|
||||
// std_hash
|
||||
|
||||
struct std_hash: std::hash<std::string>
|
||||
{
|
||||
using is_avalanching = void;
|
||||
};
|
||||
|
||||
// absl_hash
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
struct absl_hash: absl::Hash<std::string>
|
||||
{
|
||||
using is_avalanching = void;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// mulxp_hash
|
||||
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
|
||||
struct mulxp1_hash_
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
return mulxp1_hash( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp3_hash_
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
return mulxp3_hash( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp1_hash32_
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t r = mulxp1_hash32( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
|
||||
r |= r << 32;
|
||||
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp3_hash32_
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
std::size_t operator()( std::string const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t r = mulxp3_hash32( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
|
||||
r |= r << 32;
|
||||
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
int main()
|
||||
{
|
||||
init_indices();
|
||||
|
||||
test< boost::hash<std::string> >( "boost::hash" );
|
||||
test< std_hash >( "std::hash" );
|
||||
test< fnv1a_hash >( "fnv1a_hash" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test< absl_hash >( "absl::Hash" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
test< ankerl::unordered_dense::hash<std::string> >( "ankerl::unordered_dense::hash" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
|
||||
test< mulxp1_hash_ >( "mulxp1_hash" );
|
||||
test< mulxp3_hash_ >( "mulxp3_hash" );
|
||||
test< mulxp1_hash32_ >( "mulxp1_hash32" );
|
||||
test< mulxp3_hash32_ >( "mulxp3_hash32" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 32 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
319
benchmark/word_count.cpp
Normal file
319
benchmark/word_count.cpp
Normal file
@@ -0,0 +1,319 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
|
||||
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/hash/hash.h"
|
||||
#endif
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
# include "ankerl/unordered_dense.h"
|
||||
#endif
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
# include "mulxp_hash.hpp"
|
||||
#endif
|
||||
#include <boost/regex.hpp>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static std::vector<std::string> words;
|
||||
|
||||
static void init_words()
|
||||
{
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
|
||||
char const* fn = "enwik9"; // http://mattmahoney.net/dc/textdata
|
||||
|
||||
#else
|
||||
|
||||
char const* fn = "enwik8"; // ditto
|
||||
|
||||
#endif
|
||||
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
std::ifstream is( fn );
|
||||
std::string in( std::istreambuf_iterator<char>( is ), std::istreambuf_iterator<char>{} );
|
||||
|
||||
boost::regex re( "[a-zA-Z]+");
|
||||
boost::sregex_token_iterator it( in.begin(), in.end(), re, 0 ), end;
|
||||
|
||||
words.assign( it, end );
|
||||
|
||||
auto t2 = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << fn << ": " << words.size() << " words, " << ( t2 - t1 ) / 1ms << " ms\n\n";
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_word_count( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::size_t s = 0;
|
||||
|
||||
for( auto const& word: words )
|
||||
{
|
||||
++map[ word ];
|
||||
++s;
|
||||
}
|
||||
|
||||
print_time( t1, "Word count", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_contains( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::size_t s = 0;
|
||||
|
||||
for( auto const& word: words )
|
||||
{
|
||||
std::string_view w2( word );
|
||||
w2.remove_prefix( 1 );
|
||||
|
||||
s += map.contains( w2 );
|
||||
}
|
||||
|
||||
print_time( t1, "Contains", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<class Map> BOOST_NOINLINE void test_count( Map& map, std::chrono::steady_clock::time_point & t1 )
|
||||
{
|
||||
std::size_t s = 0;
|
||||
|
||||
for( auto const& word: words )
|
||||
{
|
||||
std::string_view w2( word );
|
||||
w2.remove_prefix( 1 );
|
||||
|
||||
s += map.count( w2 );
|
||||
}
|
||||
|
||||
print_time( t1, "Count", s, map.size() );
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
struct record
|
||||
{
|
||||
std::string label_;
|
||||
long long time_;
|
||||
};
|
||||
|
||||
static std::vector<record> times;
|
||||
|
||||
template<class Hash> BOOST_NOINLINE void test( char const* label )
|
||||
{
|
||||
std::cout << label << ":\n\n";
|
||||
|
||||
boost::unordered_flat_map<std::string_view, std::size_t, Hash> map;
|
||||
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
auto t1 = t0;
|
||||
|
||||
test_word_count( map, t1 );
|
||||
|
||||
record rec = { label, 0 };
|
||||
|
||||
test_contains( map, t1 );
|
||||
test_count( 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 );
|
||||
}
|
||||
|
||||
// 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 >
|
||||
{
|
||||
using is_avalanching = void;
|
||||
};
|
||||
|
||||
// std_hash
|
||||
|
||||
struct std_hash: std::hash<std::string_view>
|
||||
{
|
||||
using is_avalanching = void;
|
||||
};
|
||||
|
||||
// absl_hash
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
struct absl_hash: absl::Hash<std::string_view>
|
||||
{
|
||||
using is_avalanching = void;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
|
||||
struct mulxp1_hash_
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
return mulxp1_hash( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp3_hash_
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
return mulxp3_hash( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp3_hash32_
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t r = mulxp3_hash32( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
|
||||
r |= r << 32;
|
||||
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
struct mulxp1_hash32_
|
||||
{
|
||||
using is_avalanching = void;
|
||||
|
||||
std::size_t operator()( std::string_view const& st ) const BOOST_NOEXCEPT
|
||||
{
|
||||
std::size_t r = mulxp1_hash32( (unsigned char const*)st.data(), st.size(), 0 );
|
||||
|
||||
#if SIZE_MAX > UINT32_MAX
|
||||
|
||||
r |= r << 32;
|
||||
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
|
||||
int main()
|
||||
{
|
||||
init_words();
|
||||
|
||||
test< boost::hash<std::string_view> >( "boost::hash" );
|
||||
test< std_hash >( "std::hash" );
|
||||
test< fnv1a_hash >( "fnv1a_hash" );
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
|
||||
test< absl_hash >( "absl::Hash" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ANKERL_UNORDERED_DENSE
|
||||
|
||||
test< ankerl::unordered_dense::hash<std::string_view> >( "ankerl::unordered_dense::hash" );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MULXP_HASH
|
||||
|
||||
test< mulxp1_hash_ >( "mulxp1_hash" );
|
||||
test< mulxp3_hash_ >( "mulxp3_hash" );
|
||||
test< mulxp1_hash32_ >( "mulxp1_hash32" );
|
||||
test< mulxp3_hash32_ >( "mulxp3_hash32" );
|
||||
|
||||
#endif
|
||||
|
||||
std::cout << "---\n\n";
|
||||
|
||||
for( auto const& x: times )
|
||||
{
|
||||
std::cout << std::setw( 32 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms\n";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ABSEIL
|
||||
# include "absl/hash/internal/hash.cc"
|
||||
# include "absl/hash/internal/low_level_hash.cc"
|
||||
# include "absl/hash/internal/city.cc"
|
||||
#endif
|
||||
22
build.jam
Normal file
22
build.jam
Normal file
@@ -0,0 +1,22 @@
|
||||
# Copyright 2023-2024 René Ferdinand Rivera Morell
|
||||
# Copyright 2024 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
require-b2 5.2 ;
|
||||
|
||||
constant boost_dependencies :
|
||||
/boost/config//boost_config
|
||||
/boost/describe//boost_describe
|
||||
/boost/mp11//boost_mp11
|
||||
;
|
||||
|
||||
project /boost/container_hash ;
|
||||
|
||||
explicit
|
||||
[ alias boost_container_hash : : : : <include>include <library>$(boost_dependencies) ]
|
||||
[ alias all : boost_container_hash examples test ]
|
||||
;
|
||||
|
||||
call-if : boost-library container_hash
|
||||
;
|
||||
@@ -18,11 +18,13 @@ https://www.boost.org/LICENSE_1_0.txt
|
||||
:leveloffset: +1
|
||||
|
||||
include::hash/intro.adoc[]
|
||||
include::hash/recent.adoc[]
|
||||
include::hash/tutorial.adoc[]
|
||||
include::hash/user.adoc[]
|
||||
include::hash/combine.adoc[]
|
||||
include::hash/describe.adoc[]
|
||||
include::hash/reference.adoc[]
|
||||
include::hash/rationale.adoc[]
|
||||
include::hash/notes.adoc[]
|
||||
include::hash/links.adoc[]
|
||||
include::hash/thanks.adoc[]
|
||||
include::hash/changes.adoc[]
|
||||
|
||||
@@ -12,7 +12,6 @@ https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
:int128: __int128
|
||||
|
||||
[discrete]
|
||||
== Boost 1.67.0
|
||||
* Moved library into its own module, `container_hash`.
|
||||
* Moved headers for new module name, now at: `<boost/container_hash/hash.hpp>`, `<boost/container_hash/hash_fwd.hpp>`, `<boost/container_hash/extensions.hpp>`.
|
||||
@@ -23,11 +22,9 @@ https://www.boost.org/LICENSE_1_0.txt
|
||||
* Fix tutorial example (https://svn.boost.org/trac/boost/ticket/11017[#11017]).
|
||||
* Quick fix for hashing `vector<bool>` when using libc++. Will try to introduce a more general fix in the next release.
|
||||
|
||||
[discrete]
|
||||
== Boost 1.66.0
|
||||
* Avoid float comparison warning when using Clang - this workaround was already in place for GCC, and was used when Clang pretends to be GCC, but the warning was appearing when running Clang in other contexts.
|
||||
|
||||
[discrete]
|
||||
== Boost 1.65.0
|
||||
* Support for `char16_t`, `char32_t`, `u16string`, `u32string`
|
||||
|
||||
|
||||
61
doc/hash/describe.adoc
Normal file
61
doc/hash/describe.adoc
Normal file
@@ -0,0 +1,61 @@
|
||||
////
|
||||
Copyright 2022 Peter Dimov
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#describe]
|
||||
= Hashing User Types with Boost.Describe
|
||||
:idprefix: describe_
|
||||
|
||||
Let's look at our `point` class again:
|
||||
|
||||
[source]
|
||||
----
|
||||
class point
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
|
||||
public:
|
||||
|
||||
point() : x(0), y(0) {}
|
||||
point(int x, int y) : x(x), y(y) {}
|
||||
};
|
||||
----
|
||||
|
||||
If you're using {cpp}14 or above, a much easier way to add
|
||||
support for `boost::hash` to `point` is by using
|
||||
link:../../../describe/index.html[Boost.Describe] (and
|
||||
get an automatic definition of `operator==` for free):
|
||||
|
||||
[source]
|
||||
----
|
||||
|
||||
#include <boost/describe/class.hpp>
|
||||
#include <boost/describe/operators.hpp>
|
||||
|
||||
class point
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
|
||||
BOOST_DESCRIBE_CLASS(point, (), (), (), (x, y))
|
||||
|
||||
public:
|
||||
|
||||
point() : x(0), y(0) {}
|
||||
point(int x, int y) : x(x), y(y) {}
|
||||
};
|
||||
|
||||
using boost::describe::operators::operator==;
|
||||
using boost::describe::operators::operator!=;
|
||||
----
|
||||
|
||||
(Full code for this example is at
|
||||
link:../../examples/point2.cpp[examples/point2.cpp].)
|
||||
|
||||
Since the `point` class has been annotated with `BOOST_DESCRIBE_CLASS`,
|
||||
the library can enumerate its members (and base classes) and automatically
|
||||
synthesize the appropriate `hash_value` overload for it, without us needing
|
||||
to do so.
|
||||
@@ -16,22 +16,27 @@ https://en.wikipedia.org/wiki/Hash_function[hash function] object specified by
|
||||
link:../../../unordered/index.html[Boost.Unordered],
|
||||
link:../../../intrusive/index.html[Boost.Intrusive]'s unordered associative
|
||||
containers, link:../../../multi_index/index.html[Boost.MultiIndex]'s hash
|
||||
indicies, and link:../../../bimap/index.html[Boost.Bimap]'s `unordered_set_of`.
|
||||
indices, and link:../../../bimap/index.html[Boost.Bimap]'s `unordered_set_of`.
|
||||
|
||||
Out of the box, `boost::hash` supports
|
||||
|
||||
* standard integral types (integers, character types, and `bool`);
|
||||
* standard floating point types (`float`, `double`, `long double`);
|
||||
* pointers (to objects and to functions, but not pointers to members);
|
||||
* pointers (to objects and to functions, but not pointers to members)
|
||||
and `nullptr`;
|
||||
* enumeration types;
|
||||
* C arrays;
|
||||
* `std::complex`;
|
||||
* `std::pair`, `std::tuple`;
|
||||
* tuple-like types, such as `std::pair`, `std::tuple`, and user-defined
|
||||
types that specialize `std::tuple_size` and provide `get<I>`;
|
||||
* sequence-like types, both standard and user-defined (sequence-like types
|
||||
have `begin()` and `end()` member functions returning iterators);
|
||||
* unordered sequences, standard or user-defined (sequences for which the hash
|
||||
value does not depend on the element order, such as `std::unordered_set` and
|
||||
`std::unordered_map`);
|
||||
* described structs and classes -- ones that have been annotated with the
|
||||
`BOOST_DESCRIBE_STRUCT` or `BOOST_DESCRIBE_CLASS` macros from
|
||||
link:../../../describe/index.html[Boost.Describe];
|
||||
* `std::unique_ptr`, `std::shared_ptr`;
|
||||
* `std::type_index`;
|
||||
* `std::error_code`, `std::error_condition`;
|
||||
@@ -39,8 +44,9 @@ Out of the box, `boost::hash` supports
|
||||
* `std::variant`, `std::monostate`.
|
||||
|
||||
`boost::hash` is extensible; it's possible for a user-defined type `X` to make
|
||||
iself hashable via `boost::hash<X>`. Many, if not most, Boost types already
|
||||
contain the necessary support.
|
||||
iself hashable via `boost::hash<X>` by defining an appropriate overload of the
|
||||
function `hash_value`. Many, if not most, Boost types already contain the
|
||||
necessary support.
|
||||
|
||||
`boost::hash` meets the requirements for `std::hash` specified in the {cpp}11
|
||||
standard, namely, that for two different input values their corresponding hash
|
||||
|
||||
@@ -33,12 +33,21 @@ The library implements the extension described in Issue 6.18, pages 63-67.
|
||||
|
||||
*Methods for Identifying Versioned and Plagiarised Documents* +
|
||||
_Timothy C. Hoad, Justin Zobel_ +
|
||||
http://www.cs.rmit.edu.au/~jz/fulltext/jasist-tch.pdf
|
||||
https://people.eng.unimelb.edu.au/jzobel/fulltext/jasist03thz.pdf
|
||||
|
||||
Contains the hash function that the initial implementation of `boost::hash_combine` was based on.
|
||||
|
||||
---
|
||||
|
||||
*Performance in Practice of String Hashing Functions* +
|
||||
_M.V. Ramakrishna, J. Zobel_ +
|
||||
In Proc. Int. Conf. on Database Systems for Advanced Applications, pages 215-223, Melbourne, Australia, April 1997. +
|
||||
https://www.comp.nus.edu.sg/~lingtw/dasfaa_proceedings/DASFAA97/P215.pdf
|
||||
|
||||
Referenced in the above paper as the source of the hash function.
|
||||
|
||||
---
|
||||
|
||||
*MurmurHash3 hash function source* +
|
||||
_Austin Appleby_ +
|
||||
https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash3.cpp#L65-L90
|
||||
@@ -53,7 +62,9 @@ transformation that approximates a random permutation.
|
||||
_Austin Appleby_ +
|
||||
https://github.com/aappleby/smhasher
|
||||
|
||||
Contains a battery of tests for evaluating hash functions.
|
||||
Contains a battery of tests for evaluating hash functions. The current
|
||||
64 bit implementation of `boost::hash` for strings passes SMHasher.
|
||||
Previous iterations did not.
|
||||
|
||||
---
|
||||
|
||||
@@ -62,8 +73,8 @@ _David Stafford_ +
|
||||
https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
|
||||
|
||||
Describes the so-called "variant 13" mixing function, an improvement
|
||||
over `fmix64` from MurmurHash3, made famous by its adoption by the `splitmix64`
|
||||
http://xorshift.di.unimi.it/splitmix64.c[random number generator].
|
||||
over `fmix64` from MurmurHash3, made famous by its adoption by the
|
||||
`splitmix64` http://xorshift.di.unimi.it/splitmix64.c[random number generator].
|
||||
|
||||
---
|
||||
|
||||
@@ -71,15 +82,18 @@ http://xorshift.di.unimi.it/splitmix64.c[random number generator].
|
||||
_Pelle Evensen_ +
|
||||
https://mostlymangling.blogspot.com/2019/12/stronger-better-morer-moremur-better.html
|
||||
|
||||
Describes Moremur, an improvement over MurmurHash3 fmix64 and Stafford "variant 13".
|
||||
Describes Moremur, an improvement over MurmurHash3 `fmix64` and Stafford
|
||||
"variant 13".
|
||||
|
||||
---
|
||||
|
||||
*Improved mx3 and the RRC test* +
|
||||
_John Maiga_ +
|
||||
_Jon Maiga_ +
|
||||
http://jonkagstrom.com/mx3/mx3_rev2.html
|
||||
|
||||
Contains another improvement over MurmurHash3 fmix64 and "variant 13".
|
||||
Contains another improvement over MurmurHash3 `fmix64` and "variant 13". This
|
||||
is what the current implementation of `boost::hash_combine` uses when
|
||||
`std::size_t` is 64 bits.
|
||||
|
||||
---
|
||||
|
||||
@@ -96,4 +110,5 @@ a utility for discovering and evaluating mixing functions.
|
||||
_"TheIronBorn"_ +
|
||||
https://github.com/skeeto/hash-prospector/issues/19
|
||||
|
||||
Describes a good 32 bit mixing function.
|
||||
Describes a good 32 bit mixing function, used by the current implementation
|
||||
of `boost::hash_combine` when `std::size_t` is 32 bits.
|
||||
|
||||
228
doc/hash/notes.adoc
Normal file
228
doc/hash/notes.adoc
Normal file
@@ -0,0 +1,228 @@
|
||||
////
|
||||
Copyright 2005-2008 Daniel James
|
||||
Copyright 2022 Christian Mazakas
|
||||
Copyright 2022 Peter Dimov
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#notes]
|
||||
= Design and Implementation Notes
|
||||
:idprefix: notes_
|
||||
|
||||
== Quality of the Hash Function
|
||||
|
||||
Many hash functions strive to have little correlation between the input and
|
||||
output values. They attempt to uniformally distribute the output values for
|
||||
very similar inputs. This hash function makes no such attempt. In fact, for
|
||||
integers, the result of the hash function is often just the input value. So
|
||||
similar but different input values will often result in similar but different
|
||||
output values. This means that it is not appropriate as a general hash
|
||||
function. For example, a hash table may discard bits from the hash function
|
||||
resulting in likely collisions, or might have poor collision resolution when
|
||||
hash values are clustered together. In such cases this hash function will
|
||||
perform poorly.
|
||||
|
||||
But the standard has no such requirement for the hash function, it just
|
||||
requires that the hashes of two different values are unlikely to collide.
|
||||
Containers or algorithms designed to work with the standard hash function will
|
||||
have to be implemented to work well when the hash function's output is
|
||||
correlated to its input. Since they are paying that cost a higher quality hash
|
||||
function would be wasteful.
|
||||
|
||||
== The hash_value Customization Point
|
||||
|
||||
The way one customizes the standard `std::hash` function object for user
|
||||
types is via a specialization. `boost::hash` chooses a different mechanism --
|
||||
an overload of a free function `hash_value` in the user namespace that is
|
||||
found via argument-dependent lookup.
|
||||
|
||||
Both approaches have their pros and cons. Specializing the function object
|
||||
is stricter in that it only applies to the exact type, and not to derived
|
||||
or convertible types. Defining a function, on the other hand, is easier
|
||||
and more convenient, as it can be done directly in the type definition as
|
||||
an `inline` `friend`.
|
||||
|
||||
The fact that overloads can be invoked via conversions did cause issues in
|
||||
an earlier iteration of the library that defined `hash_value` for all
|
||||
integral types separately, including `bool`. Especially under {cpp}03,
|
||||
which doesn't have `explicit` conversion operators, some types were
|
||||
convertible to `bool` to allow their being tested in e.g. `if` statements,
|
||||
which caused them to hash to 0 or 1, rarely what one expects or wants.
|
||||
|
||||
This, however, was fixed by declaring the built-in `hash_value` overloads
|
||||
to be templates constrained on e.g. `std::is_integral` or its moral
|
||||
equivalent. This causes types convertible to an integral to no longer
|
||||
match, avoiding the problem.
|
||||
|
||||
== Hash Value Stability
|
||||
|
||||
In general, the library does not promise that the hash values will stay
|
||||
the same from release to release (otherwise improvements would be
|
||||
impossible). However, historically values have been quite stable. Before
|
||||
release 1.81, the previous changes have been in 1.56 (a better
|
||||
`hash_combine`) and 1.78 (macOS-specific change to `hash_combine`.)
|
||||
|
||||
Code should generally not depend on specific hash values, but for those
|
||||
willing to take the risk of occasional breaks due to hash value changes,
|
||||
the library now has a test that checks hash values for a number of types
|
||||
against reference values (`test/hash_reference_values.cpp`),
|
||||
whose https://github.com/boostorg/container_hash/commits/develop/test/hash_reference_values.cpp[version history]
|
||||
can be used as a rough guide to when hash values have changed, and for what
|
||||
types.
|
||||
|
||||
== hash_combine
|
||||
|
||||
The initial implementation of the library was based on Issue 6.18 of the
|
||||
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1837.pdf[Library Extension Technical Report Issues List]
|
||||
(pages 63-67) which proposed the following implementation of `hash_combine`:
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class T>
|
||||
void hash_combine(size_t & seed, T const & v)
|
||||
{
|
||||
seed ^= hash_value(v) + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
----
|
||||
|
||||
taken from the paper
|
||||
"https://people.eng.unimelb.edu.au/jzobel/fulltext/jasist03thz.pdf[Methods for Identifying Versioned and Plagiarised Documents]"
|
||||
by Timothy C. Hoad and Justin Zobel.
|
||||
|
||||
During the Boost formal review, Dave Harris pointed out that this suffers
|
||||
from the so-called "zero trap"; if `seed` is initially 0, and all the
|
||||
inputs are 0 (or hash to 0), `seed` remains 0 no matter how many input
|
||||
values are combined.
|
||||
|
||||
This is an undesirable property, because it causes containers of zeroes
|
||||
to have a zero hash value regardless of their sizes.
|
||||
|
||||
To fix this, the arbitrary constant `0x9e3779b9` (the golden ratio in a
|
||||
32 bit fixed point representation) was added to the computation, yielding
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class T>
|
||||
void hash_combine(size_t & seed, T const & v)
|
||||
{
|
||||
seed ^= hash_value(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
----
|
||||
|
||||
This is what shipped in Boost 1.33, the first release containing the library.
|
||||
|
||||
This function was a reasonable compromise between quality and speed for its
|
||||
time, when the input consisted of ``char``s, but it's less suitable for
|
||||
combining arbitrary `size_t` inputs.
|
||||
|
||||
In Boost 1.56, it was replaced by functions derived from Austin Appleby's
|
||||
https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash2.cpp#L57-L62[MurmurHash2 hash function round].
|
||||
|
||||
In Boost 1.81, it was changed again -- to the equivalent of
|
||||
`mix(seed + 0x9e3779b9 + hash_value(v))`, where `mix(x)` is a high quality
|
||||
mixing function that is a bijection over the `size_t` values, of the form
|
||||
|
||||
[source]
|
||||
----
|
||||
x ^= x >> k1;
|
||||
x *= m1;
|
||||
x ^= x >> k2;
|
||||
x *= m2;
|
||||
x ^= x >> k3;
|
||||
----
|
||||
|
||||
This type of mixing function was originally devised by Austin Appleby as
|
||||
the "final mix" part of his MurmurHash3 hash function. He used
|
||||
|
||||
[source]
|
||||
----
|
||||
x ^= x >> 33;
|
||||
x *= 0xff51afd7ed558ccd;
|
||||
x ^= x >> 33;
|
||||
x *= 0xc4ceb9fe1a85ec53;
|
||||
x ^= x >> 33;
|
||||
----
|
||||
|
||||
as the https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash2.cpp#L57-L62[64 bit function `fmix64`] and
|
||||
|
||||
[source]
|
||||
----
|
||||
x ^= x >> 16;
|
||||
x *= 0x85ebca6b;
|
||||
x ^= x >> 13;
|
||||
x *= 0xc2b2ae35;
|
||||
x ^= x >> 16;
|
||||
----
|
||||
|
||||
as the https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash3.cpp#L68-L77[32 bit function `fmix32`].
|
||||
|
||||
Several improvements of the 64 bit function have been subsequently proposed,
|
||||
by https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html[David Stafford],
|
||||
https://mostlymangling.blogspot.com/2019/12/stronger-better-morer-moremur-better.html[Pelle Evensen],
|
||||
and http://jonkagstrom.com/mx3/mx3_rev2.html[Jon Maiga]. We currently use Jon
|
||||
Maiga's function
|
||||
|
||||
[source]
|
||||
----
|
||||
x ^= x >> 32;
|
||||
x *= 0xe9846af9b1a615d;
|
||||
x ^= x >> 32;
|
||||
x *= 0xe9846af9b1a615d;
|
||||
x ^= x >> 28;
|
||||
----
|
||||
|
||||
Under 32 bit, we use a mixing function proposed by "TheIronBorn" in a
|
||||
https://github.com/skeeto/hash-prospector/issues/19[Github issue] in
|
||||
the https://github.com/skeeto/hash-prospector[repository] of
|
||||
https://nullprogram.com/blog/2018/07/31/[Hash Prospector] by Chris Wellons:
|
||||
|
||||
[source]
|
||||
----
|
||||
x ^= x >> 16;
|
||||
x *= 0x21f0aaad;
|
||||
x ^= x >> 15;
|
||||
x *= 0x735a2d97;
|
||||
x ^= x >> 15;
|
||||
----
|
||||
|
||||
With this improved `hash_combine`, `boost::hash` for strings now passes the
|
||||
https://github.com/aappleby/smhasher[SMHasher test suite] by Austin Appleby
|
||||
(for a 64 bit `size_t`).
|
||||
|
||||
== hash_range
|
||||
|
||||
The traditional implementation of `hash_range(seed, first, last)` has been
|
||||
|
||||
[source]
|
||||
----
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
boost::hash_combine<typename std::iterator_traits<It>::value_type>( seed, *first );
|
||||
}
|
||||
----
|
||||
|
||||
(the explicit template parameter is needed to support iterators with proxy
|
||||
return types such as `std::vector<bool>::iterator`.)
|
||||
|
||||
This is logical, consistent and straightforward. In the common case where
|
||||
`typename std::iterator_traits<It>::value_type` is `char` -- which it is
|
||||
in the common case of `boost::hash<std::string>` -- this however leaves a
|
||||
lot of performance on the table, because processing each `char` individually
|
||||
is much less efficient than processing several in bulk.
|
||||
|
||||
In Boost 1.81, `hash_range` was changed to process elements of type `char`,
|
||||
`signed char`, `unsigned char`, `std::byte`, or `char8_t`, four of a time.
|
||||
A `uint32_t` is composed from `first[0]` to `first[3]`, and that `uint32_t`
|
||||
is fed to `hash_combine`.
|
||||
|
||||
In Boost 1.82, `hash_range` for these types was changed to use
|
||||
https://github.com/pdimov/mulxp_hash[`mulxp1_hash`]. This improves both
|
||||
quality and speed of string hashing.
|
||||
|
||||
Note that `hash_range` has also traditionally guaranteed that the same element
|
||||
sequence yields the same hash value regardless of the iterator type. This
|
||||
property remains valid after the changes to `char` range hashing. `hash_range`,
|
||||
applied to the `char` sequence `{ 'a', 'b', 'c' }`, results in the same value
|
||||
whether the sequence comes from `char[3]`, `std::string`, `std::deque<char>`,
|
||||
or `std::list<char>`.
|
||||
@@ -1,16 +0,0 @@
|
||||
[#rationale]
|
||||
= Rationale
|
||||
|
||||
:idprefix: rationale_
|
||||
|
||||
The rationale can be found in the original designfootnote:[issue 6.18 of the http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1837.pdf[Library Extension Technical Report Issues List] (page 63)].
|
||||
|
||||
== Quality of the hash function
|
||||
|
||||
Many hash functions strive to have little correlation between the input and output values. They attempt to uniformally distribute the output values for very similar inputs. This hash function makes no such attempt. In fact, for integers, the result of the hash function is often just the input value. So similar but different input values will often result in similar but different output values. This means that it is not appropriate as a general hash function. For example, a hash table may discard bits from the hash function resulting in likely collisions, or might have poor collision resolution when hash values are clustered together. In such cases this hash function will preform poorly.
|
||||
|
||||
But the standard has no such requirement for the hash function, it just requires that the hashes of two different values are unlikely to collide. Containers or algorithms designed to work with the standard hash function will have to be implemented to work well when the hash function's output is correlated to its input. Since they are paying that cost a higher quality hash function would be wasteful.
|
||||
|
||||
For other use cases, if you do need a higher quality hash function, then neither the standard hash function or `boost::hash` are appropriate. There are several options available. One is to use a second hash on the output of this hash function, such as http://web.archive.org/web/20121102023700/http://www.concentric.net/~Ttwang/tech/inthash.htm[Thomas Wang's hash function]. This this may not work as well as a hash algorithm tailored for the input.
|
||||
|
||||
For strings there are several fast, high quality hash functions available (for example http://code.google.com/p/smhasher/[MurmurHash3] and http://code.google.com/p/cityhash/[Google's CityHash]), although they tend to be more machine specific. These may also be appropriate for hashing a binary representation of your data - providing that all equal values have an equal representation, which is not always the case (e.g. for floating point values).
|
||||
60
doc/hash/recent.adoc
Normal file
60
doc/hash/recent.adoc
Normal file
@@ -0,0 +1,60 @@
|
||||
////
|
||||
Copyright 2022 Peter Dimov
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#recent]
|
||||
= Recent Changes
|
||||
:idprefix: recent_
|
||||
|
||||
== Boost 1.89.0
|
||||
|
||||
* Added the `hash_is_avalanching` trait class.
|
||||
|
||||
== Boost 1.84.0
|
||||
|
||||
* {cpp}03 is no longer supported.
|
||||
|
||||
== Boost 1.82.0
|
||||
|
||||
* Added an overload of `hash_value` for `std::nullptr_t`.
|
||||
* Added `is_tuple_like` and an overload of `hash_value` for
|
||||
tuple-like types.
|
||||
* Changed string hashing to use
|
||||
https://github.com/pdimov/mulxp_hash[`mulxp1_hash`],
|
||||
improving both quality and speed. This changes the hash values
|
||||
for string-like types (ranges of `char`, `signed char`,
|
||||
`unsigned char`, `std::byte`, `char8_t`).
|
||||
|
||||
== Boost 1.81.0
|
||||
|
||||
Major update.
|
||||
|
||||
* The specializations of `boost::hash` have been removed; it now
|
||||
always calls `hash_value`.
|
||||
* Support for `BOOST_HASH_NO_EXTENSIONS` has been removed. The
|
||||
extensions are always enabled.
|
||||
* All standard containers are now supported. This includes
|
||||
`std::forward_list` and the unordered associative containers.
|
||||
* User-defined containers (types that have `begin()` and `end()`
|
||||
member functions that return iterators) are now supported out
|
||||
of the box.
|
||||
* Described structs and classes (those annotated with
|
||||
`BOOST_DESCRIBE_STRUCT` or `BOOST_DESCRIBE_CLASS`) are now
|
||||
supported out of the box.
|
||||
* `hash_combine` has been improved. This changes the hash values
|
||||
of composite (container and tuple) types and of scalar types
|
||||
bigger than `size_t`.
|
||||
* The performance (and quality, as a result of the above change)
|
||||
of string hashing has been improved. `boost::hash` for strings
|
||||
now passes SMHasher in 64 bit mode.
|
||||
* The documentation has been substantially revised to reflect
|
||||
the changes.
|
||||
|
||||
== Boost 1.78.0
|
||||
|
||||
* Fixed `hash_combine` so that its behavior no longer depends
|
||||
on whether `size_t` is the exact same type as `boost::uint64_t`
|
||||
(which wasn't the case on macOS). This changes the hash values
|
||||
of composite (container and tuple) types on macOS.
|
||||
@@ -1,7 +1,7 @@
|
||||
////
|
||||
Copyright 2005-2008 Daniel James
|
||||
Copyright 2022 Christian Mazakas
|
||||
Copyright 2022 Peter Dimov
|
||||
Copyright 2022, 2025 Peter Dimov
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
@@ -29,6 +29,8 @@ namespace container_hash
|
||||
template<class T> struct is_range;
|
||||
template<class T> struct is_contiguous_range;
|
||||
template<class T> struct is_unordered_range;
|
||||
template<class T> struct is_described_class;
|
||||
template<class T> struct is_tuple_like;
|
||||
|
||||
} // namespace container_hash
|
||||
|
||||
@@ -42,6 +44,8 @@ template<class It> std::size_t hash_range( It first, It last );
|
||||
template<class It> void hash_unordered_range( std::size_t& seed, It first, It last );
|
||||
template<class It> std::size_t hash_unordered_range( It first, It last );
|
||||
|
||||
template<class Hash> struct hash_is_avalanching;
|
||||
|
||||
} // namespace boost
|
||||
----
|
||||
|
||||
@@ -88,8 +92,9 @@ template<class T>
|
||||
template<class A, class B>
|
||||
std::size_t hash_value( std::pair<A, B> const& v );
|
||||
|
||||
template<class... T>
|
||||
std::size_t hash_value( std::tuple<T...> const& v );
|
||||
// Enabled only when container_hash::is_tuple_like<T>::value is true
|
||||
template<class T>
|
||||
std::size_t hash_value( T const& v );
|
||||
|
||||
// Enabled only when container_hash::is_range<T>::value is true
|
||||
template<class T>
|
||||
@@ -103,6 +108,10 @@ template<class T>
|
||||
template<class T>
|
||||
std::size_t hash_value( T const& v );
|
||||
|
||||
// Enabled only when container_hash::is_described_class<T>::value is true
|
||||
template<class T>
|
||||
std::size_t hash_value( T const& v );
|
||||
|
||||
template<class T>
|
||||
std::size_t hash_value( std::shared_ptr<T> const& v );
|
||||
|
||||
@@ -142,9 +151,9 @@ template<class T> struct hash
|
||||
std::size_t operator()( T const& v ) const;
|
||||
----
|
||||
|
||||
Returns: :: `hash_value( v )`.
|
||||
Returns: :: `hash_value(v)`.
|
||||
|
||||
Throws: :: Only throws if `hash_value( v )` throws.
|
||||
Throws: :: Only throws if `hash_value(v)` throws.
|
||||
|
||||
Remarks: :: The call to `hash_value` is unqualified, so that user-supplied
|
||||
overloads will be found via argument dependent lookup.
|
||||
@@ -159,44 +168,59 @@ template<class T> void hash_combine( std::size_t& seed, T const& v );
|
||||
Called repeatedly to incrementally create a hash value from several variables.
|
||||
|
||||
Effects: :: Updates `seed` with a new hash value generated by
|
||||
deterministically combining it with the result of `boost::hash<T>()( v )`.
|
||||
deterministically combining it with the result of `boost::hash<T>()(v)`.
|
||||
|
||||
Throws: :: Only throws if `boost::hash<T>()( v )` throws. On exception,
|
||||
Throws: :: Only throws if `boost::hash<T>()(v)` throws. On exception,
|
||||
`seed` is not updated.
|
||||
|
||||
Remarks: ::
|
||||
+
|
||||
--
|
||||
Equivalent to `seed = combine( seed, boost::hash<T>()( v ) )`,
|
||||
Equivalent to `seed = combine(seed, boost::hash<T>()(v))`,
|
||||
where `combine(s, v)` is a mixing function that takes two arguments of
|
||||
type `std::size_t` and returns `std::size_t`, with the following desirable
|
||||
properties:
|
||||
|
||||
* For a constant `s`, when `v` takes all possible `size_t` values,
|
||||
. For a constant `s`, when `v` takes all possible `size_t` values,
|
||||
`combine(s, v)` should also take all possible `size_t` values, producing
|
||||
a sequence that is close to random; that is, it should be a random
|
||||
permutation.
|
||||
+
|
||||
This guarantees that for a given `seed`, `combine` does not introduce
|
||||
hash collisions when none were produced by `boost::hash<T>( v )`. It
|
||||
also implies that `combine(s, v)`, as a function of `v`, has good avalanche
|
||||
properties; that is, small (e.g. single bit) perturbations in the input `v`
|
||||
lead to large perturbations in the return value (half of the output bits
|
||||
changing, on average).
|
||||
hash collisions when none were produced by `boost::hash<T>(v)`; that is,
|
||||
it does not lose information from the input. It also implies that
|
||||
`combine(s, v)`, as a function of `v`, has good avalanche properties;
|
||||
that is, small (e.g. single bit) perturbations in the input `v` lead to
|
||||
large perturbations in the return value (half of the output bits changing,
|
||||
on average).
|
||||
|
||||
* For two different seeds `s1` and `s2`, `combine(s1, v)` and
|
||||
. For two different seeds `s1` and `s2`, `combine(s1, v)` and
|
||||
`combine(s2, v)`, treated as functions of `v`, should produce two
|
||||
different random permutations.
|
||||
|
||||
* `combine(0, 0)` should not be 0. Since a common initial value of `seed`
|
||||
. `combine(0, 0)` should not be 0. Since a common initial value of `seed`
|
||||
is zero, `combine(0, 0) == 0` would imply that applying `hash_combine` on
|
||||
any sequence of zeroes, regardless of length, will produce zero. This is
|
||||
undesirable, as it would lead to e.g. `std::vector<int>()` and
|
||||
`std::vector<int>(4)` to have the same hash value.
|
||||
|
||||
The current implementation uses the function `mix(s + 0x9e3779b9 + v)` as
|
||||
`combine(s, v)`, where `mix` is a high quality random permutation over the
|
||||
`std::size_t` values (with the property that `mix(0)` is 0).
|
||||
`combine(s, v)`, where `mix(x)` is a high quality mixing function that is a
|
||||
bijection over the `std::size_t` values, of the form
|
||||
|
||||
[source]
|
||||
----
|
||||
x ^= x >> k1;
|
||||
x *= m1;
|
||||
x ^= x >> k2;
|
||||
x *= m2;
|
||||
x ^= x >> k3;
|
||||
----
|
||||
|
||||
where the constants `k1`, `k2`, `k3`, `m1`, `m2` are suitably chosen.
|
||||
|
||||
Note that `mix(0)` is 0. This is why we add the arbitrary constant
|
||||
`0x9e3779b9` to meet the third requirement above.
|
||||
--
|
||||
|
||||
=== hash_range
|
||||
@@ -208,14 +232,28 @@ template<class It> void hash_range( std::size_t& seed, It first, It last );
|
||||
|
||||
Effects: ::
|
||||
+
|
||||
--
|
||||
When `typename std::iterator_traits<It>::value_type` is not `char`, `signed char`,
|
||||
`unsigned char`, `std::byte`, or `char8_t`,
|
||||
|
||||
[source]
|
||||
----
|
||||
for( ; first != last; ++first )
|
||||
{
|
||||
hash_combine<typename std::iterator_traits<It>::value_type>( seed, *first );
|
||||
boost::hash_combine<typename std::iterator_traits<It>::value_type>( seed, *first );
|
||||
}
|
||||
----
|
||||
|
||||
Otherwise, bytes from `[first, last)` are coalesced and hashed in an
|
||||
unspecified manner. This is done in order to improve performance when hashing
|
||||
strings.
|
||||
--
|
||||
|
||||
Remarks: ::
|
||||
For chars, the current implementation uses
|
||||
https://github.com/pdimov/mulxp_hash[`mulxp1_hash`] when `std::size_t` is
|
||||
64 bit, and `mulxp1_hash32` when it's 32 bit.
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class It> std::size_t hash_range( It first, It last );
|
||||
@@ -226,7 +264,7 @@ Effects: ::
|
||||
[source]
|
||||
----
|
||||
size_t seed = 0;
|
||||
hash_range( seed, first, last );
|
||||
boost::hash_range( seed, first, last );
|
||||
return seed;
|
||||
----
|
||||
|
||||
@@ -238,7 +276,7 @@ template<class It> void hash_unordered_range( std::size_t& seed, It first, It la
|
||||
----
|
||||
|
||||
Effects: :: Updates `seed` with the values of
|
||||
`boost::hash<typename std::iterator_traits<It>::value_type>()( *i )`
|
||||
`boost::hash<typename std::iterator_traits<It>::value_type>()(*i)`
|
||||
for each `i` in `[first, last)`, such that the order of elements does
|
||||
not affect the final result.
|
||||
|
||||
@@ -252,7 +290,7 @@ Effects: ::
|
||||
[source]
|
||||
----
|
||||
size_t seed = 0;
|
||||
hash_unordered_range( seed, first, last );
|
||||
boost::hash_unordered_range( seed, first, last );
|
||||
return seed;
|
||||
----
|
||||
|
||||
@@ -282,7 +320,7 @@ Returns: ::
|
||||
`static_cast<std::size_t>(v)`.
|
||||
|
||||
Remarks: ::
|
||||
`hash_value( std::to_underlying(v) )` would be better, but {cpp}03
|
||||
`hash_value(std::to_underlying(v))` would be better, but {cpp}03
|
||||
compatibility mandates the current implementation.
|
||||
|
||||
[source]
|
||||
@@ -315,7 +353,7 @@ template<class T, std::size_t N>
|
||||
----
|
||||
|
||||
Returns: ::
|
||||
`hash_range( v, v + N )`.
|
||||
`boost::hash_range( v, v + N )`.
|
||||
|
||||
[source]
|
||||
----
|
||||
@@ -356,8 +394,9 @@ return seed;
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class... T>
|
||||
std::size_t hash_value( std::tuple<T...> const& v );
|
||||
// Enabled only when container_hash::is_tuple_like<T>::value is true
|
||||
template<class T>
|
||||
std::size_t hash_value( T const& v );
|
||||
----
|
||||
|
||||
Effects: ::
|
||||
@@ -366,15 +405,21 @@ Effects: ::
|
||||
----
|
||||
std::size_t seed = 0;
|
||||
|
||||
boost::hash_combine( seed, std::get<0>(v) );
|
||||
boost::hash_combine( seed, std::get<1>(v) );
|
||||
using std::get;
|
||||
|
||||
boost::hash_combine( seed, get<0>(v) );
|
||||
boost::hash_combine( seed, get<1>(v) );
|
||||
// ...
|
||||
boost::hash_combine( seed, std::get<N-1>(v) );
|
||||
boost::hash_combine( seed, get<N-1>(v) );
|
||||
|
||||
return seed;
|
||||
----
|
||||
+
|
||||
where `N` is `sizeof...(T)`.
|
||||
where `N` is `std::tuple_size<T>::value`.
|
||||
|
||||
Remarks: ::
|
||||
This overload is only enabled when
|
||||
`container_hash::is_range<T>::value` is `false`.
|
||||
|
||||
[source]
|
||||
----
|
||||
@@ -384,7 +429,7 @@ template<class T>
|
||||
----
|
||||
|
||||
Returns: ::
|
||||
`hash_range( v.begin(), v.end() )`.
|
||||
`boost::hash_range( v.begin(), v.end() )`.
|
||||
|
||||
Remarks: ::
|
||||
This overload is only enabled when
|
||||
@@ -402,7 +447,7 @@ template<class T>
|
||||
----
|
||||
|
||||
Returns: ::
|
||||
`hash_range( v.data(), v.data() + v.size() )`.
|
||||
`boost::hash_range( v.data(), v.data() + v.size() )`.
|
||||
|
||||
Remarks: ::
|
||||
This overload handles all standard contiguous containers, such as
|
||||
@@ -416,12 +461,40 @@ template<class T>
|
||||
----
|
||||
|
||||
Returns: ::
|
||||
`hash_unordered_range( v.begin(), v.end() )`.
|
||||
`boost::hash_unordered_range( v.begin(), v.end() )`.
|
||||
|
||||
Remarks: ::
|
||||
This overload handles the standard unordered containers, such as
|
||||
`std::unordered_set` and `std::unordered_map`.
|
||||
|
||||
[source]
|
||||
----
|
||||
// Enabled only when container_hash::is_described_class<T>::value is true
|
||||
template<class T>
|
||||
std::size_t hash_value( T const& v );
|
||||
----
|
||||
|
||||
Effects: ::
|
||||
+
|
||||
[source]
|
||||
----
|
||||
std::size_t seed = 0;
|
||||
|
||||
boost::hash_combine( seed, b1 );
|
||||
boost::hash_combine( seed, b2 );
|
||||
// ...
|
||||
boost::hash_combine( seed, bM );
|
||||
|
||||
boost::hash_combine( seed, m1 );
|
||||
boost::hash_combine( seed, m2 );
|
||||
// ...
|
||||
boost::hash_combine( seed, mN );
|
||||
|
||||
return seed;
|
||||
----
|
||||
+
|
||||
where `bi` are the bases of `v` and `mi` are its members.
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class T>
|
||||
@@ -432,7 +505,7 @@ template<class T, class D>
|
||||
----
|
||||
|
||||
Returns: ::
|
||||
`hash<T*>( v.get() )`.
|
||||
`boost::hash<T*>( v.get() )`.
|
||||
|
||||
[source]
|
||||
----
|
||||
@@ -468,7 +541,7 @@ template<class T>
|
||||
|
||||
Returns: ::
|
||||
For a disengaged `v`, an unspecified constant value; otherwise,
|
||||
`hash<T>()( *v )`.
|
||||
`boost::hash<T>()( *v )`.
|
||||
|
||||
[source]
|
||||
----
|
||||
@@ -501,6 +574,56 @@ where `x` is the currently contained value in `v`.
|
||||
Throws: ::
|
||||
`std::bad_variant_access` when `v.valueless_by_exception()` is `true`.
|
||||
|
||||
== <boost/container_hash/{zwsp}hash_is_avalanching.hpp>
|
||||
|
||||
Defines the trait `boost::hash_is_avalanching`.
|
||||
|
||||
[source]
|
||||
----
|
||||
namespace boost
|
||||
{
|
||||
|
||||
template<class Hash> struct hash_is_avalanching;
|
||||
|
||||
} // namespace boost
|
||||
----
|
||||
|
||||
=== hash_is_avalanching<Hash>
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class Hash> struct hash_is_avalanching
|
||||
{
|
||||
static constexpr bool value = /* see below */;
|
||||
};
|
||||
----
|
||||
|
||||
`hash_is_avalanching<Hash>::value` is:
|
||||
|
||||
* `false` if `Hash::is_avalanching` is not present,
|
||||
* `Hash::is_avalanching::value` if this is present and convertible at compile time to a `bool`,
|
||||
* `true` if `Hash::is_avalanching` is `void` (this usage is deprecated),
|
||||
* ill-formed otherwise.
|
||||
|
||||
A hash function is said to have the _avalanching property_ if small changes
|
||||
in the input translate to large changes in the returned hash code
|
||||
—ideally, flipping one bit in the representation of the input value results
|
||||
in each bit of the hash code flipping with probability 50%. Libraries
|
||||
such as link:../../../unordered/index.html[Boost.Unordered] consult this trait
|
||||
to determine if the supplied hash function is of high quality.
|
||||
`boost::hash` for `std::basic_string<Ch>` and `std::basic_string_view<Ch>`
|
||||
has this trait set to `true` when `Ch` is an integral type (this includes
|
||||
`std::string` and `std::string_view`, among others).
|
||||
Users can set this trait for a particular `Hash` type by:
|
||||
|
||||
* Inserting the nested `is_avalanching` typedef in the class definition
|
||||
if they have access to its source code.
|
||||
* Writing a specialization of `boost::hash_is_avalanching`
|
||||
for `Hash`.
|
||||
|
||||
Note that usage of this trait is not restricted to hash functions produced
|
||||
with Boost.ContainerHash.
|
||||
|
||||
== <boost/container_hash/{zwsp}is_range.hpp>
|
||||
|
||||
Defines the trait `boost::container_hash::is_range`.
|
||||
@@ -609,3 +732,75 @@ template<class T> struct is_unordered_range
|
||||
|
||||
Users are allowed to specialize `is_unordered_range` for their types
|
||||
if the default behavior does not deduce the correct value.
|
||||
|
||||
== <boost/container_hash/{zwsp}is_described_class.hpp>
|
||||
|
||||
Defines the trait `boost::container_hash::is_described_class`.
|
||||
|
||||
[source]
|
||||
----
|
||||
namespace boost
|
||||
{
|
||||
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<class T> struct is_described_class;
|
||||
|
||||
} // namespace container_hash
|
||||
|
||||
} // namespace boost
|
||||
----
|
||||
|
||||
=== is_described_class<T>
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class T> struct is_described_class
|
||||
{
|
||||
static constexpr bool value = /* see below */;
|
||||
};
|
||||
----
|
||||
|
||||
`is_described_class<T>::value` is `true` when
|
||||
`boost::describe::has_describe_bases<T>::value` is `true`,
|
||||
`boost::describe::has_describe_members<T>::value` is `true`, and
|
||||
`T` is not a union.
|
||||
|
||||
Users are allowed to specialize `is_described_class` for their types
|
||||
if the default behavior does not deduce the correct value.
|
||||
|
||||
== <boost/container_hash/{zwsp}is_tuple_like.hpp>
|
||||
|
||||
Defines the trait `boost::container_hash::is_tuple_like`.
|
||||
|
||||
[source]
|
||||
----
|
||||
namespace boost
|
||||
{
|
||||
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<class T> struct is_tuple_like;
|
||||
|
||||
} // namespace container_hash
|
||||
|
||||
} // namespace boost
|
||||
----
|
||||
|
||||
=== is_tuple_like<T>
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class T> struct is_tuple_like
|
||||
{
|
||||
static constexpr bool value = /* see below */;
|
||||
};
|
||||
----
|
||||
|
||||
`is_tuple_like<T>::value` is `true` when `std::tuple_size<T>::value`
|
||||
is valid.
|
||||
|
||||
Users are allowed to specialize `is_tuple_like` for their types
|
||||
if the default behavior does not deduce the correct value.
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
////
|
||||
Copyright 2005-2008 Daniel James
|
||||
Copyright 2022 Christian Mazakas
|
||||
Copyright 2022 Peter Dimov
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#thanks]
|
||||
= Acknowledgements
|
||||
|
||||
:idprefix: thanks_
|
||||
|
||||
This library is based on the design by Peter Dimov. During the initial development Joaquín M López Muñoz made many useful suggestions and contributed fixes.
|
||||
@@ -12,3 +19,5 @@ The implementation of the hash function for pointers is based on suggestions mad
|
||||
Some useful improvements to the floating point hash algorithm were suggested by Daniel Krügler.
|
||||
|
||||
The original implementation came from Jeremy B. Maitin-Shepard's hash table library, although this is a complete rewrite.
|
||||
|
||||
The documentation was converted from Quickbook to AsciiDoc by Christian Mazakas.
|
||||
|
||||
@@ -74,7 +74,7 @@ assert(books.find(knife) != books.end());
|
||||
assert(books.find(dandelion) == books.end());
|
||||
----
|
||||
|
||||
The full example can be found in:
|
||||
The full example can be found in
|
||||
link:../../examples/books.hpp[examples/books.hpp] and
|
||||
link:../../examples/books.cpp[examples/books.cpp].
|
||||
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
# subject to 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)
|
||||
|
||||
import testing ;
|
||||
|
||||
project : requirements <library>/boost/container_hash//boost_container_hash ;
|
||||
|
||||
run books.cpp ;
|
||||
run point.cpp ;
|
||||
run portable.cpp ;
|
||||
run template.cpp : : : <toolset>msvc-8.0:<build>no ;
|
||||
run template.cpp /boost/unordered//boost_unordered : : : <toolset>msvc-8.0:<build>no ;
|
||||
run point2.cpp ;
|
||||
|
||||
65
examples/point2.cpp
Normal file
65
examples/point2.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
// Copyright 2005 Daniel James.
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
// Force use of assert.
|
||||
#if defined(NDEBUG)
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/describe/class.hpp>
|
||||
#include <boost/describe/operators.hpp>
|
||||
#include <cassert>
|
||||
|
||||
// This example illustrates how to use Boost.Describe to obtain
|
||||
// automatic boost::hash support. For full details see the hash
|
||||
// tutorial.
|
||||
|
||||
#if defined(BOOST_DESCRIBE_CXX14)
|
||||
|
||||
class point
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
|
||||
BOOST_DESCRIBE_CLASS(point, (), (), (), (x, y))
|
||||
|
||||
public:
|
||||
|
||||
point() : x(0), y(0) {}
|
||||
point(int x, int y) : x(x), y(y) {}
|
||||
};
|
||||
|
||||
using boost::describe::operators::operator==;
|
||||
using boost::describe::operators::operator!=;
|
||||
|
||||
int main()
|
||||
{
|
||||
boost::hash<point> point_hasher;
|
||||
|
||||
point p1(0, 0);
|
||||
point p2(1, 2);
|
||||
point p3(4, 1);
|
||||
point p4 = p1;
|
||||
|
||||
assert(point_hasher(p1) == point_hasher(p4));
|
||||
|
||||
// These tests could legally fail, but if they did it'd be a pretty bad
|
||||
// hash function.
|
||||
assert(point_hasher(p1) != point_hasher(p2));
|
||||
assert(point_hasher(p1) != point_hasher(p3));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::puts( "This example requires C++14." );
|
||||
}
|
||||
|
||||
#endif
|
||||
146
include/boost/container_hash/detail/hash_integral.hpp
Normal file
146
include/boost/container_hash/detail/hash_integral.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright 2021-2023 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_HASH_DETAIL_HASH_INTEGRAL_HPP
|
||||
#define BOOST_HASH_DETAIL_HASH_INTEGRAL_HPP
|
||||
|
||||
#include <boost/container_hash/detail/hash_mix.hpp>
|
||||
#include <type_traits>
|
||||
#include <cstddef>
|
||||
#include <climits>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
// libstdc++ doesn't provide support for __int128 in the standard traits
|
||||
|
||||
template<class T> struct is_integral: public std::is_integral<T>
|
||||
{
|
||||
};
|
||||
|
||||
template<class T> struct is_unsigned: public std::is_unsigned<T>
|
||||
{
|
||||
};
|
||||
|
||||
template<class T> struct make_unsigned: public std::make_unsigned<T>
|
||||
{
|
||||
};
|
||||
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
|
||||
template<> struct is_integral<__int128_t>: public std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<> struct is_integral<__uint128_t>: public std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<> struct is_unsigned<__int128_t>: public std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<> struct is_unsigned<__uint128_t>: public std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<> struct make_unsigned<__int128_t>
|
||||
{
|
||||
typedef __uint128_t type;
|
||||
};
|
||||
|
||||
template<> struct make_unsigned<__uint128_t>
|
||||
{
|
||||
typedef __uint128_t type;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
template<class T,
|
||||
bool bigger_than_size_t = (sizeof(T) > sizeof(std::size_t)),
|
||||
bool is_unsigned = is_unsigned<T>::value,
|
||||
std::size_t size_t_bits = sizeof(std::size_t) * CHAR_BIT,
|
||||
std::size_t type_bits = sizeof(T) * CHAR_BIT>
|
||||
struct hash_integral_impl;
|
||||
|
||||
template<class T, bool is_unsigned, std::size_t size_t_bits, std::size_t type_bits> struct hash_integral_impl<T, false, is_unsigned, size_t_bits, type_bits>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
return static_cast<std::size_t>( v );
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, std::size_t size_t_bits, std::size_t type_bits> struct hash_integral_impl<T, true, false, size_t_bits, type_bits>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
typedef typename make_unsigned<T>::type U;
|
||||
|
||||
if( v >= 0 )
|
||||
{
|
||||
return hash_integral_impl<U>::fn( static_cast<U>( v ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return ~hash_integral_impl<U>::fn( static_cast<U>( ~static_cast<U>( v ) ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct hash_integral_impl<T, true, true, 32, 64>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( v >> 32 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v & 0xFFFFFFFF ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct hash_integral_impl<T, true, true, 32, 128>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( v >> 96 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v >> 64 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v >> 32 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct hash_integral_impl<T, true, true, 64, 128>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( v >> 64 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<hash_detail::is_integral<T>::value, std::size_t>::type
|
||||
hash_value( T v )
|
||||
{
|
||||
return hash_detail::hash_integral_impl<T>::fn( v );
|
||||
}
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_HASH_DETAIL_HASH_INTEGRAL_HPP
|
||||
@@ -5,7 +5,7 @@
|
||||
#ifndef BOOST_HASH_DETAIL_HASH_MIX_HPP
|
||||
#define BOOST_HASH_DETAIL_HASH_MIX_HPP
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <climits>
|
||||
|
||||
@@ -66,9 +66,9 @@ template<std::size_t Bits> struct hash_mix_impl;
|
||||
|
||||
template<> struct hash_mix_impl<64>
|
||||
{
|
||||
inline static boost::uint64_t fn( boost::uint64_t x )
|
||||
inline static std::uint64_t fn( std::uint64_t x )
|
||||
{
|
||||
boost::uint64_t const m = (boost::uint64_t(0xe9846af) << 32) + 0x9b1a615d;
|
||||
std::uint64_t const m = 0xe9846af9b1a615d;
|
||||
|
||||
x ^= x >> 32;
|
||||
x *= m;
|
||||
@@ -87,10 +87,10 @@ template<> struct hash_mix_impl<64>
|
||||
|
||||
template<> struct hash_mix_impl<32>
|
||||
{
|
||||
inline static boost::uint32_t fn( boost::uint32_t x )
|
||||
inline static std::uint32_t fn( std::uint32_t x )
|
||||
{
|
||||
boost::uint32_t const m1 = 0x21f0aaad;
|
||||
boost::uint32_t const m2 = 0x735a2d97;
|
||||
std::uint32_t const m1 = 0x21f0aaad;
|
||||
std::uint32_t const m2 = 0x735a2d97;
|
||||
|
||||
x ^= x >> 16;
|
||||
x *= m1;
|
||||
|
||||
@@ -6,31 +6,42 @@
|
||||
#define BOOST_HASH_DETAIL_HASH_RANGE_HPP
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/type_traits/enable_if.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/container_hash/detail/mulx.hpp>
|
||||
#include <type_traits>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <cstddef>
|
||||
#include <climits>
|
||||
#include <iterator>
|
||||
#include <cstring>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
template<class T> struct is_char_type: public boost::false_type {};
|
||||
template<class T> struct is_char_type: public std::false_type {};
|
||||
|
||||
#if CHAR_BIT == 8
|
||||
|
||||
template<> struct is_char_type<char>: public boost::true_type {};
|
||||
template<> struct is_char_type<signed char>: public boost::true_type {};
|
||||
template<> struct is_char_type<unsigned char>: public boost::true_type {};
|
||||
template<> struct is_char_type<char>: public std::true_type {};
|
||||
template<> struct is_char_type<signed char>: public std::true_type {};
|
||||
template<> struct is_char_type<unsigned char>: public std::true_type {};
|
||||
|
||||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||
template<> struct is_char_type<char8_t>: public std::true_type {};
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||
template<> struct is_char_type<std::byte>: public std::true_type {};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// generic version
|
||||
|
||||
template<class It>
|
||||
inline typename boost::enable_if_<
|
||||
inline typename std::enable_if<
|
||||
!is_char_type<typename std::iterator_traits<It>::value_type>::value,
|
||||
std::size_t >::type
|
||||
hash_range( std::size_t seed, It first, It last )
|
||||
@@ -43,120 +54,352 @@ std::size_t >::type
|
||||
return seed;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
inline typename boost::enable_if_<
|
||||
is_char_type<typename std::iterator_traits<It>::value_type>::value &&
|
||||
is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value,
|
||||
std::size_t>::type
|
||||
hash_range( std::size_t seed, It first, It last )
|
||||
// specialized char[] version, 32 bit
|
||||
|
||||
template<class It> inline std::uint32_t read32le( It p )
|
||||
{
|
||||
std::size_t n = static_cast<std::size_t>( last - first );
|
||||
// clang 5+, gcc 5+ figure out this pattern and use a single mov on x86
|
||||
// gcc on s390x and power BE even knows how to use load-reverse
|
||||
|
||||
for( ; n >= 4; first += 4, n -= 4 )
|
||||
{
|
||||
// clang 5+, gcc 5+ figure out this pattern and use a single mov on x86
|
||||
// gcc on s390x and power BE even knows how to use load-reverse
|
||||
std::uint32_t w =
|
||||
static_cast<std::uint32_t>( static_cast<unsigned char>( p[0] ) ) |
|
||||
static_cast<std::uint32_t>( static_cast<unsigned char>( p[1] ) ) << 8 |
|
||||
static_cast<std::uint32_t>( static_cast<unsigned char>( p[2] ) ) << 16 |
|
||||
static_cast<std::uint32_t>( static_cast<unsigned char>( p[3] ) ) << 24;
|
||||
|
||||
boost::uint32_t w =
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[0] ) ) |
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[1] ) ) << 8 |
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[2] ) ) << 16 |
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[3] ) ) << 24;
|
||||
return w;
|
||||
}
|
||||
|
||||
hash_combine( seed, w );
|
||||
}
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
|
||||
{
|
||||
// add a trailing suffix byte of 0x01 because otherwise sequences of
|
||||
// trailing zeroes are indistinguishable from end of string
|
||||
template<class T> inline std::uint32_t read32le( T* p )
|
||||
{
|
||||
std::uint32_t w;
|
||||
|
||||
boost::uint32_t w = 0x01u;
|
||||
std::memcpy( &w, p, 4 );
|
||||
return w;
|
||||
}
|
||||
|
||||
switch( n )
|
||||
{
|
||||
case 1:
|
||||
#endif
|
||||
|
||||
w =
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[0] ) ) |
|
||||
0x0100u;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
w =
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[0] ) ) |
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[1] ) ) << 8 |
|
||||
0x010000u;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
|
||||
w =
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[0] ) ) |
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[1] ) ) << 8 |
|
||||
static_cast<boost::uint32_t>( static_cast<unsigned char>( first[2] ) ) << 16 |
|
||||
0x01000000u;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
hash_combine( seed, w );
|
||||
}
|
||||
|
||||
return seed;
|
||||
inline std::uint64_t mul32( std::uint32_t x, std::uint32_t y )
|
||||
{
|
||||
return static_cast<std::uint64_t>( x ) * y;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
inline typename boost::enable_if_<
|
||||
inline typename std::enable_if<
|
||||
is_char_type<typename std::iterator_traits<It>::value_type>::value &&
|
||||
!is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value,
|
||||
std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
|
||||
std::numeric_limits<std::size_t>::digits <= 32,
|
||||
std::size_t>::type
|
||||
hash_range( std::size_t seed, It first, It last )
|
||||
{
|
||||
It p = first;
|
||||
std::size_t n = static_cast<std::size_t>( last - first );
|
||||
|
||||
std::uint32_t const q = 0x9e3779b9U;
|
||||
std::uint32_t const k = 0xe35e67b1U; // q * q
|
||||
|
||||
std::uint64_t h = mul32( static_cast<std::uint32_t>( seed ) + q, k );
|
||||
std::uint32_t w = static_cast<std::uint32_t>( h & 0xFFFFFFFF );
|
||||
|
||||
h ^= n;
|
||||
|
||||
while( n >= 4 )
|
||||
{
|
||||
std::uint32_t v1 = read32le( p );
|
||||
|
||||
w += q;
|
||||
h ^= mul32( v1 + w, k );
|
||||
|
||||
p += 4;
|
||||
n -= 4;
|
||||
}
|
||||
|
||||
{
|
||||
std::uint32_t v1 = 0;
|
||||
|
||||
if( n >= 1 )
|
||||
{
|
||||
std::size_t const x1 = ( n - 1 ) & 2; // 1: 0, 2: 0, 3: 2
|
||||
std::size_t const x2 = n >> 1; // 1: 0, 2: 1, 3: 1
|
||||
|
||||
v1 =
|
||||
static_cast<std::uint32_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x1 ) ] ) ) << x1 * 8 |
|
||||
static_cast<std::uint32_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x2 ) ] ) ) << x2 * 8 |
|
||||
static_cast<std::uint32_t>( static_cast<unsigned char>( p[ 0 ] ) );
|
||||
}
|
||||
|
||||
w += q;
|
||||
h ^= mul32( v1 + w, k );
|
||||
}
|
||||
|
||||
w += q;
|
||||
h ^= mul32( static_cast<std::uint32_t>( h & 0xFFFFFFFF ) + w, static_cast<std::uint32_t>( h >> 32 ) + w + k );
|
||||
|
||||
return static_cast<std::uint32_t>( h & 0xFFFFFFFF ) ^ static_cast<std::uint32_t>( h >> 32 );
|
||||
}
|
||||
|
||||
template<class It>
|
||||
inline typename std::enable_if<
|
||||
is_char_type<typename std::iterator_traits<It>::value_type>::value &&
|
||||
!std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
|
||||
std::numeric_limits<std::size_t>::digits <= 32,
|
||||
std::size_t>::type
|
||||
hash_range( std::size_t seed, It first, It last )
|
||||
{
|
||||
std::size_t n = 0;
|
||||
|
||||
std::uint32_t const q = 0x9e3779b9U;
|
||||
std::uint32_t const k = 0xe35e67b1U; // q * q
|
||||
|
||||
std::uint64_t h = mul32( static_cast<std::uint32_t>( seed ) + q, k );
|
||||
std::uint32_t w = static_cast<std::uint32_t>( h & 0xFFFFFFFF );
|
||||
|
||||
std::uint32_t v1 = 0;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
boost::uint32_t w = 0;
|
||||
v1 = 0;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
hash_combine( seed, w | 0x01u );
|
||||
return seed;
|
||||
break;
|
||||
}
|
||||
|
||||
w |= static_cast<boost::uint32_t>( static_cast<unsigned char>( *first ) );
|
||||
v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) );
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
hash_combine( seed, w | 0x0100u );
|
||||
return seed;
|
||||
break;
|
||||
}
|
||||
|
||||
w |= static_cast<boost::uint32_t>( static_cast<unsigned char>( *first ) ) << 8;
|
||||
v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 8;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
hash_combine( seed, w | 0x010000u );
|
||||
return seed;
|
||||
break;
|
||||
}
|
||||
|
||||
w |= static_cast<boost::uint32_t>( static_cast<unsigned char>( *first ) ) << 16;
|
||||
v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 16;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
hash_combine( seed, w | 0x01000000u );
|
||||
return seed;
|
||||
break;
|
||||
}
|
||||
|
||||
w |= static_cast<boost::uint32_t>( static_cast<unsigned char>( *first ) ) << 24;
|
||||
v1 |= static_cast<std::uint32_t>( static_cast<unsigned char>( *first ) ) << 24;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
hash_combine( seed, w );
|
||||
w += q;
|
||||
h ^= mul32( v1 + w, k );
|
||||
}
|
||||
|
||||
h ^= n;
|
||||
|
||||
w += q;
|
||||
h ^= mul32( v1 + w, k );
|
||||
|
||||
w += q;
|
||||
h ^= mul32( static_cast<std::uint32_t>( h & 0xFFFFFFFF ) + w, static_cast<std::uint32_t>( h >> 32 ) + w + k );
|
||||
|
||||
return static_cast<std::uint32_t>( h & 0xFFFFFFFF ) ^ static_cast<std::uint32_t>( h >> 32 );
|
||||
}
|
||||
|
||||
// specialized char[] version, 64 bit
|
||||
|
||||
template<class It> inline std::uint64_t read64le( It p )
|
||||
{
|
||||
std::uint64_t w =
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[0] ) ) |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[1] ) ) << 8 |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[2] ) ) << 16 |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[3] ) ) << 24 |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[4] ) ) << 32 |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[5] ) ) << 40 |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[6] ) ) << 48 |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[7] ) ) << 56;
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
|
||||
template<class T> inline std::uint64_t read64le( T* p )
|
||||
{
|
||||
std::uint64_t w;
|
||||
|
||||
std::memcpy( &w, p, 8 );
|
||||
return w;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template<class It>
|
||||
inline typename std::enable_if<
|
||||
is_char_type<typename std::iterator_traits<It>::value_type>::value &&
|
||||
std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
|
||||
(std::numeric_limits<std::size_t>::digits > 32),
|
||||
std::size_t>::type
|
||||
hash_range( std::size_t seed, It first, It last )
|
||||
{
|
||||
It p = first;
|
||||
std::size_t n = static_cast<std::size_t>( last - first );
|
||||
|
||||
std::uint64_t const q = 0x9e3779b97f4a7c15;
|
||||
std::uint64_t const k = 0xdf442d22ce4859b9; // q * q
|
||||
|
||||
std::uint64_t w = mulx( seed + q, k );
|
||||
std::uint64_t h = w ^ n;
|
||||
|
||||
while( n >= 8 )
|
||||
{
|
||||
std::uint64_t v1 = read64le( p );
|
||||
|
||||
w += q;
|
||||
h ^= mulx( v1 + w, k );
|
||||
|
||||
p += 8;
|
||||
n -= 8;
|
||||
}
|
||||
|
||||
{
|
||||
std::uint64_t v1 = 0;
|
||||
|
||||
if( n >= 4 )
|
||||
{
|
||||
v1 = static_cast<std::uint64_t>( read32le( p + static_cast<std::ptrdiff_t>( n - 4 ) ) ) << ( n - 4 ) * 8 | read32le( p );
|
||||
}
|
||||
else if( n >= 1 )
|
||||
{
|
||||
std::size_t const x1 = ( n - 1 ) & 2; // 1: 0, 2: 0, 3: 2
|
||||
std::size_t const x2 = n >> 1; // 1: 0, 2: 1, 3: 1
|
||||
|
||||
v1 =
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x1 ) ] ) ) << x1 * 8 |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[ static_cast<std::ptrdiff_t>( x2 ) ] ) ) << x2 * 8 |
|
||||
static_cast<std::uint64_t>( static_cast<unsigned char>( p[ 0 ] ) );
|
||||
}
|
||||
|
||||
w += q;
|
||||
h ^= mulx( v1 + w, k );
|
||||
}
|
||||
|
||||
return mulx( h + w, k );
|
||||
}
|
||||
|
||||
template<class It>
|
||||
inline typename std::enable_if<
|
||||
is_char_type<typename std::iterator_traits<It>::value_type>::value &&
|
||||
!std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value &&
|
||||
(std::numeric_limits<std::size_t>::digits > 32),
|
||||
std::size_t>::type
|
||||
hash_range( std::size_t seed, It first, It last )
|
||||
{
|
||||
std::size_t n = 0;
|
||||
|
||||
std::uint64_t const q = 0x9e3779b97f4a7c15;
|
||||
std::uint64_t const k = 0xdf442d22ce4859b9; // q * q
|
||||
|
||||
std::uint64_t w = mulx( seed + q, k );
|
||||
std::uint64_t h = w;
|
||||
|
||||
std::uint64_t v1 = 0;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
v1 = 0;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) );
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 8;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 16;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 24;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 32;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 40;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 48;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
if( first == last )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
v1 |= static_cast<std::uint64_t>( static_cast<unsigned char>( *first ) ) << 56;
|
||||
++first;
|
||||
++n;
|
||||
|
||||
w += q;
|
||||
h ^= mulx( v1 + w, k );
|
||||
}
|
||||
|
||||
h ^= n;
|
||||
|
||||
w += q;
|
||||
h ^= mulx( v1 + w, k );
|
||||
|
||||
return mulx( h + w, k );
|
||||
}
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
// Copyright 2005-2009 Daniel James.
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
|
||||
#define BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
#include <boost/type_traits/enable_if.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
|
||||
// no support
|
||||
|
||||
#else
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
inline typename boost::enable_if_<(I == std::tuple_size<T>::value),
|
||||
void>::type
|
||||
hash_combine_tuple(std::size_t&, T const&)
|
||||
{
|
||||
}
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
inline typename boost::enable_if_<(I < std::tuple_size<T>::value),
|
||||
void>::type
|
||||
hash_combine_tuple(std::size_t& seed, T const& v)
|
||||
{
|
||||
boost::hash_combine(seed, std::get<I>(v));
|
||||
boost::hash_detail::hash_combine_tuple<I + 1>(seed, v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::size_t hash_tuple(T const& v)
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_detail::hash_combine_tuple<0>(seed, v);
|
||||
return seed;
|
||||
}
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
|
||||
template <typename... T>
|
||||
inline std::size_t hash_value(std::tuple<T...> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline std::size_t hash_value(std::tuple<> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0>
|
||||
inline std::size_t hash_value(std::tuple<A0> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1, typename A2>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1, A2> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1, typename A2, typename A3>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1, A2, A3> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1, typename A2, typename A3, typename A4>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1, A2, A3, A4> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5, A6> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, A8> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
|
||||
inline std::size_t hash_value(std::tuple<A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> const& v)
|
||||
{
|
||||
return boost::hash_detail::hash_tuple(v);
|
||||
}
|
||||
|
||||
#endif // #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // #if defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
|
||||
#endif // #ifndef BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
|
||||
62
include/boost/container_hash/detail/hash_tuple_like.hpp
Normal file
62
include/boost/container_hash/detail/hash_tuple_like.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2005-2009 Daniel James.
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
|
||||
#define BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
#include <boost/container_hash/is_tuple_like.hpp>
|
||||
#include <boost/container_hash/is_range.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
inline
|
||||
typename std::enable_if<(I == std::tuple_size<T>::value), void>::type
|
||||
hash_combine_tuple_like( std::size_t&, T const& )
|
||||
{
|
||||
}
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
inline
|
||||
typename std::enable_if<(I < std::tuple_size<T>::value), void>::type
|
||||
hash_combine_tuple_like( std::size_t& seed, T const& v )
|
||||
{
|
||||
using std::get;
|
||||
boost::hash_combine( seed, get<I>( v ) );
|
||||
|
||||
boost::hash_detail::hash_combine_tuple_like<I + 1>( seed, v );
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::size_t hash_tuple_like( T const& v )
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
boost::hash_detail::hash_combine_tuple_like<0>( seed, v );
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
typename std::enable_if<
|
||||
container_hash::is_tuple_like<T>::value && !container_hash::is_range<T>::value,
|
||||
std::size_t>::type
|
||||
hash_value( T const& v )
|
||||
{
|
||||
return boost::hash_detail::hash_tuple_like( v );
|
||||
}
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_HASH_DETAIL_HASH_TUPLE_LIKE_HPP
|
||||
79
include/boost/container_hash/detail/mulx.hpp
Normal file
79
include/boost/container_hash/detail/mulx.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2022, 2023 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_HASH_DETAIL_MULX_HPP
|
||||
#define BOOST_HASH_DETAIL_MULX_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#if defined(_MSC_VER)
|
||||
# include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_X64) && !defined(__clang__)
|
||||
|
||||
__forceinline std::uint64_t mulx( std::uint64_t x, std::uint64_t y )
|
||||
{
|
||||
std::uint64_t r2;
|
||||
std::uint64_t r = _umul128( x, y, &r2 );
|
||||
return r ^ r2;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
|
||||
|
||||
__forceinline std::uint64_t mulx( std::uint64_t x, std::uint64_t y )
|
||||
{
|
||||
std::uint64_t r = x * y;
|
||||
std::uint64_t r2 = __umulh( x, y );
|
||||
return r ^ r2;
|
||||
}
|
||||
|
||||
#elif defined(__SIZEOF_INT128__)
|
||||
|
||||
inline std::uint64_t mulx( std::uint64_t x, std::uint64_t y )
|
||||
{
|
||||
__uint128_t r = static_cast<__uint128_t>( x ) * y;
|
||||
return static_cast<std::uint64_t>( r ) ^ static_cast<std::uint64_t>( r >> 64 );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline std::uint64_t mulx( std::uint64_t x, std::uint64_t y )
|
||||
{
|
||||
std::uint64_t x1 = static_cast<std::uint32_t>( x );
|
||||
std::uint64_t x2 = x >> 32;
|
||||
|
||||
std::uint64_t y1 = static_cast<std::uint32_t>( y );
|
||||
std::uint64_t y2 = y >> 32;
|
||||
|
||||
std::uint64_t r3 = x2 * y2;
|
||||
|
||||
std::uint64_t r2a = x1 * y2;
|
||||
|
||||
r3 += r2a >> 32;
|
||||
|
||||
std::uint64_t r2b = x2 * y1;
|
||||
|
||||
r3 += r2b >> 32;
|
||||
|
||||
std::uint64_t r1 = x1 * y1;
|
||||
|
||||
std::uint64_t r2 = (r1 >> 32) + static_cast<std::uint32_t>( r2a ) + static_cast<std::uint32_t>( r2b );
|
||||
|
||||
r1 = (r2 << 32) + static_cast<std::uint32_t>( r1 );
|
||||
r3 += r2 >> 32;
|
||||
|
||||
return r1 ^ r3;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace hash_detail
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_HASH_DETAIL_MULX_HPP
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright 2005-2014 Daniel James.
|
||||
// Copyright 2021, 2022 Peter Dimov.
|
||||
// Copyright 2021, 2022, 2025 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -11,21 +11,24 @@
|
||||
#define BOOST_FUNCTIONAL_HASH_HASH_HPP
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
#include <boost/container_hash/hash_is_avalanching.hpp>
|
||||
#include <boost/container_hash/is_range.hpp>
|
||||
#include <boost/container_hash/is_contiguous_range.hpp>
|
||||
#include <boost/container_hash/is_unordered_range.hpp>
|
||||
#include <boost/container_hash/detail/hash_tuple.hpp>
|
||||
#include <boost/container_hash/is_described_class.hpp>
|
||||
#include <boost/container_hash/detail/hash_integral.hpp>
|
||||
#include <boost/container_hash/detail/hash_tuple_like.hpp>
|
||||
#include <boost/container_hash/detail/hash_mix.hpp>
|
||||
#include <boost/container_hash/detail/hash_range.hpp>
|
||||
#include <boost/type_traits/is_enum.hpp>
|
||||
#include <boost/type_traits/is_integral.hpp>
|
||||
#include <boost/type_traits/is_floating_point.hpp>
|
||||
#include <boost/type_traits/is_signed.hpp>
|
||||
#include <boost/type_traits/is_unsigned.hpp>
|
||||
#include <boost/type_traits/make_unsigned.hpp>
|
||||
#include <boost/type_traits/enable_if.hpp>
|
||||
#include <boost/type_traits/conjunction.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/describe/bases.hpp>
|
||||
#include <boost/describe/members.hpp>
|
||||
#include <type_traits>
|
||||
#include <cstdint>
|
||||
|
||||
#if defined(BOOST_DESCRIBE_CXX14)
|
||||
# include <boost/mp11/algorithm.hpp>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
#include <complex>
|
||||
@@ -54,6 +57,10 @@
|
||||
#include <variant>
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
|
||||
# include <string_view>
|
||||
#endif
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
@@ -62,95 +69,12 @@ namespace boost
|
||||
//
|
||||
|
||||
// integral types
|
||||
|
||||
namespace hash_detail
|
||||
{
|
||||
template<class T,
|
||||
bool bigger_than_size_t = (sizeof(T) > sizeof(std::size_t)),
|
||||
bool is_unsigned = boost::is_unsigned<T>::value,
|
||||
std::size_t size_t_bits = sizeof(std::size_t) * CHAR_BIT,
|
||||
std::size_t type_bits = sizeof(T) * CHAR_BIT>
|
||||
struct hash_integral_impl;
|
||||
|
||||
template<class T, bool is_unsigned, std::size_t size_t_bits, std::size_t type_bits> struct hash_integral_impl<T, false, is_unsigned, size_t_bits, type_bits>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
return static_cast<std::size_t>( v );
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, std::size_t size_t_bits, std::size_t type_bits> struct hash_integral_impl<T, true, false, size_t_bits, type_bits>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
typedef typename boost::make_unsigned<T>::type U;
|
||||
|
||||
if( v >= 0 )
|
||||
{
|
||||
return hash_integral_impl<U>::fn( static_cast<U>( v ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return ~hash_integral_impl<U>::fn( static_cast<U>( ~static_cast<U>( v ) ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct hash_integral_impl<T, true, true, 32, 64>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( v >> 32 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct hash_integral_impl<T, true, true, 32, 128>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( v >> 96 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v >> 64 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v >> 32 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct hash_integral_impl<T, true, true, 64, 128>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( v >> 64 ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( v ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
template <typename T>
|
||||
typename boost::enable_if_<boost::is_integral<T>::value, std::size_t>::type
|
||||
hash_value( T v )
|
||||
{
|
||||
return hash_detail::hash_integral_impl<T>::fn( v );
|
||||
}
|
||||
// in detail/hash_integral.hpp
|
||||
|
||||
// enumeration types
|
||||
|
||||
template <typename T>
|
||||
typename boost::enable_if_<boost::is_enum<T>::value, std::size_t>::type
|
||||
typename std::enable_if<std::is_enum<T>::value, std::size_t>::type
|
||||
hash_value( T v )
|
||||
{
|
||||
// This should in principle return the equivalent of
|
||||
@@ -176,16 +100,15 @@ namespace boost
|
||||
{
|
||||
template<class T,
|
||||
std::size_t Bits = sizeof(T) * CHAR_BIT,
|
||||
int Digits = std::numeric_limits<T>::digits,
|
||||
std::size_t size_t_bits = sizeof(std::size_t) * CHAR_BIT>
|
||||
int Digits = std::numeric_limits<T>::digits>
|
||||
struct hash_float_impl;
|
||||
|
||||
// float
|
||||
template<class T, int Digits, std::size_t size_t_bits> struct hash_float_impl<T, 32, Digits, size_t_bits>
|
||||
template<class T, int Digits> struct hash_float_impl<T, 32, Digits>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint32_t w;
|
||||
std::uint32_t w;
|
||||
std::memcpy( &w, &v, sizeof( v ) );
|
||||
|
||||
return w;
|
||||
@@ -193,132 +116,72 @@ namespace boost
|
||||
};
|
||||
|
||||
// double
|
||||
template<class T, int Digits> struct hash_float_impl<T, 64, Digits, 64>
|
||||
template<class T, int Digits> struct hash_float_impl<T, 64, Digits>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint64_t w;
|
||||
std::uint64_t w;
|
||||
std::memcpy( &w, &v, sizeof( v ) );
|
||||
|
||||
return w;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, int Digits> struct hash_float_impl<T, 64, Digits, 32>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint32_t w[ 2 ];
|
||||
std::memcpy( &w, &v, sizeof( v ) );
|
||||
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[1] ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
return hash_value( w );
|
||||
}
|
||||
};
|
||||
|
||||
// 80 bit long double in 12 bytes
|
||||
template<class T> struct hash_float_impl<T, 96, 64, 64>
|
||||
template<class T> struct hash_float_impl<T, 96, 64>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint64_t w[ 2 ] = {};
|
||||
std::uint64_t w[ 2 ] = {};
|
||||
std::memcpy( &w, &v, 80 / CHAR_BIT );
|
||||
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[1] ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct hash_float_impl<T, 96, 64, 32>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint32_t w[ 3 ] = {};
|
||||
std::memcpy( &w, &v, 80 / CHAR_BIT );
|
||||
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[1] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[2] ) + hash_detail::hash_mix( seed );
|
||||
seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
// 80 bit long double in 16 bytes
|
||||
template<class T> struct hash_float_impl<T, 128, 64, 64>
|
||||
template<class T> struct hash_float_impl<T, 128, 64>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint64_t w[ 2 ] = {};
|
||||
std::uint64_t w[ 2 ] = {};
|
||||
std::memcpy( &w, &v, 80 / CHAR_BIT );
|
||||
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[1] ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T> struct hash_float_impl<T, 128, 64, 32>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint32_t w[ 3 ] = {};
|
||||
std::memcpy( &w, &v, 80 / CHAR_BIT );
|
||||
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[1] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[2] ) + hash_detail::hash_mix( seed );
|
||||
seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
// 128 bit long double
|
||||
template<class T, int Digits> struct hash_float_impl<T, 128, Digits, 64>
|
||||
template<class T, int Digits> struct hash_float_impl<T, 128, Digits>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint64_t w[ 2 ];
|
||||
std::uint64_t w[ 2 ];
|
||||
std::memcpy( &w, &v, sizeof( v ) );
|
||||
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[1] ) + hash_detail::hash_mix( seed );
|
||||
#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
|
||||
seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
|
||||
|
||||
template<class T, int Digits> struct hash_float_impl<T, 128, Digits, 32>
|
||||
{
|
||||
static std::size_t fn( T v )
|
||||
{
|
||||
boost::uint32_t w[ 4 ];
|
||||
std::memcpy( &w, &v, sizeof( v ) );
|
||||
#else
|
||||
|
||||
std::size_t seed = 0;
|
||||
|
||||
seed = static_cast<std::size_t>( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[1] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[2] ) + hash_detail::hash_mix( seed );
|
||||
seed = static_cast<std::size_t>( w[3] ) + hash_detail::hash_mix( seed );
|
||||
seed = hash_value( w[0] ) + hash_detail::hash_mix( seed );
|
||||
seed = hash_value( w[1] ) + hash_detail::hash_mix( seed );
|
||||
|
||||
#endif
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
@@ -326,7 +189,7 @@ namespace boost
|
||||
} // namespace hash_detail
|
||||
|
||||
template <typename T>
|
||||
typename boost::enable_if_<boost::is_floating_point<T>::value, std::size_t>::type
|
||||
typename std::enable_if<std::is_floating_point<T>::value, std::size_t>::type
|
||||
hash_value( T v )
|
||||
{
|
||||
return boost::hash_detail::hash_float_impl<T>::fn( v + 0 );
|
||||
@@ -337,7 +200,7 @@ namespace boost
|
||||
// `x + (x >> 3)` adjustment by Alberto Barbati and Dave Harris.
|
||||
template <class T> std::size_t hash_value( T* const& v )
|
||||
{
|
||||
boost::uintptr_t x = reinterpret_cast<boost::uintptr_t>( v );
|
||||
std::uintptr_t x = reinterpret_cast<std::uintptr_t>( v );
|
||||
return boost::hash_value( x + (x >> 3) );
|
||||
}
|
||||
|
||||
@@ -382,7 +245,7 @@ namespace boost
|
||||
// ranges (list, set, deque...)
|
||||
|
||||
template <typename T>
|
||||
typename boost::enable_if_<container_hash::is_range<T>::value && !container_hash::is_contiguous_range<T>::value && !container_hash::is_unordered_range<T>::value, std::size_t>::type
|
||||
typename std::enable_if<container_hash::is_range<T>::value && !container_hash::is_contiguous_range<T>::value && !container_hash::is_unordered_range<T>::value, std::size_t>::type
|
||||
hash_value( T const& v )
|
||||
{
|
||||
return boost::hash_range( v.begin(), v.end() );
|
||||
@@ -391,7 +254,7 @@ namespace boost
|
||||
// contiguous ranges (string, vector, array)
|
||||
|
||||
template <typename T>
|
||||
typename boost::enable_if_<container_hash::is_contiguous_range<T>::value, std::size_t>::type
|
||||
typename std::enable_if<container_hash::is_contiguous_range<T>::value, std::size_t>::type
|
||||
hash_value( T const& v )
|
||||
{
|
||||
return boost::hash_range( v.data(), v.data() + v.size() );
|
||||
@@ -400,7 +263,7 @@ namespace boost
|
||||
// unordered ranges (unordered_set, unordered_map)
|
||||
|
||||
template <typename T>
|
||||
typename boost::enable_if_<container_hash::is_unordered_range<T>::value, std::size_t>::type
|
||||
typename std::enable_if<container_hash::is_unordered_range<T>::value, std::size_t>::type
|
||||
hash_value( T const& v )
|
||||
{
|
||||
return boost::hash_unordered_range( v.begin(), v.end() );
|
||||
@@ -413,7 +276,7 @@ namespace boost
|
||||
// resolve ambiguity with unconstrained stdext::hash_value in <xhash> :-/
|
||||
|
||||
template<template<class...> class L, class... T>
|
||||
typename boost::enable_if_<container_hash::is_range<L<T...>>::value && !container_hash::is_contiguous_range<L<T...>>::value && !container_hash::is_unordered_range<L<T...>>::value, std::size_t>::type
|
||||
typename std::enable_if<container_hash::is_range<L<T...>>::value && !container_hash::is_contiguous_range<L<T...>>::value && !container_hash::is_unordered_range<L<T...>>::value, std::size_t>::type
|
||||
hash_value( L<T...> const& v )
|
||||
{
|
||||
return boost::hash_range( v.begin(), v.end() );
|
||||
@@ -422,14 +285,14 @@ namespace boost
|
||||
// contiguous ranges (string, vector, array)
|
||||
|
||||
template<template<class...> class L, class... T>
|
||||
typename boost::enable_if_<container_hash::is_contiguous_range<L<T...>>::value, std::size_t>::type
|
||||
typename std::enable_if<container_hash::is_contiguous_range<L<T...>>::value, std::size_t>::type
|
||||
hash_value( L<T...> const& v )
|
||||
{
|
||||
return boost::hash_range( v.data(), v.data() + v.size() );
|
||||
}
|
||||
|
||||
template<template<class, std::size_t> class L, class T, std::size_t N>
|
||||
typename boost::enable_if_<container_hash::is_contiguous_range<L<T, N>>::value, std::size_t>::type
|
||||
typename std::enable_if<container_hash::is_contiguous_range<L<T, N>>::value, std::size_t>::type
|
||||
hash_value( L<T, N> const& v )
|
||||
{
|
||||
return boost::hash_range( v.data(), v.data() + v.size() );
|
||||
@@ -438,12 +301,55 @@ namespace boost
|
||||
// unordered ranges (unordered_set, unordered_map)
|
||||
|
||||
template<template<class...> class L, class... T>
|
||||
typename boost::enable_if_<container_hash::is_unordered_range<L<T...>>::value, std::size_t>::type
|
||||
typename std::enable_if<container_hash::is_unordered_range<L<T...>>::value, std::size_t>::type
|
||||
hash_value( L<T...> const& v )
|
||||
{
|
||||
return boost::hash_unordered_range( v.begin(), v.end() );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// described classes
|
||||
|
||||
#if defined(BOOST_DESCRIBE_CXX14)
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER == 1900
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4100) // unreferenced formal parameter
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<container_hash::is_described_class<T>::value, std::size_t>::type
|
||||
hash_value( T const& v )
|
||||
{
|
||||
static_assert( !std::is_union<T>::value, "described unions are not supported" );
|
||||
|
||||
std::size_t r = 0;
|
||||
|
||||
using Bd = describe::describe_bases<T, describe::mod_any_access>;
|
||||
|
||||
mp11::mp_for_each<Bd>([&](auto D){
|
||||
|
||||
using B = typename decltype(D)::type;
|
||||
boost::hash_combine( r, (B const&)v );
|
||||
|
||||
});
|
||||
|
||||
using Md = describe::describe_members<T, describe::mod_any_access>;
|
||||
|
||||
mp11::mp_for_each<Md>([&](auto D){
|
||||
|
||||
boost::hash_combine( r, v.*D.pointer );
|
||||
|
||||
});
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER == 1900
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// std::unique_ptr, std::shared_ptr
|
||||
@@ -499,6 +405,19 @@ namespace boost
|
||||
return seed;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// std::nullptr_t
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_NULLPTR)
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_same<T, std::nullptr_t>::value, std::size_t>::type
|
||||
hash_value( T const& /*v*/ )
|
||||
{
|
||||
return boost::hash_value( static_cast<void*>( nullptr ) );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// std::optional
|
||||
@@ -510,7 +429,7 @@ namespace boost
|
||||
{
|
||||
if( !v )
|
||||
{
|
||||
// Arbitray value for empty optional.
|
||||
// Arbitrary value for empty optional.
|
||||
return 0x12345678;
|
||||
}
|
||||
else
|
||||
@@ -638,8 +557,17 @@ namespace boost
|
||||
};
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef BOOST_FUNCTIONAL_HASH_ROTL32
|
||||
// hash_is_avalanching
|
||||
|
||||
template<class Ch> struct hash_is_avalanching< boost::hash< std::basic_string<Ch> > >: std::is_integral<Ch> {};
|
||||
|
||||
#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
|
||||
|
||||
template<class Ch> struct hash_is_avalanching< boost::hash< std::basic_string_view<Ch> > >: std::is_integral<Ch> {};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright 2005-2009 Daniel James.
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Copyright 2021, 2022, 2025 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace container_hash
|
||||
template<class T> struct is_range;
|
||||
template<class T> struct is_contiguous_range;
|
||||
template<class T> struct is_unordered_range;
|
||||
template<class T> struct is_described_class;
|
||||
template<class T> struct is_tuple_like;
|
||||
|
||||
} // namespace container_hash
|
||||
|
||||
@@ -30,6 +32,8 @@ template<class It> std::size_t hash_range( It, It );
|
||||
template<class It> void hash_unordered_range( std::size_t&, It, It );
|
||||
template<class It> std::size_t hash_unordered_range( It, It );
|
||||
|
||||
template<class Hash> struct hash_is_avalanching;
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_FUNCTIONAL_HASH_FWD_HPP
|
||||
|
||||
57
include/boost/container_hash/hash_is_avalanching.hpp
Normal file
57
include/boost/container_hash/hash_is_avalanching.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2025 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_HASH_HASH_IS_AVALANCHING_HPP_INCLUDED
|
||||
#define BOOST_HASH_HASH_IS_AVALANCHING_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
template<class... Ts> struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template<class... Ts> using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template<class IsAvalanching> struct avalanching_value
|
||||
{
|
||||
static constexpr bool value = IsAvalanching::value;
|
||||
};
|
||||
|
||||
// may be explicitly marked as BOOST_DEPRECATED in the future
|
||||
template<> struct avalanching_value<void>
|
||||
{
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template<class Hash, class = void> struct hash_is_avalanching_impl: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class Hash> struct hash_is_avalanching_impl<Hash, void_t<typename Hash::is_avalanching> >:
|
||||
std::integral_constant<bool, avalanching_value<typename Hash::is_avalanching>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template<class Hash>
|
||||
struct hash_is_avalanching_impl<Hash, typename std::enable_if< ((void)Hash::is_avalanching, true) >::type>
|
||||
{
|
||||
// Hash::is_avalanching is not a type: we don't define value to produce
|
||||
// a compile error downstream
|
||||
};
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
template<class Hash> struct hash_is_avalanching: hash_detail::hash_is_avalanching_impl<Hash>::type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_HASH_HASH_IS_AVALANCHING_HPP_INCLUDED
|
||||
@@ -6,15 +6,12 @@
|
||||
#define BOOST_HASH_IS_CONTIGUOUS_RANGE_HPP_INCLUDED
|
||||
|
||||
#include <boost/container_hash/is_range.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700)
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1910)
|
||||
|
||||
#include <boost/type_traits/is_integral.hpp>
|
||||
#include <boost/type_traits/declval.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <iterator>
|
||||
|
||||
namespace boost
|
||||
@@ -23,11 +20,11 @@ namespace hash_detail
|
||||
{
|
||||
|
||||
template<class It, class T, class S>
|
||||
integral_constant< bool, is_same<typename std::iterator_traits<It>::value_type, T>::value && is_integral<S>::value >
|
||||
std::integral_constant< bool, std::is_same<typename std::iterator_traits<It>::value_type, T>::value && std::is_integral<S>::value >
|
||||
is_contiguous_range_check( It first, It last, T const*, T const*, S );
|
||||
|
||||
template<class T> decltype( is_contiguous_range_check( declval<T const&>().begin(), declval<T const&>().end(), declval<T const&>().data(), declval<T const&>().data() + declval<T const&>().size(), declval<T const&>().size() ) ) is_contiguous_range_( int );
|
||||
template<class T> false_type is_contiguous_range_( ... );
|
||||
template<class T> decltype( is_contiguous_range_check( std::declval<T const&>().begin(), std::declval<T const&>().end(), std::declval<T const&>().data(), std::declval<T const&>().data() + std::declval<T const&>().size(), std::declval<T const&>().size() ) ) is_contiguous_range_( int );
|
||||
template<class T> std::false_type is_contiguous_range_( ... );
|
||||
|
||||
template<class T> struct is_contiguous_range: decltype( hash_detail::is_contiguous_range_<T>( 0 ) )
|
||||
{
|
||||
@@ -38,54 +35,64 @@ template<class T> struct is_contiguous_range: decltype( hash_detail::is_contiguo
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<class T> struct is_contiguous_range: integral_constant< bool, is_range<T>::value && hash_detail::is_contiguous_range<T>::value >
|
||||
template<class T> struct is_contiguous_range: std::integral_constant< bool, is_range<T>::value && hash_detail::is_contiguous_range<T>::value >
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace container_hash
|
||||
} // namespace boost
|
||||
|
||||
#else // !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR)
|
||||
#else // !BOOST_WORKAROUND(BOOST_MSVC, < 1910)
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#if !defined(BOOST_NO_CXX11_HDR_ARRAY)
|
||||
#include <array>
|
||||
#endif
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<class T> struct is_contiguous_range: false_type
|
||||
template<class T> struct is_contiguous_range: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class E, class T, class A> struct is_contiguous_range< std::basic_string<E, T, A> >: true_type
|
||||
template<class E, class T, class A> struct is_contiguous_range< std::basic_string<E, T, A> >: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class E, class T, class A> struct is_contiguous_range< std::basic_string<E, T, A> const >: true_type
|
||||
template<class E, class T, class A> struct is_contiguous_range< std::basic_string<E, T, A> const >: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_HDR_ARRAY)
|
||||
|
||||
template<class T, std::size_t N> struct is_contiguous_range< std::array<T, N> >: true_type
|
||||
template<class T, class A> struct is_contiguous_range< std::vector<T, A> >: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class T, std::size_t N> struct is_contiguous_range< std::array<T, N> const >: true_type
|
||||
template<class T, class A> struct is_contiguous_range< std::vector<T, A> const >: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
#endif
|
||||
template<class A> struct is_contiguous_range< std::vector<bool, A> >: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class A> struct is_contiguous_range< std::vector<bool, A> const >: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class T, std::size_t N> struct is_contiguous_range< std::array<T, N> >: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class T, std::size_t N> struct is_contiguous_range< std::array<T, N> const >: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace container_hash
|
||||
} // namespace boost
|
||||
|
||||
#endif // !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR)
|
||||
#endif // !BOOST_WORKAROUND(BOOST_MSVC, < 1910)
|
||||
|
||||
#endif // #ifndef BOOST_HASH_IS_CONTIGUOUS_RANGE_HPP_INCLUDED
|
||||
|
||||
37
include/boost/container_hash/is_described_class.hpp
Normal file
37
include/boost/container_hash/is_described_class.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_HASH_IS_DESCRIBED_CLASS_HPP_INCLUDED
|
||||
#define BOOST_HASH_IS_DESCRIBED_CLASS_HPP_INCLUDED
|
||||
|
||||
#include <boost/describe/bases.hpp>
|
||||
#include <boost/describe/members.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
#if defined(BOOST_DESCRIBE_CXX11)
|
||||
|
||||
template<class T> struct is_described_class: std::integral_constant<bool,
|
||||
describe::has_describe_bases<T>::value &&
|
||||
describe::has_describe_members<T>::value &&
|
||||
!std::is_union<T>::value>
|
||||
{
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template<class T> struct is_described_class: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace container_hash
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_HASH_IS_DESCRIBED_CLASS_HPP_INCLUDED
|
||||
@@ -5,28 +5,25 @@
|
||||
#ifndef BOOST_HASH_IS_RANGE_HPP_INCLUDED
|
||||
#define BOOST_HASH_IS_RANGE_HPP_INCLUDED
|
||||
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/type_traits/is_integral.hpp>
|
||||
#include <boost/type_traits/declval.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/type_traits/remove_cv.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700)
|
||||
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
template<class T> struct iterator_traits: std::iterator_traits<T> {};
|
||||
template<> struct iterator_traits< void* > {};
|
||||
template<> struct iterator_traits< void const* > {};
|
||||
|
||||
template<class T, class It>
|
||||
integral_constant< bool, !is_same<typename remove_cv<T>::type, typename std::iterator_traits<It>::value_type>::value >
|
||||
std::integral_constant< bool, !std::is_same<typename std::remove_cv<T>::type, typename iterator_traits<It>::value_type>::value >
|
||||
is_range_check( It first, It last );
|
||||
|
||||
template<class T> decltype( is_range_check<T>( declval<T const&>().begin(), declval<T const&>().end() ) ) is_range_( int );
|
||||
template<class T> false_type is_range_( ... );
|
||||
template<class T> decltype( is_range_check<T>( std::declval<T const&>().begin(), std::declval<T const&>().end() ) ) is_range_( int );
|
||||
template<class T> std::false_type is_range_( ... );
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
@@ -39,35 +36,6 @@ template<class T> struct is_range: decltype( hash_detail::is_range_<T>( 0 ) )
|
||||
|
||||
} // namespace container_hash
|
||||
|
||||
#else
|
||||
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
template<class T, class E = true_type> struct is_range_: false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class T> struct is_range_< T, integral_constant< bool,
|
||||
is_same<typename T::value_type, typename std::iterator_traits<typename T::const_iterator>::value_type>::value &&
|
||||
is_integral<typename T::size_type>::value
|
||||
> >: true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<class T> struct is_range: hash_detail::is_range_<T>
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace container_hash
|
||||
|
||||
#endif // !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR)
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_HASH_IS_RANGE_HPP_INCLUDED
|
||||
|
||||
36
include/boost/container_hash/is_tuple_like.hpp
Normal file
36
include/boost/container_hash/is_tuple_like.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef BOOST_HASH_IS_TUPLE_LIKE_HPP_INCLUDED
|
||||
#define BOOST_HASH_IS_TUPLE_LIKE_HPP_INCLUDED
|
||||
|
||||
// Copyright 2017, 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
template<class T, class E = std::true_type> struct is_tuple_like_: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class T> struct is_tuple_like_<T, std::integral_constant<bool, std::tuple_size<T>::value == std::tuple_size<T>::value> >: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace hash_detail
|
||||
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<class T> struct is_tuple_like: hash_detail::is_tuple_like_< typename std::remove_cv<T>::type >
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace container_hash
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_HASH_IS_TUPLE_LIKE_HPP_INCLUDED
|
||||
@@ -6,21 +6,20 @@
|
||||
#define BOOST_HASH_IS_UNORDERED_RANGE_HPP_INCLUDED
|
||||
|
||||
#include <boost/container_hash/is_range.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace hash_detail
|
||||
{
|
||||
|
||||
template<class T, class E = true_type> struct has_hasher_: false_type
|
||||
template<class T, class E = std::true_type> struct has_hasher_: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class T> struct has_hasher_< T, integral_constant< bool,
|
||||
is_same<typename T::hasher, typename T::hasher>::value
|
||||
> >: true_type
|
||||
template<class T> struct has_hasher_< T, std::integral_constant< bool,
|
||||
std::is_same<typename T::hasher, typename T::hasher>::value
|
||||
> >: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
@@ -29,7 +28,7 @@ template<class T> struct has_hasher_< T, integral_constant< bool,
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<class T> struct is_unordered_range: integral_constant< bool, is_range<T>::value && hash_detail::has_hasher_<T>::value >
|
||||
template<class T> struct is_unordered_range: std::integral_constant< bool, is_range<T>::value && hash_detail::has_hasher_<T>::value >
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"Daniel James"
|
||||
],
|
||||
"maintainers": [
|
||||
"Daniel James <dnljms -at- gmail.com>"
|
||||
"Peter Dimov <pdimov -at- gmail.com>"
|
||||
],
|
||||
"description": "An STL-compatible hash function object that can be extended to hash user defined types.",
|
||||
"std": [
|
||||
@@ -16,6 +16,6 @@
|
||||
"category": [
|
||||
"Function-objects"
|
||||
],
|
||||
"cxxstd": "03"
|
||||
"cxxstd": "11"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2018, 2019, 2021 Peter Dimov
|
||||
# Copyright 2018, 2019, 2021, 2022, 2025 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -6,6 +6,7 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
|
||||
|
||||
if(HAVE_BOOST_TEST)
|
||||
|
||||
boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::container_hash Boost::core Boost::utility)
|
||||
boost_test_jamfile(FILE Jamfile.v2
|
||||
LINK_LIBRARIES Boost::container_hash Boost::core Boost::utility)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright 2005-2012 Daniel James.
|
||||
# Copyright 2022 Peter Dimov
|
||||
# Copyright 2022, 2025 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -10,6 +10,10 @@ local clang-flags = $(gcc-flags) -Wno-c99-extensions ;
|
||||
|
||||
project hash-tests
|
||||
: requirements
|
||||
<library>/boost/container_hash//boost_container_hash
|
||||
<library>/boost/core//boost_core
|
||||
<library>/boost/type_traits//boost_type_traits
|
||||
|
||||
<warnings>pedantic
|
||||
<toolset>intel:<warnings>on
|
||||
<toolset>gcc:<cxxflags>$(gcc-flags)
|
||||
@@ -72,13 +76,15 @@ run quick.cpp ;
|
||||
|
||||
run hash_number_test2.cpp ;
|
||||
run hash_integral_test.cpp ;
|
||||
run hash_string_test2.cpp ;
|
||||
run hash_string_test2.cpp
|
||||
/boost/utility//boost_utility ;
|
||||
|
||||
# for gcc-4.8
|
||||
local fs-path-req = "-<toolset>gcc:<cxxflags>-Wshadow" "-<toolset>gcc:<cxxflags>-Wconversion" ;
|
||||
local fs-path-req = <library>/boost/filesystem//boost_filesystem/<warnings>off "-<toolset>gcc:<cxxflags>-Wshadow" "-<toolset>gcc:<cxxflags>-Wconversion" <toolset>gcc-4.7:<build>no <undefined-sanitizer>norecover:<link>static ;
|
||||
|
||||
run hash_fs_path_test.cpp /boost//filesystem/<warnings>off : : : $(fs-path-req) <toolset>msvc-14.0,<cxxstd>latest:<build>no <toolset>msvc-8.0:<build>no ;
|
||||
run is_range_test2.cpp : : : $(fs-path-req) <toolset>msvc-8.0:<build>no ;
|
||||
run hash_fs_path_test.cpp : : : $(fs-path-req) <toolset>msvc-14.0,<cxxstd>latest:<build>no ;
|
||||
|
||||
run is_range_test2.cpp : : : $(fs-path-req) ;
|
||||
|
||||
run hash_container_test.cpp ;
|
||||
|
||||
@@ -101,3 +107,31 @@ run hash_unordered_map_test.cpp ;
|
||||
run is_range_test3.cpp ;
|
||||
run is_contiguous_range_test2.cpp ;
|
||||
run is_unordered_range_test2.cpp ;
|
||||
run is_contiguous_range_test3.cpp ;
|
||||
|
||||
run is_described_class_test.cpp
|
||||
: : : <warnings>extra ;
|
||||
run is_described_class_test2.cpp
|
||||
: : : <warnings>extra ;
|
||||
run is_described_class_test3.cpp
|
||||
: : : <warnings>extra ;
|
||||
|
||||
run described_class_test.cpp
|
||||
: : : <warnings>extra ;
|
||||
|
||||
run hash_is_avalanching_test.cpp ;
|
||||
run hash_is_avalanching_test2.cpp ;
|
||||
run hash_is_avalanching_test3.cpp ;
|
||||
|
||||
run hash_integral_test2.cpp ;
|
||||
|
||||
run hash_nullptr_test.cpp ;
|
||||
|
||||
run is_tuple_like_test.cpp ;
|
||||
|
||||
run hash_tuple_like_test.cpp ;
|
||||
run hash_tuple_like_test2.cpp
|
||||
: : : <warnings>extra ;
|
||||
|
||||
run is_range_test4.cpp ;
|
||||
run hash_container_test2.cpp ;
|
||||
|
||||
@@ -14,4 +14,4 @@ target_link_libraries(quick Boost::container_hash)
|
||||
enable_testing()
|
||||
add_test(quick quick)
|
||||
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)
|
||||
add_custom_target(check VERBATIM COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)
|
||||
|
||||
@@ -9,8 +9,8 @@ project(cmake_subdir_test LANGUAGES CXX)
|
||||
add_subdirectory(../.. boostorg/container_hash)
|
||||
|
||||
add_subdirectory(../../../config boostorg/config)
|
||||
add_subdirectory(../../../type_traits boostorg/type_traits)
|
||||
add_subdirectory(../../../static_assert boostorg/static_assert)
|
||||
add_subdirectory(../../../describe boostorg/describe)
|
||||
add_subdirectory(../../../mp11 boostorg/mp11)
|
||||
|
||||
add_executable(quick ../quick.cpp)
|
||||
target_link_libraries(quick Boost::container_hash)
|
||||
@@ -18,4 +18,4 @@ target_link_libraries(quick Boost::container_hash)
|
||||
enable_testing()
|
||||
add_test(quick quick)
|
||||
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)
|
||||
add_custom_target(check VERBATIM COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)
|
||||
|
||||
159
test/described_class_test.cpp
Normal file
159
test/described_class_test.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic ignored "-Wunused-private-field"
|
||||
#endif
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/describe/class.hpp>
|
||||
#include <boost/describe/operators.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
|
||||
#if !defined(BOOST_DESCRIBE_CXX14)
|
||||
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
|
||||
BOOST_PRAGMA_MESSAGE( "Skipping test because BOOST_DESCRIBE_CXX14 is not defined" )
|
||||
|
||||
int main() {}
|
||||
|
||||
#else
|
||||
|
||||
struct X1
|
||||
{
|
||||
int m;
|
||||
explicit X1( int m_ ): m( m_ ) {}
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT( X1, (), (m) )
|
||||
|
||||
struct X2
|
||||
{
|
||||
int m;
|
||||
explicit X2( int m_ ): m( m_ ) {}
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT( X2, (), (m) )
|
||||
|
||||
struct X3
|
||||
{
|
||||
int m;
|
||||
explicit X3( int m_ ): m( m_ ) {}
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT( X3, (), (m) )
|
||||
|
||||
class Y: public X1, protected X2, private X3
|
||||
{
|
||||
public:
|
||||
|
||||
int m1;
|
||||
|
||||
protected:
|
||||
|
||||
int m2;
|
||||
|
||||
private:
|
||||
|
||||
int m3;
|
||||
|
||||
public:
|
||||
|
||||
Y( int x1, int x2, int x3, int m1_, int m2_, int m3_ ):
|
||||
X1( x1 ), X2( x2 ), X3( x3 ), m1( m1_ ), m2( m2_ ), m3( m3_ ) {}
|
||||
|
||||
BOOST_DESCRIBE_CLASS( Y, (X1, X2, X3), (m1), (m2), (m3) )
|
||||
};
|
||||
|
||||
using boost::describe::operators::operator==;
|
||||
using boost::describe::operators::operator!=;
|
||||
using boost::describe::operators::operator<<;
|
||||
|
||||
int main()
|
||||
{
|
||||
Y y1( 0, 0, 0, 0, 0, 0 );
|
||||
|
||||
BOOST_TEST_EQ( y1, y1 );
|
||||
BOOST_TEST_EQ( boost::hash<Y>()(y1), boost::hash<Y>()(y1) );
|
||||
|
||||
Y y2( 1, 0, 0, 0, 0, 0 );
|
||||
|
||||
BOOST_TEST_NE( y1, y2 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y2) );
|
||||
|
||||
Y y3( 0, 1, 0, 0, 0, 0 );
|
||||
|
||||
BOOST_TEST_NE( y1, y3 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y3) );
|
||||
|
||||
BOOST_TEST_NE( y2, y3 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y3) );
|
||||
|
||||
Y y4( 0, 0, 1, 0, 0, 0 );
|
||||
|
||||
BOOST_TEST_NE( y1, y4 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y4) );
|
||||
|
||||
BOOST_TEST_NE( y2, y4 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y4) );
|
||||
|
||||
BOOST_TEST_NE( y3, y4 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y3), boost::hash<Y>()(y4) );
|
||||
|
||||
Y y5( 0, 0, 0, 1, 0, 0 );
|
||||
|
||||
BOOST_TEST_NE( y1, y5 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y5) );
|
||||
|
||||
BOOST_TEST_NE( y2, y5 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y5) );
|
||||
|
||||
BOOST_TEST_NE( y3, y5 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y3), boost::hash<Y>()(y5) );
|
||||
|
||||
BOOST_TEST_NE( y4, y5 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y4), boost::hash<Y>()(y5) );
|
||||
|
||||
Y y6( 0, 0, 0, 0, 1, 0 );
|
||||
|
||||
BOOST_TEST_NE( y1, y6 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y6) );
|
||||
|
||||
BOOST_TEST_NE( y2, y6 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y6) );
|
||||
|
||||
BOOST_TEST_NE( y3, y6 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y3), boost::hash<Y>()(y6) );
|
||||
|
||||
BOOST_TEST_NE( y4, y6 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y4), boost::hash<Y>()(y6) );
|
||||
|
||||
BOOST_TEST_NE( y5, y6 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y5), boost::hash<Y>()(y6) );
|
||||
|
||||
Y y7( 0, 0, 0, 0, 0, 1 );
|
||||
|
||||
BOOST_TEST_NE( y1, y7 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y1), boost::hash<Y>()(y7) );
|
||||
|
||||
BOOST_TEST_NE( y2, y7 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y2), boost::hash<Y>()(y7) );
|
||||
|
||||
BOOST_TEST_NE( y3, y7 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y3), boost::hash<Y>()(y7) );
|
||||
|
||||
BOOST_TEST_NE( y4, y7 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y4), boost::hash<Y>()(y7) );
|
||||
|
||||
BOOST_TEST_NE( y5, y7 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y5), boost::hash<Y>()(y7) );
|
||||
|
||||
BOOST_TEST_NE( y6, y7 );
|
||||
BOOST_TEST_NE( boost::hash<Y>()(y6), boost::hash<Y>()(y7) );
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,8 @@
|
||||
// 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)
|
||||
|
||||
#define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING
|
||||
|
||||
#include "./config.hpp"
|
||||
|
||||
#if !defined(BOOST_HASH_TEST_EXTENSIONS)
|
||||
|
||||
@@ -48,5 +48,13 @@ int main()
|
||||
test<float>();
|
||||
test<double>();
|
||||
|
||||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||
test<char8_t>();
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||
test<std::byte>();
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
39
test/hash_container_test2.cpp
Normal file
39
test/hash_container_test2.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2024 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
struct X1
|
||||
{
|
||||
void* begin() const { return nullptr; }
|
||||
void* end() const { return nullptr; }
|
||||
};
|
||||
|
||||
std::size_t hash_value( X1 const& )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct X2
|
||||
{
|
||||
void const* begin() const { return nullptr; }
|
||||
void const* end() const { return nullptr; }
|
||||
};
|
||||
|
||||
std::size_t hash_value( X2 const& )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
X1 x1;
|
||||
BOOST_TEST_EQ( boost::hash<X1>()( x1 ), 1u );
|
||||
|
||||
X2 x2;
|
||||
BOOST_TEST_EQ( boost::hash<X2>()( x2 ), 2u );
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
// 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)
|
||||
|
||||
#define _SILENCE_CXX23_DENORM_DEPRECATION_WARNING
|
||||
|
||||
#include "hash_float_test.hpp"
|
||||
|
||||
int main()
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
// P2614 deprecates has_denorm in C++23
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
char const* float_type(float*) { return "float"; }
|
||||
char const* float_type(double*) { return "double"; }
|
||||
char const* float_type(long double*) { return "long double"; }
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include "./compile_time.hpp"
|
||||
|
||||
void void_func1() { static int x = 1; ++x; }
|
||||
void void_func2() { static int x = 2; --x; }
|
||||
void void_func1() { static int x = 1; ++x; (void)x; }
|
||||
void void_func2() { static int x = 2; --x; (void)x; }
|
||||
int int_func1(int) { return 0; }
|
||||
int int_func2(int) { return 1; }
|
||||
|
||||
|
||||
@@ -50,10 +50,11 @@ void write_compiler_info() {
|
||||
msvc) - 1;
|
||||
unsigned difference = msvc.version - v->version;
|
||||
|
||||
std::cout << v->description << std::endl;
|
||||
std::cout << v->description;
|
||||
if (difference) {
|
||||
std::cout << "+" << difference << std::endl;
|
||||
std::cout << " +" << difference;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -109,6 +110,7 @@ int main() {
|
||||
std::cout << std::endl;
|
||||
|
||||
PRINT(sizeof(std::size_t)*CHAR_BIT);
|
||||
PRINT(std::numeric_limits<std::size_t>::digits);
|
||||
std::cout << std::endl;
|
||||
|
||||
PRINT(sizeof(float)*CHAR_BIT);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/type_name.hpp>
|
||||
#include <boost/type_traits/is_signed.hpp>
|
||||
#include <set>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(disable: 4127) // conditional expression is constant
|
||||
|
||||
52
test/hash_integral_test2.cpp
Normal file
52
test/hash_integral_test2.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2005-2009 Daniel James.
|
||||
// Copyright 2021 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/type_name.hpp>
|
||||
#include <boost/type_traits/is_signed.hpp>
|
||||
#include <boost/type_traits/make_unsigned.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
// This test checks that values representable in a signed
|
||||
// and the corresponding unsigned type hash to the same value
|
||||
|
||||
template<class T>
|
||||
void signed_unsigned_test()
|
||||
{
|
||||
BOOST_STATIC_ASSERT( boost::is_signed<T>::value );
|
||||
|
||||
typedef typename boost::make_unsigned<T>::type U;
|
||||
|
||||
T x = std::numeric_limits<T>::max();
|
||||
|
||||
do
|
||||
{
|
||||
BOOST_TEST_EQ( boost::hash<T>()( x ), boost::hash<U>()( static_cast<U>( x ) ) );
|
||||
x /= 3;
|
||||
}
|
||||
while( x > 0 );
|
||||
}
|
||||
|
||||
#define TEST(type) std::cerr << "Testing: " #type " (" << boost::core::type_name<type>() << ")\n"; signed_unsigned_test<type>();
|
||||
|
||||
int main()
|
||||
{
|
||||
TEST(signed char)
|
||||
TEST(short)
|
||||
TEST(int)
|
||||
TEST(long)
|
||||
|
||||
#if !defined(BOOST_NO_LONG_LONG)
|
||||
TEST(boost::long_long_type)
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_HAS_INT128)
|
||||
TEST(boost::int128_type)
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
45
test/hash_is_avalanching_test.cpp
Normal file
45
test/hash_is_avalanching_test.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2022, 2025 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/container_hash/hash_is_avalanching.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <string>
|
||||
|
||||
enum my_char { min = 0, max = 255 };
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::hash_is_avalanching;
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::string> > ));
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::wstring> > ));
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_CHAR16_T)
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::u16string> > ));
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_CHAR32_T)
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::u32string> > ));
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash< std::basic_string<char8_t> > > ));
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 160000
|
||||
// std::char_traits<Ch> is deprecated for non-char types
|
||||
#else
|
||||
BOOST_TEST_TRAIT_FALSE(( hash_is_avalanching< boost::hash<std::basic_string<my_char> > > ));
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
56
test/hash_is_avalanching_test2.cpp
Normal file
56
test/hash_is_avalanching_test2.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2022, 2025 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/container_hash/hash_is_avalanching.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
|
||||
#if defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE( "Test skipped, BOOST_NO_CXX17_HDR_STRING_VIEW is defined" )
|
||||
int main() {}
|
||||
|
||||
#else
|
||||
|
||||
#include <string_view>
|
||||
|
||||
enum my_char { min = 0, max = 255 };
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::hash_is_avalanching;
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::string_view> > ));
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::wstring_view> > ));
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_CHAR16_T)
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::u16string_view> > ));
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_CHAR32_T)
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash<std::u32string_view> > ));
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash< std::basic_string_view<char8_t> > > ));
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 160000
|
||||
// std::char_traits<Ch> is deprecated for non-char types
|
||||
#else
|
||||
BOOST_TEST_TRAIT_FALSE(( hash_is_avalanching< boost::hash<std::basic_string_view<my_char> > > ));
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
#endif
|
||||
38
test/hash_is_avalanching_test3.cpp
Normal file
38
test/hash_is_avalanching_test3.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2025 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/hash_is_avalanching.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
struct X
|
||||
{
|
||||
using is_avalanching = void;
|
||||
};
|
||||
|
||||
struct Y
|
||||
{
|
||||
using is_avalanching = std::true_type;
|
||||
};
|
||||
|
||||
struct Z
|
||||
{
|
||||
using is_avalanching = std::false_type;
|
||||
};
|
||||
|
||||
struct W
|
||||
{
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::hash_is_avalanching;
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< X > ));
|
||||
BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< Y > ));
|
||||
BOOST_TEST_TRAIT_FALSE(( hash_is_avalanching< Z > ));
|
||||
BOOST_TEST_TRAIT_FALSE(( hash_is_avalanching< W > ));
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
// 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)
|
||||
|
||||
#define _SILENCE_CXX23_DENORM_DEPRECATION_WARNING
|
||||
|
||||
#include "hash_float_test.hpp"
|
||||
|
||||
int main()
|
||||
|
||||
41
test/hash_nullptr_test.cpp
Normal file
41
test/hash_nullptr_test.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
#if defined(BOOST_NO_CXX11_NULLPTR)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE( "Test skipped, BOOST_NO_CXX11_NULLPTR is defined" )
|
||||
int main() {}
|
||||
|
||||
#else
|
||||
|
||||
template<class T> std::size_t hv( T const& x )
|
||||
{
|
||||
return boost::hash<T>()( x );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
BOOST_TEST_EQ( hv((void*)0), hv(nullptr) );
|
||||
}
|
||||
|
||||
{
|
||||
int x = 0;
|
||||
|
||||
BOOST_TEST_EQ( hv((int*)0), hv(nullptr) );
|
||||
BOOST_TEST_NE( hv(&x), hv(nullptr) );
|
||||
|
||||
(void)x;
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -21,6 +21,10 @@ int main() {}
|
||||
# pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable: 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <string>
|
||||
@@ -167,16 +171,16 @@ int main()
|
||||
|
||||
#if SIZE_MAX == 4294967295U
|
||||
|
||||
BOOST_TEST_EQ( hv(1.0), 1072693248U );
|
||||
BOOST_TEST_EQ( hv(-1.0), 3220176896U );
|
||||
BOOST_TEST_EQ( hv(3.14), 3972386992U );
|
||||
BOOST_TEST_EQ( hv(-3.14), 1824903344U );
|
||||
BOOST_TEST_EQ( hv(1e-308), 2213556530U );
|
||||
BOOST_TEST_EQ( hv(-1e-308), 66072882U );
|
||||
BOOST_TEST_EQ( hv(1e+308), 2623678890U );
|
||||
BOOST_TEST_EQ( hv(-1e+308), 476195242U );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<double>::infinity()), 2146435072U );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<double>::infinity()), 4293918720U );
|
||||
BOOST_TEST_EQ( hv(1.0), 2619008688U );
|
||||
BOOST_TEST_EQ( hv(-1.0), 146497060U );
|
||||
BOOST_TEST_EQ( hv(3.14), 101651732U );
|
||||
BOOST_TEST_EQ( hv(-3.14), 210858151U );
|
||||
BOOST_TEST_EQ( hv(1e-308), 3911789313U );
|
||||
BOOST_TEST_EQ( hv(-1e-308), 1812507313U );
|
||||
BOOST_TEST_EQ( hv(1e+308), 987802568U );
|
||||
BOOST_TEST_EQ( hv(-1e+308), 1639042439U );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<double>::infinity()), 3227645345U );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<double>::infinity()), 2247339177U );
|
||||
|
||||
#else
|
||||
|
||||
@@ -197,49 +201,62 @@ int main()
|
||||
BOOST_TEST_EQ( hv(0.0L), 0 );
|
||||
BOOST_TEST_EQ( hv(-0.0L), 0 );
|
||||
|
||||
#if defined(_WIN32) && !defined(__GNUC__) // Under MS ABI, long double == double
|
||||
std::size_t const ldbits = sizeof( long double ) * CHAR_BIT;
|
||||
|
||||
#if SIZE_MAX == 4294967295U
|
||||
|
||||
BOOST_TEST_EQ( hv(1.0L), 1072693248U );
|
||||
BOOST_TEST_EQ( hv(-1.0L), 3220176896U );
|
||||
BOOST_TEST_EQ( hv(3.14L), 3972386992U );
|
||||
BOOST_TEST_EQ( hv(-3.14L), 1824903344U );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), 2146435072U );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), 4293918720U );
|
||||
if( ldbits == 64 )
|
||||
{
|
||||
BOOST_TEST_EQ( hv(1.0L), hv(1.0) );
|
||||
BOOST_TEST_EQ( hv(-1.0L), hv(-1.0) );
|
||||
BOOST_TEST_EQ( hv(3.14L), hv(3.14) );
|
||||
BOOST_TEST_EQ( hv(-3.14L), hv(-3.14) );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), hv(std::numeric_limits<double>::infinity()) );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), hv(-std::numeric_limits<double>::infinity()) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// ldbits == 96
|
||||
|
||||
BOOST_TEST_EQ( hv(1.0L), 3632050780U );
|
||||
BOOST_TEST_EQ( hv(-1.0L), 3632083548U );
|
||||
BOOST_TEST_EQ( hv(3.14L), 1742026549U );
|
||||
BOOST_TEST_EQ( hv(-3.14L), 1742059317U );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), 3632067164U );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), 3632099932U );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
BOOST_TEST_EQ( hv(1.0L), 4607182418800017408ULL );
|
||||
BOOST_TEST_EQ( hv(-1.0L), 13830554455654793216ULL );
|
||||
BOOST_TEST_EQ( hv(3.14L), 4614253070214989087ULL );
|
||||
BOOST_TEST_EQ( hv(-3.14L), 13837625107069764895ULL );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), 9218868437227405312ULL );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), 18442240474082181120ULL );
|
||||
if( ldbits == 64 )
|
||||
{
|
||||
BOOST_TEST_EQ( hv(1.0L), 4607182418800017408ULL );
|
||||
BOOST_TEST_EQ( hv(-1.0L), 13830554455654793216ULL );
|
||||
BOOST_TEST_EQ( hv(3.14L), 4614253070214989087ULL );
|
||||
BOOST_TEST_EQ( hv(-3.14L), 13837625107069764895ULL );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), 9218868437227405312ULL );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), 18442240474082181120ULL );
|
||||
}
|
||||
else if( ldbits == 128 && std::numeric_limits<long double>::digits == 64 )
|
||||
{
|
||||
BOOST_TEST_EQ( hv(1.0L), 18308860000934227808ULL );
|
||||
BOOST_TEST_EQ( hv(-1.0L), 18308860000934260576ULL );
|
||||
BOOST_TEST_EQ( hv(3.14L), 13482288377848558187ULL );
|
||||
BOOST_TEST_EQ( hv(-3.14L), 13482288377848590955ULL );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), 18308860000934244192ULL );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), 18308860000934276960ULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
// ldbits == 128 && std::numeric_limits<long double>::digits == 113
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if SIZE_MAX == 4294967295U
|
||||
|
||||
BOOST_TEST_EQ( hv(1.0L), 3770520689U );
|
||||
BOOST_TEST_EQ( hv(-1.0L), 3770553457U );
|
||||
BOOST_TEST_EQ( hv(3.14L), 1150018772U );
|
||||
BOOST_TEST_EQ( hv(-3.14L), 1150051540U );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), 3770537073U );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), 3770569841U );
|
||||
|
||||
#else
|
||||
|
||||
BOOST_TEST_EQ( hv(1.0L), 18308860000934227808ULL );
|
||||
BOOST_TEST_EQ( hv(-1.0L), 18308860000934260576ULL );
|
||||
BOOST_TEST_EQ( hv(3.14L), 13482288377848558187ULL );
|
||||
BOOST_TEST_EQ( hv(-3.14L), 13482288377848590955ULL );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), 18308860000934244192ULL );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), 18308860000934276960ULL );
|
||||
|
||||
#endif
|
||||
BOOST_TEST_EQ( hv(1.0L), 4611404543450677248ULL );
|
||||
BOOST_TEST_EQ( hv(-1.0L), 13834776580305453056ULL );
|
||||
BOOST_TEST_EQ( hv(3.14L), 5967435363179612952ULL );
|
||||
BOOST_TEST_EQ( hv(-3.14L), 15190807400034388760ULL );
|
||||
BOOST_TEST_EQ( hv(std::numeric_limits<long double>::infinity()), 9223090561878065152ULL );
|
||||
BOOST_TEST_EQ( hv(-std::numeric_limits<long double>::infinity()), 18446462598732840960ULL );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -267,19 +284,19 @@ int main()
|
||||
// string
|
||||
#if SIZE_MAX == 4294967295U
|
||||
|
||||
BOOST_TEST_EQ( hv(std::string()), 1580013426U );
|
||||
BOOST_TEST_EQ( hv(std::string("abc")), 469308065U );
|
||||
BOOST_TEST_EQ( hv(std::string("\0", 1)), 165258820U );
|
||||
BOOST_TEST_EQ( hv(std::string("\0\0", 2)), 4017288109U );
|
||||
BOOST_TEST_EQ( hv(std::string("\0\0\0", 3)), 1352445396U );
|
||||
BOOST_TEST_EQ( hv(std::string()), 1868390524U );
|
||||
BOOST_TEST_EQ( hv(std::string("abc")), 3674866719U );
|
||||
BOOST_TEST_EQ( hv(std::string("\0", 1)), 1965885047U );
|
||||
BOOST_TEST_EQ( hv(std::string("\0\0", 2)), 54340706U );
|
||||
BOOST_TEST_EQ( hv(std::string("\0\0\0", 3)), 688730713U );
|
||||
|
||||
#else
|
||||
|
||||
BOOST_TEST_EQ( hv(std::string()), 2220755840493918647ULL );
|
||||
BOOST_TEST_EQ( hv(std::string("abc")), 7565583854499162206ULL );
|
||||
BOOST_TEST_EQ( hv(std::string("\0", 1)), 1241131678047372712ULL );
|
||||
BOOST_TEST_EQ( hv(std::string("\0\0", 2)), 152341731040131640ULL );
|
||||
BOOST_TEST_EQ( hv(std::string("\0\0\0", 3)), 12957252994983528908ULL );
|
||||
BOOST_TEST_EQ( hv(std::string()), 2060355526954642342ULL );
|
||||
BOOST_TEST_EQ( hv(std::string("abc")), 2539195663733406973ULL );
|
||||
BOOST_TEST_EQ( hv(std::string("\0", 1)), 18432168439372857722ULL );
|
||||
BOOST_TEST_EQ( hv(std::string("\0\0", 2)), 6494115972580589074ULL );
|
||||
BOOST_TEST_EQ( hv(std::string("\0\0\0", 3)), 4419026507380069870ULL );
|
||||
|
||||
#endif
|
||||
|
||||
@@ -327,10 +344,10 @@ int main()
|
||||
|
||||
#if SIZE_MAX == 4294967295U
|
||||
|
||||
BOOST_TEST_EQ( hv(std::complex<double>(+1.0, 0.0)), 1072693248U );
|
||||
BOOST_TEST_EQ( hv(std::complex<double>(-1.0, 0.0)), 3220176896U );
|
||||
BOOST_TEST_EQ( hv(std::complex<double>(0.0, +1.0)), 2619008688U );
|
||||
BOOST_TEST_EQ( hv(std::complex<double>(0.0, -1.0)), 146497060U );
|
||||
BOOST_TEST_EQ( hv(std::complex<double>(+1.0, 0.0)), 2619008688U );
|
||||
BOOST_TEST_EQ( hv(std::complex<double>(-1.0, 0.0)), 146497060U );
|
||||
BOOST_TEST_EQ( hv(std::complex<double>(0.0, +1.0)), 22395692U );
|
||||
BOOST_TEST_EQ( hv(std::complex<double>(0.0, -1.0)), 1449221192U );
|
||||
|
||||
#else
|
||||
|
||||
@@ -359,17 +376,17 @@ int main()
|
||||
// vector<char>
|
||||
#if SIZE_MAX == 4294967295U
|
||||
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(0)), 1580013426U );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(1)), 165258820U );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(2)), 4017288109U );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(3)), 1352445396U );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(0)), 1868390524U );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(1)), 1965885047U );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(2)), 54340706U );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(3)), 688730713U );
|
||||
|
||||
#else
|
||||
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(0)), 2220755840493918647ULL );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(1)), 1241131678047372712ULL );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(2)), 152341731040131640ULL );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(3)), 12957252994983528908ULL );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(0)), 2060355526954642342ULL );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(1)), 18432168439372857722ULL );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(2)), 6494115972580589074ULL );
|
||||
BOOST_TEST_EQ( hv(std::vector<char>(3)), 4419026507380069870ULL );
|
||||
|
||||
#endif
|
||||
|
||||
@@ -410,17 +427,17 @@ int main()
|
||||
// list<char>
|
||||
#if SIZE_MAX == 4294967295U
|
||||
|
||||
BOOST_TEST_EQ( hv(std::list<char>(0)), 1580013426U );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(1)), 165258820U );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(2)), 4017288109U );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(3)), 1352445396U );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(0)), 1868390524U );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(1)), 1965885047U );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(2)), 54340706U );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(3)), 688730713U );
|
||||
|
||||
#else
|
||||
|
||||
BOOST_TEST_EQ( hv(std::list<char>(0)), 2220755840493918647ULL );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(1)), 1241131678047372712ULL );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(2)), 152341731040131640ULL );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(3)), 12957252994983528908ULL );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(0)), 2060355526954642342ULL );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(1)), 18432168439372857722ULL );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(2)), 6494115972580589074ULL );
|
||||
BOOST_TEST_EQ( hv(std::list<char>(3)), 4419026507380069870ULL );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
204
test/hash_tuple_like_test.cpp
Normal file
204
test/hash_tuple_like_test.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/type_traits/enable_if.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <utility>
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace user
|
||||
{
|
||||
|
||||
struct Y1
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
template<std::size_t I> int& get( Y1& v );
|
||||
template<std::size_t I> int const& get( Y1 const& v );
|
||||
|
||||
template<> int& get<0>( Y1& v )
|
||||
{
|
||||
return v.a;
|
||||
}
|
||||
|
||||
template<> int const& get<0>( Y1 const& v )
|
||||
{
|
||||
return v.a;
|
||||
}
|
||||
|
||||
template<> int& get<1>( Y1& v )
|
||||
{
|
||||
return v.b;
|
||||
}
|
||||
|
||||
template<> int const& get<1>( Y1 const& v )
|
||||
{
|
||||
return v.b;
|
||||
}
|
||||
|
||||
struct Y2
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
|
||||
template<class T> friend
|
||||
typename boost::enable_if_<boost::is_same<T, Y2>::value, std::size_t>::type
|
||||
hash_value( T const& v )
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
boost::hash_combine( seed, v.a );
|
||||
boost::hash_combine( seed, v.b );
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace user
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<> struct tuple_size<user::Y1>: std::integral_constant<std::size_t, 2>
|
||||
{
|
||||
};
|
||||
|
||||
template<> struct tuple_size<user::Y2>: std::integral_constant<std::size_t, 2>
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<> struct is_tuple_like<user::Y2>: boost::false_type {};
|
||||
|
||||
} // namespace container_hash
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
|
||||
template<class T> std::size_t hv( T const& t )
|
||||
{
|
||||
return boost::hash<T>()( t );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
std::pair<int, int> tp( 1, 2 );
|
||||
int const a[] = { 1, 2 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
|
||||
{
|
||||
std::tuple<> tp;
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), 0u );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int> tp( 1 );
|
||||
int const a[] = { 1 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int, int> tp( 1, 2 );
|
||||
int const a[] = { 1, 2 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int, int, int> tp( 1, 2, 3 );
|
||||
int const a[] = { 1, 2, 3 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int, int, int, int> tp( 1, 2, 3, 4 );
|
||||
int const a[] = { 1, 2, 3, 4 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int, int, int, int, int> tp( 1, 2, 3, 4, 5 );
|
||||
int const a[] = { 1, 2, 3, 4, 5 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int, int, int, int, int, int> tp( 1, 2, 3, 4, 5, 6 );
|
||||
int const a[] = { 1, 2, 3, 4, 5, 6 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int, int, int, int, int, int, int> tp( 1, 2, 3, 4, 5, 6, 7 );
|
||||
int const a[] = { 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int, int, int, int, int, int, int, int> tp( 1, 2, 3, 4, 5, 6, 7, 8 );
|
||||
int const a[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
{
|
||||
std::tuple<int, int, int, int, int, int, int, int, int> tp( 1, 2, 3, 4, 5, 6, 7, 8, 9 );
|
||||
int const a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1800)
|
||||
|
||||
{
|
||||
user::Y1 tp = { 1, 2 };
|
||||
int const a[] = { 1, 2 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
{
|
||||
user::Y2 tp = { 1, 2 };
|
||||
int const a[] = { 1, 2 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
78
test/hash_tuple_like_test2.cpp
Normal file
78
test/hash_tuple_like_test2.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/describe/class.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
#include <utility>
|
||||
|
||||
#if defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE( "Skipping test because BOOST_NO_CXX11_HDR_TUPLE is defined" )
|
||||
int main() {}
|
||||
|
||||
#elif !defined(BOOST_DESCRIBE_CXX14)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE( "Skipping test because BOOST_DESCRIBE_CXX14 is not defined" )
|
||||
int main() {}
|
||||
|
||||
#else
|
||||
|
||||
namespace user
|
||||
{
|
||||
|
||||
struct Y3
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT(Y3, (), (a, b))
|
||||
|
||||
} // namespace user
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<> struct tuple_size<user::Y3>: std::integral_constant<std::size_t, 2>
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<> struct is_tuple_like<user::Y3>: boost::false_type {};
|
||||
|
||||
} // namespace container_hash
|
||||
} // namespace boost
|
||||
|
||||
template<class T> std::size_t hv( T const& t )
|
||||
{
|
||||
return boost::hash<T>()( t );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
user::Y3 tp = { 1, 2 };
|
||||
int const a[] = { 1, 2 };
|
||||
|
||||
BOOST_TEST_EQ( hv(tp), hv(a) );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -50,11 +50,11 @@ int main()
|
||||
BOOST_TEST_TRAIT_TRUE((is_contiguous_range<std::wstring>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_contiguous_range<std::wstring const>));
|
||||
|
||||
// std::vector doesn't have data() in C++03
|
||||
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700)
|
||||
BOOST_TEST_TRAIT_TRUE((is_contiguous_range< std::vector<X> >));
|
||||
BOOST_TEST_TRAIT_TRUE((is_contiguous_range< std::vector<X> const >));
|
||||
#endif
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_contiguous_range< std::vector<bool> >));
|
||||
BOOST_TEST_TRAIT_FALSE((is_contiguous_range< std::vector<bool> const >));
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_contiguous_range< std::deque<X> >));
|
||||
BOOST_TEST_TRAIT_FALSE((is_contiguous_range< std::deque<X> const >));
|
||||
|
||||
@@ -50,7 +50,7 @@ int main()
|
||||
{
|
||||
using boost::container_hash::is_contiguous_range;
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700)
|
||||
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_GCC, < 40700) && !BOOST_WORKAROUND(BOOST_MSVC, < 1910)
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((is_contiguous_range<X1>));
|
||||
|
||||
|
||||
25
test/is_contiguous_range_test3.cpp
Normal file
25
test/is_contiguous_range_test3.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/is_contiguous_range.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
struct X1
|
||||
{
|
||||
char const* begin() const;
|
||||
char const* end() const;
|
||||
std::size_t size() const;
|
||||
|
||||
char data[16];
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::container_hash::is_contiguous_range;
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_contiguous_range<X1>));
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
56
test/is_described_class_test.cpp
Normal file
56
test/is_described_class_test.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/is_described_class.hpp>
|
||||
#include <boost/describe/class.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <string>
|
||||
|
||||
struct X1
|
||||
{
|
||||
};
|
||||
|
||||
struct X2
|
||||
{
|
||||
int m;
|
||||
};
|
||||
|
||||
struct Y1
|
||||
{
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT( Y1, (), () )
|
||||
|
||||
struct Y2: Y1
|
||||
{
|
||||
int m;
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT( Y2, (Y1), (m) )
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::container_hash::is_described_class;
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<void>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<int>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<X1>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<X2>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<int[2]>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<std::string>));
|
||||
|
||||
#if defined(BOOST_DESCRIBE_CXX14)
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((is_described_class<Y1>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_described_class<Y2>));
|
||||
|
||||
#else
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<Y1>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<Y2>));
|
||||
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
35
test/is_described_class_test2.cpp
Normal file
35
test/is_described_class_test2.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/type_traits/integral_constant.hpp>
|
||||
#include <boost/describe/class.hpp>
|
||||
|
||||
struct X
|
||||
{
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT( X, (), () )
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace container_hash
|
||||
{
|
||||
|
||||
template<class T> struct is_described_class;
|
||||
template<> struct is_described_class<X>: boost::false_type {};
|
||||
|
||||
} // namespace container_hash
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/container_hash/is_described_class.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::container_hash::is_described_class;
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<X>));
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
31
test/is_described_class_test3.cpp
Normal file
31
test/is_described_class_test3.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/is_described_class.hpp>
|
||||
#include <boost/describe/class.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
|
||||
union Y1
|
||||
{
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT( Y1, (), () )
|
||||
|
||||
union Y2
|
||||
{
|
||||
int m1;
|
||||
float m2;
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT( Y2, (), (m1, m2) )
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::container_hash::is_described_class;
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<Y1>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_described_class<Y2>));
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
28
test/is_range_test4.cpp
Normal file
28
test/is_range_test4.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2024 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/container_hash/is_range.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
|
||||
struct X1
|
||||
{
|
||||
void* begin() const;
|
||||
void* end() const;
|
||||
};
|
||||
|
||||
struct X2
|
||||
{
|
||||
void const* begin() const;
|
||||
void const* end() const;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::container_hash::is_range;
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_range<X1>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_range<X2>));
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
107
test/is_tuple_like_test.cpp
Normal file
107
test/is_tuple_like_test.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2017, 2022 Peter Dimov.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
|
||||
#include <boost/container_hash/is_tuple_like.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <utility>
|
||||
|
||||
struct X
|
||||
{
|
||||
};
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace user
|
||||
{
|
||||
|
||||
struct Y
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
template<std::size_t I> int& get( Y& v );
|
||||
template<std::size_t I> int const& get( Y const& v );
|
||||
|
||||
template<> int& get<0>( Y& v )
|
||||
{
|
||||
return v.a;
|
||||
}
|
||||
|
||||
template<> int const& get<0>( Y const& v )
|
||||
{
|
||||
return v.a;
|
||||
}
|
||||
|
||||
template<> int& get<1>( Y& v )
|
||||
{
|
||||
return v.b;
|
||||
}
|
||||
|
||||
template<> int const& get<1>( Y const& v )
|
||||
{
|
||||
return v.b;
|
||||
}
|
||||
|
||||
} // namespace user
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<> struct tuple_size<user::Y>: std::integral_constant<std::size_t, 2>
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // #if !defined(BOOST_NO_CXX11_HDR_TUPLE)
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::container_hash::is_tuple_like;
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_tuple_like<void>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_tuple_like<void const>));
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_tuple_like<int>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_tuple_like<int const>));
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_tuple_like<X>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_tuple_like<X const>));
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((is_tuple_like<int[2]>));
|
||||
BOOST_TEST_TRAIT_FALSE((is_tuple_like<int const [2]>));
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_HDR_TUPLE) && !BOOST_WORKAROUND(BOOST_MSVC, <= 1800)
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::pair<int, X> >));
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::pair<int, X> const>));
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::tuple<> >));
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::tuple<> const>));
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::tuple<X> >));
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::tuple<X> const>));
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::tuple<X, X> >));
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::tuple<X, X> const>));
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::tuple<X, X, X> >));
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like< std::tuple<X, X, X> const>));
|
||||
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like<user::Y>));
|
||||
BOOST_TEST_TRAIT_TRUE((is_tuple_like<user::Y const>));
|
||||
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
Reference in New Issue
Block a user