forked from boostorg/container_hash
Compare commits
25 Commits
feature/as
...
feature/ha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
194
.drone.jsonnet
Normal file
194
.drone.jsonnet
Normal file
@@ -0,0 +1,194 @@
|
||||
# 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',
|
||||
'wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -',
|
||||
] +
|
||||
(if sources != [] then [ ('apt-add-repository "' + source + '"') for source in sources ] else []) +
|
||||
(if packages != "" then [ 'apt-get update', 'apt-get -y install ' + packages ] else []) +
|
||||
[
|
||||
'export LIBRARY=' + library,
|
||||
'./.drone/drone.sh',
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
local macos_pipeline(name, environment, xcode_version = "12.2", osx_version = "catalina", arch = "amd64") =
|
||||
{
|
||||
name: name,
|
||||
kind: "pipeline",
|
||||
type: "exec",
|
||||
trigger: triggers,
|
||||
platform: {
|
||||
"os": "darwin",
|
||||
"arch": arch
|
||||
},
|
||||
node: {
|
||||
"os": osx_version
|
||||
},
|
||||
steps: [
|
||||
{
|
||||
name: "everything",
|
||||
environment: environment + { "DEVELOPER_DIR": "/Applications/Xcode-" + xcode_version + ".app/Contents/Developer" },
|
||||
commands:
|
||||
[
|
||||
'export LIBRARY=' + library,
|
||||
'./.drone/drone.sh',
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
local windows_pipeline(name, image, environment, arch = "amd64") =
|
||||
{
|
||||
name: name,
|
||||
kind: "pipeline",
|
||||
type: "docker",
|
||||
trigger: triggers,
|
||||
platform:
|
||||
{
|
||||
os: "windows",
|
||||
arch: arch
|
||||
},
|
||||
"steps":
|
||||
[
|
||||
{
|
||||
name: "everything",
|
||||
image: image,
|
||||
environment: environment,
|
||||
commands:
|
||||
[
|
||||
'cmd /C .drone\\\\drone.bat ' + library,
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
[
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.4",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.4', CXXSTD: '98,0x' },
|
||||
"g++-4.4",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.6 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.6', CXXSTD: '98,0x', ADDRMD: '32,64' },
|
||||
"g++-4.6-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.7 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.7', CXXSTD: '98,0x', ADDRMD: '32,64' },
|
||||
"g++-4.7-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.8* 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11', ADDRMD: '32,64' },
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 14.04 GCC 4.9 32/64",
|
||||
"cppalliance/droneubuntu1404:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '03,11', ADDRMD: '32,64' },
|
||||
"g++-4.9-multilib",
|
||||
[ "ppa:ubuntu-toolchain-r/test" ],
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9 ARM64",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' },
|
||||
arch="arm64",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 20.04 GCC 9 S390x",
|
||||
"cppalliance/droneubuntu2004:multiarch",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' },
|
||||
arch="s390x",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 GCC 12 32 ASAN",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14,17,20', ADDRMD: '32' } + asan,
|
||||
"g++-12-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 GCC 12 64 ASAN",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14,17,20', ADDRMD: '64' } + asan,
|
||||
"g++-12-multilib",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 14 UBSAN",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20' } + ubsan,
|
||||
"clang-14",
|
||||
),
|
||||
|
||||
linux_pipeline(
|
||||
"Linux 22.04 Clang 14 ASAN",
|
||||
"cppalliance/droneubuntu2204:1",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20' } + asan,
|
||||
"clang-14",
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 10.15 Xcode 12.2 UBSAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + ubsan,
|
||||
),
|
||||
|
||||
macos_pipeline(
|
||||
"MacOS 10.15 Xcode 12.2 ASAN",
|
||||
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + asan,
|
||||
),
|
||||
|
||||
windows_pipeline(
|
||||
"Windows VS2017 msvc-14.1",
|
||||
"cppalliance/dronevs2017",
|
||||
{ TOOLSET: 'msvc-14.1', CXXSTD: '14,17,latest' },
|
||||
),
|
||||
]
|
||||
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
|
||||
25
.drone/drone.sh
Executable file
25
.drone/drone.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2022 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
set -ex
|
||||
|
||||
DRONE_BUILD_DIR=$(pwd)
|
||||
|
||||
BOOST_BRANCH=develop
|
||||
if [ "$DRONE_BRANCH" = "master" ]; then BOOST_BRANCH=master; fi
|
||||
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
git submodule update --init tools/boostdep
|
||||
cp -r $DRONE_BUILD_DIR/* libs/$LIBRARY
|
||||
python tools/boostdep/depinst/depinst.py -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}
|
||||
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).
|
||||
@@ -18,11 +18,12 @@ 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/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`
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ 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
|
||||
|
||||
@@ -39,8 +39,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.
|
||||
|
||||
219
doc/hash/notes.adoc
Normal file
219
doc/hash/notes.adoc
Normal file
@@ -0,0 +1,219 @@
|
||||
////
|
||||
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_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 principle, when `size_t` is 64 bit, we could have used `uint64_t` instead.
|
||||
We do not, because this allows producing an arbitrary hash value by choosing
|
||||
the input bytes appropriately (because `hash_combine` is reversible.)
|
||||
|
||||
Allowing control only over 32 bits of the full 64 bit `size_t` value makes
|
||||
these "chosen plaintext attacks" harder.
|
||||
|
||||
This is not as harmful to performance as it first appears, because the
|
||||
input to `hash<string>` (e.g. the key in an unordered container) is often
|
||||
short (9 to 13 bytes in some typical scenarios.)
|
||||
|
||||
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).
|
||||
29
doc/hash/recent.adoc
Normal file
29
doc/hash/recent.adoc
Normal file
@@ -0,0 +1,29 @@
|
||||
////
|
||||
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.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.
|
||||
* `hash_combine` has been improved.
|
||||
* 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.
|
||||
@@ -142,9 +142,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 +159,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 +223,23 @@ 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 in an unspecified manner
|
||||
and then passed to `hash_combine`, more than one at a time. This is done in
|
||||
order to improve performance when hashing strings.
|
||||
--
|
||||
|
||||
[source]
|
||||
----
|
||||
template<class It> std::size_t hash_range( It first, It last );
|
||||
@@ -226,7 +250,7 @@ Effects: ::
|
||||
[source]
|
||||
----
|
||||
size_t seed = 0;
|
||||
hash_range( seed, first, last );
|
||||
boost::hash_range( seed, first, last );
|
||||
return seed;
|
||||
----
|
||||
|
||||
@@ -238,7 +262,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 +276,7 @@ Effects: ::
|
||||
[source]
|
||||
----
|
||||
size_t seed = 0;
|
||||
hash_unordered_range( seed, first, last );
|
||||
boost::hash_unordered_range( seed, first, last );
|
||||
return seed;
|
||||
----
|
||||
|
||||
@@ -282,7 +306,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 +339,7 @@ template<class T, std::size_t N>
|
||||
----
|
||||
|
||||
Returns: ::
|
||||
`hash_range( v, v + N )`.
|
||||
`boost::hash_range( v, v + N )`.
|
||||
|
||||
[source]
|
||||
----
|
||||
@@ -384,7 +408,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 +426,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,7 +440,7 @@ 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
|
||||
@@ -432,7 +456,7 @@ template<class T, class D>
|
||||
----
|
||||
|
||||
Returns: ::
|
||||
`hash<T*>( v.get() )`.
|
||||
`boost::hash<T*>( v.get() )`.
|
||||
|
||||
[source]
|
||||
----
|
||||
@@ -468,7 +492,7 @@ template<class T>
|
||||
|
||||
Returns: ::
|
||||
For a disengaged `v`, an unspecified constant value; otherwise,
|
||||
`hash<T>()( *v )`.
|
||||
`boost::hash<T>()( *v )`.
|
||||
|
||||
[source]
|
||||
----
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -27,6 +27,14 @@ 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 {};
|
||||
|
||||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||
template<> struct is_char_type<char8_t>: public boost::true_type {};
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||
template<> struct is_char_type<std::byte>: public boost::true_type {};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
template<class It>
|
||||
|
||||
@@ -176,12 +176,11 @@ 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 )
|
||||
{
|
||||
@@ -193,35 +192,19 @@ 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::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 )
|
||||
{
|
||||
@@ -230,32 +213,15 @@ namespace boost
|
||||
|
||||
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 )
|
||||
{
|
||||
@@ -264,32 +230,15 @@ namespace boost
|
||||
|
||||
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 )
|
||||
{
|
||||
@@ -298,27 +247,17 @@ namespace boost
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -640,6 +579,4 @@ namespace boost
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef BOOST_FUNCTIONAL_HASH_ROTL32
|
||||
|
||||
#endif // #ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user