Overview
This library implements an extensible framework for implementing hashing algorithms that can support user-defined types. Its structure is largely based on the paper "Types don’t know #" by Howard Hinnant, Vinnie Falco and John Bytheway.
The key feature of the design is the clean separation between the hash
algorithm, which takes an untyped stream of bytes (a message) and produces
a hash value (a message digest), and the hash_append
function, which takes
a type and is responsible for turning the value of this type into a
sequence of bytes and feeding them to a hash algorithm.
This allows hashing support for user-defined types to be written once, and then automatically being usable with any hash algorithms, even such that weren’t yet available at the time the type was defined.
The following popular hashing algorithms are provided:
but it’s also possible for users to write their own; as long as the
hash algorithm conforms to the concept, hash_append
will work with it,
and so will all user-defined types that support hash_append
.
Hashing Byte Sequences
This library addresses two major use cases: hashing an untyped sequence of bytes, and hashing C++ objects.
Untyped byte sequences (also called messages) are hashed by passing them to a hash algorithm, which then produces a hash value (or a message digest).
The same hash algorithm, when passed the same message, will always produce the same digest. (Published algorithms provide message and corresponding digest pairs, called test vectors, to enable verification of independent implementations.)
(To hash a C++ object, it’s first converted (serialized) to a sequence of bytes, then passed to a hash algorithm.)
Hash Algorithm Requirements
A hash algorithm must have the following structure, and meet the following minimum requirements:
struct HashAlgorithm
{
using result_type = /*integral or array-like*/;
static constexpr int block_size = /*...*/; // optional
HashAlgorithm();
explicit HashAlgorithm( std::uint64_t seed );
HashAlgorithm( unsigned char const* seed, std::size_t n );
HashAlgorithm( HashAlgorithm const& r );
HashAlgorithm& operator=( HashAlgorithm const& r );
void update( void const* data, std::size_t n );
result_type result();
};
result_type
The nested type result_type
is the type of the produced hash value. It
can be an unsigned integer type (that is not bool
), typically
std::uint32_t
or std::uint64_t
, or a std::array
-like type with a
value type of unsigned char
.
Normally, non-cryptographic hash functions have an integer result_type
,
and cryptographic hash functions have an array-like result_type
, but
that’s not required.
The provided utility function get_integral_result
can be used to obtain
an integer hash value from any valid result_type
.
block_size
Cryptographic hash functions provide a block_size
value, which is their
block size (e.g. 64 for MD5, 128 for SHA2-512) and is required in order
to implement the corresponding HMAC.
block_size
is an optional requirement.
Default Constructor
All hash algorithms must be default constructible. The default constructor initializes the internal state of the hash algorithm to its initial values, as published in its specification.
For example, the default constructor of md5_128
corresponds to calling
the function MD5_Init
of the reference implementation.
Constructor Taking an Integer Seed
All hash algorithms must be constructible from a value of type
std::uint64_t
, which serves as a seed.
Using a seed value of 0 is equivalent to default construction.
Distinct seed values cause the internal state to be initialized differently, and therefore, instances of the hash algorithm initialized by different seeds produce different hash values when passed the same message.
Seeding using random (unobservable from the outside) values is useful for preventing hash flooding attacks.
Constructor Taking a Byte Sequence Seed
All hash algorithms can be constructed from a seed sequence of
unsigned char
values (this makes all hash algorithms
keyed hash functions.)
A null sequence (one with length 0) produces a default-constructed instance.
Different seed sequences produce differently initialized instances.
While this requirement makes all hash algorithms usable as MACs (Message Authentication Codes), you should as a general rule prefer an established MAC algorithm, such as HMAC. (A HMAC implementation is provided.)
Copy Constructor, Copy Assignment
Hash algorithms are copy constructible and copy assignable, providing the usual guarantees for these operations. That is, a copy is equivalent to the original.
update
The function update
is the mechanism by which the input message is provided
to the hash algorithm.
Calling update
several times is equivalent to calling it once with the
concatenated byte sequences from the individual calls. That is, the input
message may be provided in parts, and the way it’s split into parts does
not matter and does not affect the final hash value.
Given
Hash hash; // some hash algorithm
unsigned char message[6] = { /*...*/ }; // some input message
the following update
call
hash.update( message, 6 );
is equivalent to
hash.update( message, 4 );
hash.update( message + 4, 2 );
and to
for( int i = 0; i < 6; ++i ) hash.update( &message[i], 1 );
result
After the entire input message has been provided via calls to update
, the
final hash value can be obtained by calling result
.
The call to result
finalizes the internal state, by padding the message as
per the concrete algorithm specification, by optionally incorporating the
length of the message into the state, and by performing finalization
operations on the state, again as specified by the concrete algorithm.
A final hash value is then obtained by transforming the internal state, and returned.
Note that result
is non-const, because it changes the internal state. It’s
allowed for result
to be called more than once; subsequent calls perform
the state finalization again and as a result produce a pseudorandom sequence
of result_type
values. This can be used to effectively extend the output of
the hash function. For example, a 256 bit result can be obtained from a hash
algorithm whose result_type
is 64 bit, by calling result
four times.
As a toy example, not intended for production use, this is how one could write a random number generator on top of the FNV-1a implementation provided by the library:
std::uint64_t random()
{
static boost::hash2::fnv1a_64 hash;
return hash.result();
}
Compile Time Hashing
Under C++14, it’s possible to invoke some hash algorithms at compile time. These algorithms provide the following interface:
struct HashAlgorithm
{
using result_type = /*integral or array-like*/;
static constexpr int block_size = /*...*/; // optional
constexpr HashAlgorithm();
explicit constexpr HashAlgorithm( std::uint64_t seed );
constexpr HashAlgorithm( unsigned char const* seed, std::size_t n );
constexpr HashAlgorithm( HashAlgorithm const& r );
constexpr HashAlgorithm& operator=( HashAlgorithm const& r );
void update( void const* data, std::size_t n );
constexpr void update( unsigned char const* data, std::size_t n );
constexpr result_type result();
};
Apart from the added constexpr
qualifiers, the only difference is that
update
has a second overload that takes unsigned char const*
instead
of void const*
. (Pointers to void
cannot be used in constexpr
functions
before C++26.)
Provided Hash Algorithms
FNV-1a
The Fowler-Noll-Vo hash function
is provided as a representative of the class of hash functions that process
their input one byte at a time. The 32 or 64 bit state is updated for each
input character ch
by using the operation state = (state ^ ch) * fnv_prime
.
FNV-1a is non-cryptographic, relatively weak compared to state of the art hash functions (although good for its class), but fast when the input strings are short.
xxHash
xxHash is a fast non-cryptographic hashing algorithm by Yann Collet.
Its speed (~5GB/s for xxhash_32
, ~10GB/s for xxhash_64
on a Xeon E5-2683 v4 @ 2.10GHz)
makes it well suited for quick generation of file or data integrity checksums.
SipHash
SipHash by Jean-Philippe Aumasson and Daniel J. Bernstein (paper) has been designed to thwart hash flooding attacks against hash tables that receive external untrusted input (e.g. HTTP message headers, or JSON objects.)
It’s not a cryptographic hash function (even though its design is similar to one), because it does not provide collision resistance when the initial seed is known.
It is, however, a cryptographically strong keyed hash function (or a pseudorandom function, PRF). If the initial seed is unknown to the attacker, it’s computationally difficult to engineer a collision, or to recover the seed by observing the output.
SipHash has been adopted as the de-facto standard hash function for hash tables that can be exposed to external input, and is used in Python, Perl, Ruby, Rust, and other languages.
SipHash is the recommended hash function for hash tables exposed to external input. As a best practice, it should be seeded with a random value that varies per connection, and not a fixed one per process.
MD5
Designed in 1991 by Ron Rivest, MD5 used to be the best known and the most widely used cryptographic hash function, but has been broken and is no longer considered cryptographic for any purposes. It produces a 128 bit digest.
MD5 should no longer be used in new code when cryptographic strength is required, except when implementing an existing specification or protocol that calls for its use.
Prefer SHA2-512/256 (or SHA2-256 in 32 bit code) instead.
If you require a digest of exactly 128 bits, use RIPEMD-128 instead. Do note that 128 bit digests are no longer considered cryptographic, because attacks with a complexity of 264 are within the capabilities of well-funded attackers.
SHA-1
SHA-1 is a cryptographic hash function that was designed by NSA and published in 1995 by NIST as a Federal Information Processing Standard (FIPS). It produces a 160 bit digest.
SHA-1 is now considered insecure against a well-funded attacker, and should no longer be used in new code. Prefer SHA2-512/256, SHA2-256 in 32 bit code, or, if you require a digest of exactly 160 bits, RIPEMD-160 instead.
SHA-2
SHA-2 is a family of cryptographic hash functions, also designed by NSA, initially published by NIST in 2002, and updated in 2015. It includes SHA2-224, SHA2-256, SHA2-384, SHA2-512, SHA2-512/224, and SHA2-512/256, each producing a digest with the corresponding bit length.
Of these, SHA2-256 and SHA2-512 are the basis ones, and the rest are variants with the digest truncated.
The SHA-2 functions haven’t been broken and are in wide use, despite the existence of a newer standard (SHA-3).
SHA2-256 and its truncated variant SHA2-224 use 32 bit operations and therefore do not lose performance on a 32 bit platform.
SHA2-512 and its truncated variants SHA2-384, SHA2-512/224, and SHA2-512/256 use 64 bit operations and are approximately 1.5 times as fast as SHA2-256 on a 64 bit platform, but twice as slow in 32 bit code.
On 64 bit platforms, SHA2-512/256 and SHA2-512/224 should be preferred over SHA2-256 and SHA2-224 not just because of speed, but because they are resistant to length extension attacks as they don’t expose all of the bits of their internal state in the final digest.
RIPEMD-160, RIPEMD-128
Designed in 1996, RIPEMD-160 is a cryptographic hash function that was less well known than MD5 and SHA-1, but that has recently become popular because of its use in Bitcoin and other cryptocurrencies.
Even though it hasn’t been broken, there’s no reason to prefer its use in new code over SHA-2.
RIPEMD-128 is a truncated variant of RIPEMD-160. (Do note that 128 bit digests are no longer considered cryptographic, because attacks with a complexity of 264 are within the capabilities of well-funded attackers.)
HMAC
HMAC (Hash-based Message Authentication Code) is an algorithm for deriving a message authentication code by using a cryptographic hash function. It’s described in RFC 2104.
A message authentication code differs from a digest by the fact that it depends on both the contents of the message and on a secret key; in contrast, a message digest only depends on the contents of the message.
Even though all hash algorithms provided by the library can be used to produce message authentication codes, by means of seeding the hash algorithm initially with a secret key by calling the constructor taking a byte sequence, hash algorithms have usually not been designed to be used in this manner, and such use hasn’t been cryptographically analyzed and vetted. (SipHash is an exception; it has specifically been designed as a MAC.)
The HMAC algorithm is provided in the form of a class template hmac<H>
that adapts a cryptographic hash
algorithm H
. hmac<H>
satisfies the requirements of a cryptographic hash algorithm.
Convenience aliases of common HMAC instantiations are provided. For example, the md5.hpp
header defining
md5_128
also defines hmac_md5_128
as an alias to hmac<md5_128>
.
Choosing a Hash Algorithm
If your use case requires cryptographic strength, use SHA2-512/256 (or SHA2-256 in 32 bit code) for digests, and the corresponding HMAC for message authentication codes.
Note
|
Digests of fewer than 256 bits in length are no longer recommended when cryptographic security is required or desired. |
For computing file or content checksums, when speed is of the essence and externally induced collisions aren’t a concern, use xxHash-64.
Note
|
xxHash-32 will be faster in 32 bit code, but since it only produces a 32 bit result, collisions will become an issue when the number of items reaches tens of thousands, which is usually unacceptable. |
For a large number of items (many millions), 64 bits may not be enough; in that case, use either MD5, or xxHash-64, extended to 128 bits.
Note
|
Even though MD5 is no longer cryptographically secure, it can still be used when cryptographic strength is not a requirement. |
For hash tables, use SipHash by default, with a random (unpredictable from the outside) seed that varies per connection or per container. Avoid using a fixed processwide seed. Never use SipHash without a seed.
For hash tables with very short keys (3-4-5 bytes), unexposed to external input, you can use FNV-1a, although the default hash function of e.g. boost::unordered_flat_map
will typically perform better.
Hashing C++ Objects
The traditional approach to hashing C++ objects is to make
them responsible for providing a hash value. The standard,
for instance, follows this by making it the responsibility
of each type T
to implement a specialization of std::hash<T>
,
which when invoked with a value returns its size_t
hash.
This, of course, means that the specific hash algorithm varies per type and is, in the general case, completely opaque.
This library takes a different approach; the hash algorithm is known and chosen by the user. A C++ object is hashed by first being converted to a sequence of bytes representing its value (a message) which is then passed to the hash algorithm.
The conversion must obey the following requirements:
-
Equal objects must produce the same message;
-
Different objects should produce different messages;
-
An object should always produce a non-empty message.
The first two requirements follow directly from the hash value
requirements, whereas the third one is a bit more subtle and
is intended to prevent things like the distinct sequences
[[1], [], []]
and [[], [1], []]
producing the same message.
(This is similar to the requirement that all C++ objects have
sizeof
that is not zero, including empty ones.)
In this library, the conversion is performed by the function
hash_append
. It’s declared as follows:
template<class Hash, class Flavor = default_flavor, class T>
constexpr void hash_append( Hash& h, Flavor const& f, T const& v );
and the effect of invoking hash_append(h, f, v)
is to call
h.update(p, n)
one or more times (but never zero times.) The
combined result of these calls forms the message corresponding
to v
.
hash_append
handles natively the following types T
:
-
Integral types (signed and unsigned integers, character types,
bool
); -
Floating point types (
float
anddouble
); -
Enumeration types;
-
Pointer types (object and function, but not pointer to member types);
-
C arrays;
-
Containers and ranges (types that provide
begin()
andend()
); -
Unordered containers and ranges;
-
Constant size containers (
std::array
,boost::array
); -
Tuple-like types (
std::pair
,std::tuple
); -
Described classes (using Boost.Describe).
User-defined types that aren’t in the above categories can provide
support for hash_append
by declaring an overload of the tag_invoke
function with the appropriate parameters.
The second argument to hash_append
, the flavor, is used to control
the serialization process in cases where more than one behavior is
possible and desirable. It currently contains the following members:
-
static constexpr endian byte_order; // native, little, or big
-
using size_type = std::uint64_t; // or std::uint32_t
The byte_order
member of the flavor affects how scalar C++ objects
are serialized into bytes. For example, the uint32_t
integer 0x01020304
can be serialized into { 0x01, 0x02, 0x03, 0x04 }
when byte_order
is
endian::big
, and into { 0x04, 0x03, 0x02, 0x01 }
when byte_order
is endian::little
.
The value endian::native
means to use the byte order of the current
platform. This typically results in higher performance, because it allows
hash_append
to pass the underlying object bytes directly to the hash
algorithm, without any processing.
The size_type
member type of the flavor affects how container and range
sizes (typically of type size_t
) are serialized. Since the size of
size_t
in bytes can vary, serializing the type directly results in
different hash values when the code is compiled for 64 bit or for 32 bit.
Using a fixed width type avoids this.
There are three predefined flavors, defined in boost/hash2/flavor.hpp
:
struct default_flavor
{
using size_type = std::uint64_t;
static constexpr auto byte_order = endian::native;
};
struct little_endian_flavor
{
using size_type = std::uint64_t;
static constexpr auto byte_order = endian::little;
};
struct big_endian_flavor
{
using size_type = std::uint64_t;
static constexpr auto byte_order = endian::big;
};
The default one is used when hash_append
is invoked without passing
a flavor: hash_append(h, {}, v);
. It results in higher performance,
but the hash values are endianness dependent.
Contiguously Hashable Types
The first thing hash_append(h, f, v)
does is to check whether the type is contiguously hashable under the requested byte order, by testing is_contiguously_hashable<T, Flavor::byte_order>::value
.
When that’s true
, it invokes h.update(&v, sizeof(v))
.
Integral Types
When T
is an integral type (bool
, a signed or unsigned integer type like int
or unsigned long
, or a character type like char8_t
or char32_t
),
v
converted into its byte representation (an array of unsigned char
and a size of sizeof(T)
) under the requested byte order.
hash_append
then calls h.update(p, n)
, where p
is the address of this representation, and n
is its size.
For example, the value 0x01020304
of type std::uint32_t
, when Flavor::byte_order
is endian::little
, is converted into the array { 0x04, 0x03, 0x02, 0x01 }
.
int main()
{
boost::hash2::fnv1a_32 h1;
std::uint32_t v1 = 0x01020304;
boost::hash2::hash_append( h1, boost::hash2::little_endian_flavor(), v1 );
boost::hash2::fnv1a_32 h2;
unsigned char v2[] = { 0x04, 0x03, 0x02, 0x01 };
h2.update( v2, sizeof(v2) );
assert( h1.result() == h2.result() );
}
Floating Point Types
When T
is a floating point type (only float
and double
are supported at the moment),
v
is converted into an unsigned integer type of the same size using the equivalent of std::bit_cast
, then hash_append
is invoked with that converted value.
int main()
{
boost::hash2::fnv1a_32 h1;
float v1 = 3.14f;
boost::hash2::hash_append( h1, {}, v1 );
boost::hash2::fnv1a_32 h2;
std::uint32_t v2 = 0x4048f5c3;
boost::hash2::hash_append( h2, {}, v2 );
assert( h1.result() == h2.result() );
}
However, there’s a subtlety here. The requirements for a hash function H
say that if x == y
, then H(x) == H(y)
. But +0.0 == -0.0
, even though the bit representations of these two values differ.
So, in order to meet the requirement, if v
is negative zero, it’s first replaced with a positive zero of the same type, before bit_cast
to an integer.
int main()
{
boost::hash2::fnv1a_32 h1;
boost::hash2::hash_append( h1, {}, +0.0 );
boost::hash2::fnv1a_32 h2;
boost::hash2::hash_append( h2, {}, -0.0 );
assert( h1.result() == h2.result() );
}
Enumeration Types
When T
is an enumeration type, v
is converted to the underlying type of T
, then the converted value is passed to hash_append
.
enum E: int
{
v1 = 123
};
int main()
{
boost::hash2::fnv1a_32 h1;
boost::hash2::hash_append( h1, {}, v1 );
boost::hash2::fnv1a_32 h2;
int v2 = 123;
boost::hash2::hash_append( h2, {}, v2 );
assert( h1.result() == h2.result() );
}
Pointers
When T
is a pointer type, it’s converted to std::uintptr_t
using reinterpret_cast
, and the converted value is passed to hash_append
.
int x1 = 0;
int main()
{
boost::hash2::fnv1a_32 h1;
boost::hash2::hash_append( h1, {}, &x1 );
boost::hash2::fnv1a_32 h2;
boost::hash2::hash_append( h2, {}, reinterpret_cast<std::uintptr_t>(&x1) );
assert( h1.result() == h2.result() );
}
Arrays
When T
is an array type U[N]
, the elements of v
are passed to hash_append
in sequence.
This is accomplished by calling hash_append_range(h, f, v + 0, v + N)
.
int main()
{
boost::hash2::fnv1a_32 h1;
int v1[4] = { 1, 2, 3, 4 };
boost::hash2::hash_append( h1, {}, v1 );
boost::hash2::fnv1a_32 h2;
boost::hash2::hash_append_range( h2, {}, v1 + 0, v1 + 4 );
assert( h1.result() == h2.result() );
boost::hash2::fnv1a_32 h3;
boost::hash2::hash_append( h3, {}, v1[0] );
boost::hash2::hash_append( h3, {}, v1[1] );
boost::hash2::hash_append( h3, {}, v1[2] );
boost::hash2::hash_append( h3, {}, v1[3] );
assert( h1.result() == h3.result() );
}
Ranges
When T
is a range (boost::container_hash::is_range<T>::value
is true
), its elements are passed to hash_append
as follows:
-
When
T
is an unordered range (boost::container_hash::is_unordered_range<T>::value
istrue
),hash_append
invokeshash_append_unordered_range(h, f, v.begin(), v.end())
.hash_append_unordered_range
derives a hash value from the range elements in such a way so that their order doesn’t affect the hash value. -
When
T
is a contiguous range (boost::container_hash::is_contiguous_range<T>::value
istrue
),hash_append
first invokeshash_append_range(h, f, v.data(), v.data() + v.size())
, then, ifhas_constant_size<T>::value
isfalse
, it invokeshash_append_size(h, f, v.size())
. -
Otherwise,
hash_append
first invokeshash_append_range(h, f, v.begin(), v.end())
, then, ifhas_constant_size<T>::value
isfalse
, it invokeshash_append_size(h, f, m)
, wherem
isstd::distance(v.begin(), v.end())
.
As a special case, in order to meet the requirement that a call to hash_append
must always result in at least one call to Hash::update
, for ranges of constant size 0, hash_append(h, f, '\x00')
is called.
int main()
{
boost::hash2::fnv1a_32 h1;
std::vector<int> v1 = { 1, 2, 3, 4 };
boost::hash2::hash_append( h1, {}, v1 );
boost::hash2::fnv1a_32 h2;
std::list<int> v2 = { 1, 2, 3, 4 };
boost::hash2::hash_append( h2, {}, v2 );
assert( h1.result() == h2.result() );
boost::hash2::fnv1a_32 h3;
boost::hash2::hash_append_range( h3, {}, v1.data(), v1.data() + v1.size() );
boost::hash2::hash_append_size( h3, {}, v1.size() );
assert( h1.result() == h3.result() );
boost::hash2::fnv1a_32 h4;
boost::hash2::hash_append_range( h4, {}, v2.begin(), v2.end() );
boost::hash2::hash_append_size( h4, {}, std::distance(v2.begin(), v2.end()) );
assert( h2.result() == h4.result() );
}
Tuples
When T
is a tuple (boost::container_hash::is_tuple_like<T>::value
is true
), its elements as obtained by get<I>(v)
for I
in [0, std::tuple_size<T>::value)
are passed to hash_append
, in sequence.
As a special case, in order to meet the requirement that a call to hash_append
must always result in at least one call to Hash::update
, for tuples of size 0, hash_append(h, f, '\x00')
is called.
int main()
{
boost::hash2::fnv1a_32 h1;
std::tuple<int, int, int> v1 = { 1, 2, 3 };
boost::hash2::hash_append( h1, {}, v1 );
boost::hash2::fnv1a_32 h2;
boost::hash2::hash_append( h2, {}, get<0>(v1) );
boost::hash2::hash_append( h2, {}, get<1>(v1) );
boost::hash2::hash_append( h2, {}, get<2>(v1) );
assert( h1.result() == h2.result() );
}
Described Classes
When T
is a described class (boost::container_hash::is_described_class<T>::value
is true
), Boost.Describe primitives are used to enumerate its bases and members, and then,
for each base class subobject b
of v
, hash_append(h, f, b)
is called, then for each member subobject m
of v
, hash_append(h, f, m)
is called.
struct X
{
int a;
};
BOOST_DESCRIBE_STRUCT(X, (), (a))
struct Y: public X
{
int b;
};
BOOST_DESCRIBE_STRUCT(Y, (X), (b))
int main()
{
boost::hash2::fnv1a_32 h1;
X v1 = { { 1 }, 2 };
boost::hash2::hash_append( h1, {}, v1 );
boost::hash2::fnv1a_32 h2;
boost::hash2::hash_append( h2, {}, v1.a );
boost::hash2::hash_append( h2, {}, v1.b );
assert( h1.result() == h2.result() );
}
As a special case, in order to meet the requirement that a call to hash_append
must always result in at least one call to Hash::update
, for classes without any bases or members, hash_append(h, f, '\x00')
is called.
User Defined Types
When T
is a user defined type that does not fall into one of the above categories, it needs to provide its own hashing support, by defining an appropriate tag_invoke
overload.
This tag_invoke
overload needs to have the following form:
template<class Hash, class Flavor>
void tag_invoke( boost::hash2::hash_append_tag const&, Hash& h, Flavor const& f, X const& v );
where X
is the user-defined type.
It can be defined as a separate free function in the namespace of X
, but the recommended approach is to define it as an inline friend
in the definition of X
:
#include <boost/hash2/hash_append_fwd.hpp>
#include <string>
class X
{
private:
std::string a;
int b;
// not part of the salient state
void const* c;
public:
friend bool operator==( X const& x1, X const& x2 )
{
return x1.a == x2.a && x1.b == x2.b;
}
template<class Hash, class Flavor>
friend void tag_invoke( boost::hash2::hash_append_tag const&,
Hash& h, Flavor const& f, X const& v )
{
boost::hash2::hash_append(h, f, v.a);
boost::hash2::hash_append(h, f, v.b);
}
};
This overload needs to meet the three requirements for a hash function. In practice, this means that the definitions of equality (operator==
) and hashing (tag_invoke
) must agree on what members need to be included.
In the example above, the member c
is not part of the object state, so it’s neither compared in operator==
, nor included in the object message in tag_invoke
.
The particular implementation of tag_invoke
is type-specific. In general, it needs to include all salient parts of the object’s value in the resultant message, but the exact way to do so is type-dependent.
As another example, here’s how one might implement tag_invoke
for an "inline string" type (a string that stores its characters, up to some maximum count, in the type itself):
#include <boost/hash2/hash_append_fwd.hpp>
#include <algorithm>
#include <cstdint>
class Str
{
private:
static constexpr std::size_t N = 32;
std::uint8_t size_ = 0;
char data_[ N ] = {};
public:
friend constexpr bool operator==( Str const& x1, Str const& x2 )
{
return x1.size_ == x2.size_ && std::equal( x1.data_, x1.data_ + x1.size_, x2.data_ );
}
template<class Hash, class Flavor>
friend constexpr void tag_invoke( boost::hash2::hash_append_tag const&,
Hash& h, Flavor const& f, X const& v )
{
boost::hash2::hash_append_range( h, f, v.data_, v.data_ + v.size_ );
boost::hash2::hash_append_size( h, f, v.size_ );
}
};
Note
|
This example is illustrative; in practice, the above type will likely provide begin() , end() , data() , and size() member functions, which will make it a contiguous range and the built-in support will do the right thing.
|
Usage Examples
md5sum
A command line utility that prints the MD5 digests of a list of files passed as arguments.
#include <boost/hash2/md5.hpp>
#include <array>
#include <string>
#include <cerrno>
#include <cstdio>
static void md5sum( std::FILE* f, char const* fn )
{
boost::hash2::md5_128 hash;
int const N = 4096;
unsigned char buffer[ N ];
for( ;; )
{
std::size_t n = std::fread( buffer, 1, N, f );
if( std::ferror( f ) )
{
std::fprintf( stderr, "'%s': read error: %s\n", fn, std::strerror( errno ) );
return;
}
if( n == 0 ) break;
hash.update( buffer, n );
}
std::string digest = to_string( hash.result() );
std::printf( "%s *%s\n", digest.c_str(), fn );
}
int main( int argc, char const* argv[] )
{
for( int i = 1; i < argc; ++i )
{
std::FILE* f = std::fopen( argv[i], "rb" );
if( f == 0 )
{
std::fprintf( stderr, "'%s': open error: %s\n", argv[i], std::strerror( errno ) );
continue;
}
md5sum( f, argv[i] );
std::fclose( f );
}
}
Sample command:
md5sum apache_builds.json canada.json citm_catalog.json twitter.json
Sample output:
7dc25b5fd9eb2217ed648dad23b311da *apache_builds.json 8767d618bff99552b4946078d3a90c0c *canada.json b4391581160654374bee934a3b91255e *citm_catalog.json bf7d37451840af4e8873b65763315cbf *twitter.json
hash2sum
A command line utility that prints the digests of a list of files, using a specified hash algorithm.
The hash algorithm is passed as the first command line argument.
This example requires C++14.
#include <boost/hash2/md5.hpp>
#include <boost/hash2/sha1.hpp>
#include <boost/hash2/sha2.hpp>
#include <boost/hash2/ripemd.hpp>
#include <boost/mp11.hpp>
#include <array>
#include <string>
#include <cerrno>
#include <cstdio>
template<class Hash> void hash2sum( std::FILE* f, char const* fn )
{
Hash hash;
int const N = 4096;
unsigned char buffer[ N ];
for( ;; )
{
std::size_t n = std::fread( buffer, 1, N, f );
if( std::ferror( f ) )
{
std::fprintf( stderr, "'%s': read error: %s\n", fn, std::strerror( errno ) );
return;
}
if( n == 0 ) break;
hash.update( buffer, n );
}
std::string digest = to_string( hash.result() );
std::printf( "%s *%s\n", digest.c_str(), fn );
}
template<class Hash> void hash2sum( char const* fn )
{
std::FILE* f = std::fopen( fn, "rb" );
if( f == 0 )
{
std::fprintf( stderr, "'%s': open error: %s\n", fn, std::strerror( errno ) );
}
else
{
hash2sum<Hash>( f, fn );
std::fclose( f );
}
}
using namespace boost::mp11;
using namespace boost::hash2;
using hashes = mp_list<
md5_128,
sha1_160,
sha2_256,
sha2_224,
sha2_512,
sha2_384,
sha2_512_256,
sha2_512_224,
ripemd_160,
ripemd_128
>;
constexpr char const* names[] = {
"md5_128",
"sha1_160",
"sha2_256",
"sha2_224",
"sha2_512",
"sha2_384",
"sha2_512_256",
"sha2_512_224",
"ripemd_160",
"ripemd_128"
};
int main( int argc, char const* argv[] )
{
if( argc < 2 )
{
std::fputs( "usage: hash2sum <hash> <files...>\n", stderr );
return 2;
}
std::string hash( argv[1] );
bool found = false;
mp_for_each< mp_iota<mp_size<hashes>> >([&](auto I){
if( hash == names[I] )
{
using Hash = mp_at_c<hashes, I>;
for( int i = 2; i < argc; ++i )
{
hash2sum<Hash>( argv[i] );
}
found = true;
}
});
if( !found )
{
std::fprintf( stderr, "hash2sum: unknown hash algorithm name '%s'; use one of the following:\n\n", hash.c_str() );
for( char const* name: names )
{
std::fprintf( stderr, " %s\n", name );
}
return 1;
}
}
Sample command:
hash2sum sha2_512_224 apache_builds.json canada.json citm_catalog.json twitter.json
Sample output:
a95d7fde785fe24f9507fd1709014567bbc595867f1abaad96f50dbc *apache_builds.json b07e42587d10ec323a25fd8fc3eef2213fb0997beb7950350f4e8a4b *canada.json 4ceee5a83ad320fedb0dfddfb6f80af50b99677e87158e2d039aa168 *citm_catalog.json 854ebe0da98cadd426ea0fa3218d60bb52cf6494e435d2f385a37d48 *twitter.json
Compile Time Hashing
This example demonstrates calculating the MD5 digest of a data array, embedded in the program source, at compile time. It requires C++14.
#include <boost/hash2/md5.hpp>
#include <iostream>
// xxd -i resource
constexpr unsigned char resource[] = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x6d, 0x90,
0xcf, 0x6e, 0x83, 0x30, 0x0c, 0xc6, 0xcf, 0x45, 0xea, 0x3b, 0x78, 0x9c,
0x4b, 0x02, 0x3d, 0x6e, 0xd0, 0x43, 0xff, 0x1c, 0x26, 0x55, 0x3b, 0x14,
0x6d, 0xd7, 0x2a, 0x04, 0x43, 0x22, 0x95, 0x84, 0x25, 0x66, 0x8c, 0x47,
0xda, 0x5b, 0x2e, 0x91, 0xd6, 0xcb, 0xd4, 0x93, 0x2d, 0xdb, 0xdf, 0xef,
0xb3, 0x5d, 0x2a, 0x1a, 0x6e, 0xbb, 0x75, 0x52, 0x2a, 0x14, 0x6d, 0x8c,
0x03, 0x92, 0x00, 0x45, 0x34, 0x66, 0xf8, 0x39, 0xe9, 0xaf, 0x2a, 0x75,
0xd8, 0x39, 0xf4, 0x2a, 0x05, 0x69, 0x0d, 0xa1, 0xa1, 0x2a, 0xcd, 0x5f,
0xe0, 0xfd, 0x72, 0xae, 0x5a, 0x2b, 0x79, 0x54, 0x73, 0x25, 0xbc, 0xda,
0xb2, 0x98, 0xa6, 0x91, 0xc0, 0xef, 0xa8, 0xc6, 0xb6, 0x4b, 0x88, 0x17,
0x6c, 0xb5, 0x43, 0x49, 0xda, 0xf4, 0x40, 0x16, 0xca, 0x80, 0x0f, 0xcc,
0x2a, 0x7d, 0xa8, 0x7f, 0x50, 0x2c, 0xb9, 0xd8, 0x31, 0xc6, 0x22, 0xf9,
0x8f, 0x58, 0xf2, 0xfb, 0xd6, 0x4f, 0x59, 0xb6, 0x4e, 0x56, 0x3f, 0x70,
0xb0, 0xe3, 0xe2, 0x74, 0xaf, 0x08, 0xf6, 0x38, 0x08, 0x03, 0x47, 0x31,
0xa3, 0xdf, 0xc0, 0x36, 0xcf, 0x8b, 0xd0, 0x3f, 0x6a, 0x4f, 0x4e, 0x37,
0x13, 0x61, 0x0b, 0x93, 0x69, 0xd1, 0x01, 0x29, 0x84, 0xbd, 0xb5, 0x9e,
0xa0, 0xb6, 0x1d, 0xcd, 0xc2, 0x21, 0x9c, 0xb5, 0x44, 0xe3, 0x71, 0x03,
0x1f, 0xe8, 0xbc, 0xb6, 0x06, 0x0a, 0x96, 0x07, 0xd3, 0x55, 0x8d, 0x08,
0x42, 0x4a, 0x3b, 0x8c, 0xc2, 0x2c, 0xf1, 0x86, 0x4e, 0xdf, 0xc2, 0xf4,
0xeb, 0xe1, 0xf4, 0x56, 0x9f, 0xae, 0xc5, 0x35, 0x67, 0xf4, 0x4d, 0x60,
0x5d, 0xf8, 0xcf, 0xb8, 0x80, 0xa0, 0x20, 0x89, 0xef, 0x7b, 0xe6, 0x7c,
0x9e, 0x67, 0xd6, 0x44, 0x13, 0x66, 0x5d, 0xcf, 0xff, 0x29, 0xd6, 0x49,
0x96, 0x85, 0x0b, 0x7e, 0x01, 0x36, 0x66, 0x95, 0x6b, 0x80, 0x01, 0x00,
0x00
};
template<std::size_t N> constexpr auto md5( unsigned char const(&a)[ N ] )
{
boost::hash2::md5_128 hash;
hash.update( a, N );
return hash.result();
}
constexpr auto resource_digest = md5( resource );
int main()
{
std::cout << "Resource digest: " << resource_digest << std::endl;
}
Since the constexpr
overload of update
takes unsigned char const*
(void const*
is not allowed in constexpr
functions), if the
data to be hashed is a character array of type char const[]
, passing it directly to update
will not compile. In that case, we can
use hash_append_range
instead of calling update
, as in the following example.
#include <boost/hash2/sha2.hpp>
#include <boost/hash2/hash_append.hpp>
#include <iostream>
extern constexpr char const license[] =
"Boost Software License - Version 1.0 - August 17th, 2003\n"
"\n"
"Permission is hereby granted, free of charge, to any person or organization\n"
"obtaining a copy of the software and accompanying documentation covered by\n"
"this license (the \"Software\") to use, reproduce, display, distribute,\n"
"execute, and transmit the Software, and to prepare derivative works of the\n"
"Software, and to permit third-parties to whom the Software is furnished to\n"
"do so, all subject to the following:\n"
"\n"
"The copyright notices in the Software and this entire statement, including\n"
"the above license grant, this restriction and the following disclaimer,\n"
"must be included in all copies of the Software, in whole or in part, and\n"
"all derivative works of the Software, unless such copies or derivative\n"
"works are solely in the form of machine-executable object code generated by\n"
"a source language processor.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\n"
"SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\n"
"FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\n"
"ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n"
"DEALINGS IN THE SOFTWARE.\n"
;
constexpr unsigned char secret[] = {
0xA4, 0x80, 0x0E, 0xE8, 0x20, 0x0B, 0x7C, 0x9A,
0xF1, 0x3E, 0x3D, 0xEC, 0x64, 0x4F, 0x64, 0xCA,
0x33, 0xCC, 0x84, 0xC8, 0x34, 0xE3, 0x08, 0xAE,
0x92, 0x89, 0xEB, 0xD0, 0x47, 0x39, 0x87, 0xD8,
};
template<std::size_t N> constexpr auto hmac_sha2_256( char const(&s)[ N ] )
{
boost::hash2::hmac_sha2_256 hmac( secret, sizeof(secret) );
// N-1, in order to not include the null terminator
boost::hash2::hash_append_range( hmac, {}, s, s + N - 1 );
return hmac.result();
}
constexpr auto license_mac = hmac_sha2_256( license );
int main()
{
std::cout << "License authentication code: " << license_mac << std::endl;
}
Use with Unordered Containers
To use one of our hash algorithms (such as fnv1a_64
) with an unordered container (such as boost::unordered_flat_map
), we need
to create an adaptor class that exposes an interface compatible with std::hash<T>
.
To do that, in the operator()(T const& v)
member function of our adaptor, we need to create an instance h
of the hash algorithm,
use hash_append(h, {}, v)
to send v
to it, and then extract the result using h.result()
and return it as std::size_t
.
The minimal working example below illustrates this approach.
#include <boost/hash2/fnv1a.hpp>
#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/get_integral_result.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <string>
template<class T, class H> class hash
{
public:
std::size_t operator()( T const& v ) const
{
H h;
boost::hash2::hash_append( h, {}, v );
return boost::hash2::get_integral_result<std::size_t>( h.result() );
}
};
int main()
{
using hasher = hash<std::string, boost::hash2::fnv1a_64>;
boost::unordered_flat_map<std::string, int, hasher> map;
map[ "foo" ] = 1;
map[ "bar" ] = 2;
}
Since hash<T, H>
is templated not just on the key type T
, but on the hash algorithm type H
, we can easily switch from fnv1a_64
to another hash algorithm, for example siphash_64
, by only changing the line
using hasher = hash<std::string, boost::hash2::fnv1a_64>;
to
using hasher = hash<std::string, boost::hash2::siphash_64>;
This will work, but SipHash is not intended to be used without an initial random seed, and we don’t pass any. To rectify this, let’s
modify hash<T, H>
to have a constructor taking a seed of type uint64_t
:
#include <boost/hash2/siphash.hpp>
#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/get_integral_result.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <string>
template<class T, class H> class hash
{
private:
std::uint64_t seed_;
public:
explicit hash( std::uint64_t seed ): seed_( seed )
{
}
std::size_t operator()( T const& v ) const
{
H h( seed_ );
boost::hash2::hash_append( h, {}, v );
return boost::hash2::get_integral_result<std::size_t>( h.result() );
}
};
int main()
{
std::uint64_t seed = 0x0102030405060708ull;
using hasher = hash<std::string, boost::hash2::siphash_64>;
boost::unordered_flat_map<std::string, int, hasher> map( 0, hasher( seed ) );
map[ "foo" ] = 1;
map[ "bar" ] = 2;
}
Note
|
In real code, the seed will not be a hardcoded constant; ideally, every unordered container instance will have its own random and unpredictable seed. |
Since all hash algorithms that conform to our library requirements are constructible with an initial seed of type uint64_t
, the above will work with any of them.
This is good enough for any practical purposes, but in principle, SipHash64 takes a 16 byte seed per specification, and we only (effectively) pass 8 bytes.
We could modify our hash
yet again and this time use a constructor taking a sequence of bytes as the seed:
#include <boost/hash2/siphash.hpp>
#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/get_integral_result.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <string>
template<class T, class H> class hash
{
private:
H h_;
public:
hash( unsigned char const* p, std::size_t n ): h_( p, n )
{
}
std::size_t operator()( T const& v ) const
{
H h( h_ );
boost::hash2::hash_append( h, {}, v );
return boost::hash2::get_integral_result<std::size_t>( h.result() );
}
};
int main()
{
unsigned char const seed[ 16 ] =
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};
using hasher = hash<std::string, boost::hash2::siphash_64>;
boost::unordered_flat_map<std::string, int, hasher> map( 0, hasher( seed, sizeof(seed) ) );
map[ "foo" ] = 1;
map[ "bar" ] = 2;
}
As before, construction from a byte sequence is a required part of the hash algorithm interface, so the above will work with any of them.
To avoid the need to store the initial seed as we did in the uint64_t
case — which would require an allocation because n
can be arbitrary, necessitating the use of std::vector<unsigned char>
— we construct an instance h_
of the hash algorithm, passing it the seed, to capture the initial seeded state, and then
copy this seeded instance in operator()
.
But once we’ve done that, we might notice that we can construct this initial instance h_
using any of the three supported constructors, not just the one taking a byte sequence:
#include <boost/hash2/siphash.hpp>
#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/get_integral_result.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <string>
template<class T, class H> class hash
{
private:
H h_;
public:
hash(): h_()
{
}
explicit hash( std::uint64_t seed ): h_( seed )
{
}
hash( unsigned char const* p, std::size_t n ): h_( p, n )
{
}
std::size_t operator()( T const& v ) const
{
H h( h_ );
boost::hash2::hash_append( h, {}, v );
return boost::hash2::get_integral_result<std::size_t>( h.result() );
}
};
int main()
{
using hasher = hash<std::string, boost::hash2::siphash_64>;
{
boost::unordered_flat_map<std::string, int, hasher> map;
map[ "foo" ] = 1;
map[ "bar" ] = 2;
}
{
std::uint64_t seed = 0x0102030405060708ull;
boost::unordered_flat_map<std::string, int, hasher> map( 0, hasher( seed ) );
map[ "foo" ] = 1;
map[ "bar" ] = 2;
}
{
unsigned char const seed[ 16 ] =
{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};
boost::unordered_flat_map<std::string, int, hasher> map( 0, hasher(seed, sizeof(seed)) );
map[ "foo" ] = 1;
map[ "bar" ] = 2;
}
}
This variation of hash<T, H>
is universal; it can be used without a seed, with an unsigned integer seed, and with a byte sequence seed.
Note
|
In real code, you might want to omit the default constructor, to avoid the possibility of accidentally using an unseeded hash algorithm. |
There’s one final modification we could do to hash
. In the examples above, we unconditionally use the 64 bit variant of SipHash, even though we only need a result of type std::size_t
because that’s what std::hash
mandates.
It would be better for performance if we used siphash_32
when std::size_t
is 32 bit, and siphash_64
when it’s 64 bit.
For that, we can make hash
take two hash algorithms, one 32 bit and one 64 bit, and have it pick the appropriate one automatically:
#include <boost/hash2/siphash.hpp>
#include <boost/hash2/md5.hpp>
#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/get_integral_result.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/core/type_name.hpp>
#include <type_traits>
#include <string>
#include <iostream>
template<class T, class H1, class H2 = H1> class hash
{
public:
using hash_type = typename std::conditional<
sizeof(typename H1::result_type) == sizeof(std::size_t), H1, H2
>::type;
private:
hash_type h_;
public:
hash(): h_()
{
}
explicit hash( std::uint64_t seed ): h_( seed )
{
}
hash( unsigned char const* p, std::size_t n ): h_( p, n )
{
}
std::size_t operator()( T const& v ) const
{
hash_type h( h_ );
boost::hash2::hash_append( h, {}, v );
return boost::hash2::get_integral_result<std::size_t>( h.result() );
}
};
int main()
{
{
using hasher = hash<std::string, boost::hash2::siphash_32, boost::hash2::siphash_64>;
std::cout << boost::core::type_name<hasher>() << " uses "
<< boost::core::type_name<hasher::hash_type>() << std::endl;
boost::unordered_flat_map<std::string, int, hasher> map;
map[ "foo" ] = 1;
map[ "bar" ] = 2;
}
{
using hasher = hash<std::string, boost::hash2::md5_128>;
std::cout << boost::core::type_name<hasher>() << " uses "
<< boost::core::type_name<hasher::hash_type>() << std::endl;
boost::unordered_flat_map<std::string, int, hasher> map;
map[ "foo" ] = 1;
map[ "bar" ] = 2;
}
}
To keep the case where we only pass one hash algorithm working, we default the second template parameter to the first one, so that if only one hash algorithm is passed, it will always be used.
Result Extension
Some of our hash algorithms, such as xxhash_64
and siphash_64
, have more than 64 bits of internal state, but only produce a 64 bit result.
If we’re using one of these algorithms to produce file or content checksums, do not tolerate collisions, and operate on a large number of files or items (many millions), it might be better to use a 128 bit digest instead.
Since the algorithms maintain more than 64 bits of state, we can call result()
twice and obtain a meaningful 128 bit result.
The following example demonstrates how. It defines an algorithm xxhash_128
which is implemented by wrapping xxhash_64
and redefining its result_type
and result
members appropriately:
#include <boost/hash2/xxhash.hpp>
#include <boost/hash2/digest.hpp>
#include <boost/endian/conversion.hpp>
class xxhash_128: private boost::hash2::xxhash_64
{
public:
using result_type = boost::hash2::digest<16>;
using xxhash_64::xxhash_64;
using xxhash_64::update;
result_type result()
{
std::uint64_t r1 = xxhash_64::result();
std::uint64_t r2 = xxhash_64::result();
result_type r = {};
boost::endian::store_little_u64( r.data() + 0, r1 );
boost::endian::store_little_u64( r.data() + 8, r2 );
return r;
}
};
#include <string>
#include <iostream>
int main()
{
std::string tv( "The quick brown fox jumps over the lazy dog" );
xxhash_128 hash( 43 );
hash.update( tv.data(), tv.size() );
std::cout << hash.result() << std::endl;
}
Implementation Features
Supported Compilers
The library requires C++11. The following compilers:
-
g++ 4.8 or later
-
clang++ 3.9 or later
-
Visual Studio 2015 and above
are being tested on Github Actions and Appveyor.
Reference
Hash Algorithms
<boost/hash2/fnv1a.hpp>
namespace boost {
namespace hash2 {
class fnv1a_32;
class fnv1a_64;
} // namespace hash2
} // namespace boost
This header implements the FNV-1a algorithm, in 32 and 64 bit variants.
fnv1a_32
class fnv1a_32
{
private:
std::uint32_t state_; // exposition only
public:
using result_type = std::uint32_t;
constexpr fnv1a_32();
explicit constexpr fnv1a_32( std::uint64_t seed );
constexpr fnv1a_32( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr fnv1a_32();
Default constructor.
- Effects:
-
Initializes
state_
to0x811c9dc5
.
explicit constexpr fnv1a_32( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the state as if by default construction, then if
seed
is not zero, performsupdate(p, 8)
wherep
points to a little-endian representation of the value ofseed
. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
constexpr fnv1a_32( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, and then, if
n
is not zero, performsupdate(p, n)
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
For each
unsigned char
valuech
in the range[p, p+n)
performsstate_ = (state_ ^ ch) * 0x01000193
.
result
constexpr result_type result();
- Effects:
-
Updates
state_
to(state_ ^ 0xFF) * 0x01000193
. - Returns:
-
The value of
state_
before the update. - Remarks:
-
The state is updated to allow repeated calls to
result()
to return a pseudorandom sequence ofresult_type
values, effectively extending the output.
fnv1a_64
class fnv1a_64
{
private:
std::uint64_t state_; // exposition only
public:
using result_type = std::uint64_t;
constexpr fnv1a_64();
explicit constexpr fnv1a_64( std::uint64_t seed );
constexpr fnv1a_64( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr fnv1a_64();
Default constructor.
- Effects:
-
Initializes
state_
to0xcbf29ce484222325
.
explicit constexpr fnv1a_64( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the state as if by default construction, then if
seed
is not zero, performsupdate(p, 8)
wherep
points to a little-endian representation of the value ofseed
. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
constexpr fnv1a_64( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, and then, if
n
is not zero, performsupdate(p, n)
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
For each
unsigned char
valuech
in the range[p, p+n)
performsstate_ = (state_ ^ ch) * 0x100000001b3
.
result
constexpr result_type result();
- Effects:
-
Updates
state_
to(state_ ^ 0xFF) * 0x100000001b3
. - Returns:
-
The value of
state_
before the update. - Remarks:
-
The state is updated to allow repeated calls to
result()
to return a pseudorandom sequence ofresult_type
values, effectively extending the output.
<boost/hash2/xxhash.hpp>
namespace boost {
namespace hash2 {
class xxhash_32;
class xxhash_64;
} // namespace hash2
} // namespace boost
This header implements the XXH32 and XXH64 algorithms.
xxhash_32
class xxhash_32
{
public:
using result_type = std::uint32_t;
constexpr xxhash_32();
explicit constexpr xxhash_32( std::uint64_t seed );
constexpr xxhash_32( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr xxhash_32();
Default constructor.
- Effects:
-
Initializes the internal state of the XXH32 algorithm to its initial values.
explicit constexpr xxhash_32( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the internal state of the XXH32 algorithm using the low 32 bits of
seed
as the seed, then if the high 32 bits ofseed
aren’t zero, mixes them into the state. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
xxhash_32( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, then if
n
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the XXH32 algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Obtains a 32 bit hash value from the state as specified by XXH32, then updates the state.
- Returns:
-
The obtained hash value.
- Remarks:
-
The state is updated to allow repeated calls to
result()
to return a pseudorandom sequence ofresult_type
values, effectively extending the output.
xxhash_64
class xxhash_64
{
public:
using result_type = std::uint64_t;
constexpr xxhash_64();
explicit constexpr xxhash_64( std::uint64_t seed );
constexpr xxhash_64( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr xxhash_64();
Default constructor.
- Effects:
-
Initializes the internal state of the XXH64 algorithm to its initial values.
explicit constexpr xxhash_64( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the internal state of the XXH64 algorithm using
seed
as the seed. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
xxhash_64( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, then if
n
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the XXH64 algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Obtains a 64 bit hash value from the state as specified by XXH64, then updates the state.
- Returns:
-
The obtained hash value.
- Remarks:
-
The state is updated to allow repeated calls to
result()
to return a pseudorandom sequence ofresult_type
values, effectively extending the output.
<boost/hash2/siphash.hpp>
namespace boost {
namespace hash2 {
class siphash_32;
class siphash_64;
} // namespace hash2
} // namespace boost
This header implements the SipHash and HalfSipHash algorithms.
siphash_32
class siphash_32
{
public:
using result_type = std::uint32_t;
constexpr siphash_32();
explicit constexpr siphash_32( std::uint64_t seed );
constexpr siphash_32( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr siphash_32();
Default constructor.
- Effects:
-
Initializes the internal state of the HalfSipHash algorithm as if using a sequence of 8 zero bytes as the key.
explicit constexpr siphash_32( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the internal state of the HalfSipHash algorithm using
seed
as the key, as if it were a sequence of its 8 constituent bytes, in little-endian order. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
siphash_32( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
If
n
is 8, initializes the state as specified by the algorithm; otherwise, initializes the state as if by default construction, then ifn
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the HalfSipHash algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Obtains a 32 bit hash value from the state as specified by HalfSipHash.
- Returns:
-
The obtained hash value.
- Remarks:
-
The state is updated, which allows repeated calls to
result()
to return a pseudorandom sequence ofresult_type
values, effectively extending the output.
siphash_64
class siphash_64
{
public:
using result_type = std::uint64_t;
constexpr siphash_64();
explicit constexpr siphash_64( std::uint64_t seed );
constexpr siphash_64( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr siphash_64();
Default constructor.
- Effects:
-
Initializes the internal state of the SipHash algorithm as if using a sequence of 16 zero bytes as the key.
explicit constexpr siphash_64( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the internal state of the SipHash algorithm using
seed
as the key, as if it were a sequence of its 8 constituent bytes, in little-endian order, followed by 8 zero bytes. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
siphash_64( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
If
n
is 16, initializes the state as specified by the algorithm; otherwise, initializes the state as if by default construction, then ifn
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the SipHash algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Obtains a 64 bit hash value from the state as specified by SipHash, then updates the state.
- Returns:
-
The obtained hash value.
- Remarks:
-
The state is updated, which allows repeated calls to
result()
to return a pseudorandom sequence ofresult_type
values, effectively extending the output.
<boost/hash2/hmac.hpp>
namespace boost {
namespace hash2 {
template<class H> class hmac;
} // namespace hash2
} // namespace boost
This header implements the HMAC algorithm.
hmac
template<class H> class hmac
{
public:
using result_type = typename H::result_type;
static constexpr int block_size = H::block_size;
constexpr hmac();
explicit constexpr hmac( std::uint64_t seed );
constexpr hmac( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
The class template hmac
takes as a parameter a cryptographic hash algorithm H
and implements the corresponding hash-based message authentication code (HMAC) algorithm.
For example, HMAC-SHA2-256 is implemented by hmac<sha2_256>
.
Constructors
constexpr hmac();
Default constructor.
- Effects:
-
Initializes the internal state using an empty byte sequence as the secret key.
explicit constexpr hmac( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
If
seed
is zero, initializes the state as if by default construction, otherwise, initializes it using the 8 bytes of the little-endian representation ofseed
as the secret key. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
hmac( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as specified by the HMAC algorithm using
[p, p+n)
as the secret key. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the HMAC algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Pads the accumulated message and finalizes the HMAC digest.
- Returns:
-
The HMAC digest of the message formed from the byte sequences of the preceding calls to
update
. - Remarks:
-
Repeated calls to
result()
return a pseudorandom sequence ofresult_type
values, effectively extending the output.
<boost/hash2/md5.hpp>
#include <boost/hash2/hmac.hpp>
#include <boost/hash2/digest.hpp>
namespace boost {
namespace hash2 {
class md5_128;
using hmac_md5_128 = hmac<md5_128>;
} // namespace hash2
} // namespace boost
This header implements the MD5 algorithm.
md5_128
class md5_128
{
public:
using result_type = digest<16>;
static constexpr int block_size = 64;
constexpr md5_128();
explicit constexpr md5_128( std::uint64_t seed );
constexpr md5_128( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr md5_128();
Default constructor.
- Effects:
-
Initializes the internal state of the MD5 algorithm to its initial values.
explicit constexpr md5_128( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the state as if by default construction, then if
seed
is not zero, performsupdate(p, 8); result();
wherep
points to a little-endian representation of the value ofseed
. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
md5_128( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, then if
n
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the MD5 algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Pads the accumulated message and finalizes the MD5 digest.
- Returns:
-
The MD5 digest of the message formed from the byte sequences of the preceding calls to
update
. - Remarks:
-
Repeated calls to
result()
return a pseudorandom sequence ofresult_type
values, effectively extending the output.
<boost/hash2/sha1.hpp>
#include <boost/hash2/hmac.hpp>
#include <boost/hash2/digest.hpp>
namespace boost {
namespace hash2 {
class sha1_160;
using hmac_sha1_160 = hmac<sha1_160>;
} // namespace hash2
} // namespace boost
This header implements the SHA-1 algorithm.
sha1_160
class sha1_160
{
public:
using result_type = digest<20>;
static constexpr int block_size = 64;
constexpr sha1_160();
explicit constexpr sha1_160( std::uint64_t seed );
constexpr sha1_160( unsigned char const* p, std::size_t n );
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr sha1_160();
Default constructor.
- Effects:
-
Initializes the internal state of the SHA-1 algorithm to its initial values.
explicit constexpr sha1_160( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the state as if by default construction, then if
seed
is not zero, performsupdate(p, 8); result();
wherep
points to a little-endian representation of the value ofseed
. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
constexpr sha1_160( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, then if
n
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the SHA-1 algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Pads the accumulated message and finalizes the SHA-1 digest.
- Returns:
-
The SHA-1 digest of the message formed from the byte sequences of the preceding calls to
update
. - Remarks:
-
Repeated calls to
result()
return a pseudorandom sequence ofresult_type
values, effectively extending the output.
<boost/hash2/sha2.hpp>
#include <boost/hash2/hmac.hpp>
#include <boost/hash2/digest.hpp>
namespace boost {
namespace hash2 {
class sha2_256;
class sha2_224;
class sha2_512;
class sha2_384;
class sha2_512_256;
class sha2_512_224;
using hmac_sha2_256 = hmac<sha2_256>;
using hmac_sha2_224 = hmac<sha2_224>;
using hmac_sha2_512 = hmac<sha2_512>;
using hmac_sha2_384 = hmac<sha2_384>;
using hmac_sha2_512_256 = hmac<sha2_512_256>;
using hmac_sha2_512_224 = hmac<sha2_512_224>;
} // namespace hash2
} // namespace boost
This header implements the SHA-2 family of functions.
sha2_256
class sha2_256
{
using result_type = digest<32>;
static constexpr int block_size = 64;
constexpr sha2_256();
constexpr explicit sha2_256( std::uint64_t seed );
constexpr sha2_256( unsigned char const * p, std::size_t n );
void update( void const * p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr sha2_256();
Default constructor.
- Effects:
-
Initializes the internal state of the SHA-256 algorithm to its initial values.
constexpr explicit sha2_256( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the state as if by default construction, then if
seed
is not zero, performsupdate(p, 8); result();
wherep
points to a little-endian representation of the value ofseed
. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
constexpr sha2_256( unsigned char const * p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, then if
n
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const * p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the SHA-256 algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Pads the accumulated message and finalizes the SHA-256 digest.
- Returns:
-
The SHA-256 digest of the message formed from the byte sequences of the preceding calls to
update
. - Remarks:
-
Repeated calls to
result()
return a pseudorandom sequence ofresult_type
values, effectively extending the output.
sha2_224
The SHA-224 algorithm is identical to the SHA-256 algorithm described above.
The only differences are the internal state’s initial values and the size of the message digest, which is:
using result_type = digest<28>;
Otherwise, all other operations and constants are identical.
The message digest is obtained by truncating the final results of the SHA-256 algorithm to its leftmost 224 bits.
sha2_512
class sha2_512
{
using result_type = digest<64>;
static constexpr int block_size = 128;
constexpr sha2_512();
constexpr explicit sha2_512( std::uint64_t seed );
constexpr sha2_512( unsigned char const * p, std::size_t n );
void update( void const * p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr sha2_512();
Default constructor.
- Effects:
-
Initializes the internal state of the SHA-512 algorithm to its initial values.
constexpr explicit sha2_512( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the state as if by default construction, then if
seed
is not zero, performsupdate(p, 8); result();
wherep
points to a little-endian representation of the value ofseed
. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
constexpr sha2_512( unsigned char const * p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, then if
n
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const * p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the SHA-512 algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Pads the accumulated message and finalizes the SHA-512 digest.
- Returns:
-
The SHA-512 digest of the message formed from the byte sequences of the preceding calls to
update
. - Remarks:
-
Repeated calls to
result()
return a pseudorandom sequence ofresult_type
values, effectively extending the output.
sha2_384
The SHA-384 algorithm is identical to the SHA-512 algorithm described above.
The only differences are the internal state’s initial values and the size of the message digest, which is:
using result_type = digest<48>;
Otherwise, all other operations and constants are identical.
The message digest is obtained by truncating the final results of the SHA-512 algorithm to its leftmost 384 bits.
sha2_512_224
The SHA-512/224 algorithm is identical to the SHA-512 algorithm described above.
The only differences are the internal state’s initial values and the size of the message digest, which is:
using result_type = digest<28>;
Otherwise, all other operations and constants are identical.
The message digest is obtained by truncating the final results of the SHA-512 algorithm to its leftmost 224 bits.
sha2_512_256
The SHA-512/256 algorithm is identical to the SHA-512 algorithm described above.
The only differences are the internal state’s initial values and the size of the message digest, which is:
using result_type = digest<32>;
Otherwise, all other operations and constants are identical.
The message digest is obtained by truncating the final results of the SHA-512 algorithm to its leftmost 256 bits.
<boost/hash2/ripemd.hpp>
#include <boost/hash2/hmac.hpp>
#include <boost/hash2/digest.hpp>
namespace boost {
namespace hash2 {
class ripemd_160;
class ripemd_128;
using hmac_ripemd_160 = hmac<ripemd_160>;
using hmac_ripemd_128 = hmac<ripemd_128>;
} // namespace hash2
} // namespace boost
This header implements the RIPEMD-160 and RIPEMD-128 algorithms.
ripemd_160
class ripemd_160
{
using result_type = digest<20>;
static constexpr int block_size = 64;
constexpr ripemd_160();
explicit constexpr ripemd_160( std::uint64_t seed );
constexpr ripemd_160( unsigned char const* p, std::size_t n );
void update( void const * pv, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
constexpr result_type result();
};
Constructors
constexpr ripemd_160();
Default constructor.
- Effects:
-
Initializes the internal state of the RIPEMD-160 algorithm to its initial values.
explicit constexpr ripemd_160( std::uint64_t seed );
Constructor taking an integer seed value.
- Effects:
-
Initializes the state as if by default construction, then if
seed
is not zero, performsupdate(p, 8); result();
wherep
points to a little-endian representation of the value ofseed
. - Remarks:
-
By convention, if
seed
is zero, the effect of this constructor is the same as default construction.
constexpr ripemd_160( unsigned char const* p, std::size_t n );
Constructor taking a byte sequence seed.
- Effects:
-
Initializes the state as if by default construction, then if
n
is not zero, performsupdate(p, n); result()
. - Remarks:
-
By convention, if
n
is zero, the effect of this constructor is the same as default construction.
update
void update( void const* p, std::size_t n );
constexpr void update( unsigned char const* p, std::size_t n );
- Effects:
-
Updates the internal state of the RIPEMD-160 algorithm from the byte sequence
[p, p+n)
. - Remarks:
-
Consecutive calls to
update
are equivalent to a single call with the concatenated byte sequences of the individual calls.
result
constexpr result_type result();
- Effects:
-
Pads the accumulated message and finalizes the RIPEMD-160 digest.
- Returns:
-
The RIPEMD-160 digest of the message formed from the byte sequences of the preceding calls to
update
. - Remarks:
-
Repeated calls to
result()
return a pseudorandom sequence ofresult_type
values, effectively extending the output.
Utilities and Traits
<boost/hash2/digest.hpp>
namespace boost {
namespace hash2 {
template<std::size_t N> class digest
{
private: // exposition only
unsigned char data_[ N ] = {};
public:
using value_type = unsigned char;
using reference = unsigned char&;
using const_reference = unsigned char const&;
using iterator = unsigned char*;
using const_iterator = unsigned char const*;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// constructors
constexpr digest() = default;
constexpr digest( unsigned char const (&v)[ N ] ) noexcept;
// copy
constexpr digest( digest const& ) = default;
constexpr digest& operator=( digest const& ) = default;
// iteration
constexpr iterator begin() noexcept;
constexpr const_iterator begin() const noexcept;
constexpr iterator end() noexcept;
constexpr const_iterator end() const noexcept;
// data, size
constexpr unsigned char* data() noexcept;
constexpr unsigned char const* data() const noexcept;
constexpr size_type size() const noexcept;
constexpr size_type max_size() const noexcept;
// element access
constexpr reference operator[]( std::size_t i );
constexpr const_reference operator[]( std::size_t i ) const;
constexpr reference front() noexcept;
constexpr const_reference front() const noexcept;
constexpr reference back() noexcept;
constexpr const_reference back() const noexcept;
};
// comparisons
template<std::size_t N>
constexpr bool operator==( digest<N> const& a, digest<N> const& b ) noexcept;
template<std::size_t N>
constexpr bool operator!=( digest<N> const& a, digest<N> const& b ) noexcept;
// to_chars
template<std::size_t N>
constexpr char* to_chars( digest<N> const& v, char* first, char* last ) noexcept;
template<std::size_t N, std::size_t M>
constexpr void to_chars( digest<N> const& v, char (&w)[ M ] ) noexcept;
// operator<<
template<std::size_t N>
std::ostream& operator<<( std::ostream& os, digest<N> const& v );
// to_string
template<std::size_t N>
std::string to_string( digest<N> const& v );
} // namespace hash2
} // namespace boost
digest
digest<N>
is a constexpr
-friendly class template similar to std::array<unsigned char, N>
.
It is used to store the resulting message digest of hash algorithms such as SHA2-256 or RIPEMD-160.
Constructors
constexpr digest() = default;
- Effects:
-
Zero-initializes
data_
.
constexpr digest( unsigned char const (&v)[ N ] ) noexcept;
- Effects:
-
Initializes the elements of
data_
from the corresponding elements ofv
.
Iteration
constexpr iterator begin() noexcept;
constexpr const_iterator begin() const noexcept;
- Returns:
-
data_
.
constexpr iterator end() noexcept;
constexpr const_iterator end() const noexcept;
- Returns:
-
data_ + N
.
Accessors
constexpr unsigned char* data() noexcept;
constexpr unsigned char const* data() const noexcept;
- Returns:
-
data_
.
constexpr size_type size() const noexcept;
constexpr size_type max_size() const noexcept;
- Returns:
-
N
.
Element Access
constexpr reference operator[]( std::size_t i );
constexpr const_reference operator[]( std::size_t i ) const;
- Requires:
-
i < size()
. - Returns:
-
data_[ i ]
.
constexpr reference front() noexcept;
constexpr const_reference front() const noexcept;
- Returns:
-
data_[ 0 ]
.
constexpr reference back() noexcept;
constexpr const_reference back() const noexcept;
- Returns:
-
data_[ N-1 ]
.
Comparisons
template<std::size_t N>
constexpr bool operator==( digest<N> const& a, digest<N> const& b ) noexcept;
- Returns:
-
true
when the elements ofa.data_
are equal to the corresponding elements ofb.data_
,false
otherwise.
template<std::size_t N>
constexpr bool operator!=( digest<N> const& a, digest<N> const& b ) noexcept;
- Returns:
-
!(a == b)
.
Formatting
template<std::size_t N>
constexpr char* to_chars( digest<N> const& v, char* first, char* last ) noexcept;
- Effects:
-
Writes the contents of
data_
as a hexadecimal string to the provided output range[first, last)
. - Returns:
-
A pointer one past the end of the generated output, or
nullptr
if[first, last)
is not large enough.
template<std::size_t N, std::size_t M>
constexpr void to_chars( digest<N> const& v, char (&w)[ M ] ) noexcept;
- Requires:
-
M >= N*2 + 1
. - Effects:
-
Writes the contents of
data_
as a hexadecimal string, then a null terminator, to the provided output bufferw
.
template<std::size_t N>
std::ostream& operator<<( std::ostream& os, digest<N> const& v );
- Effects:
-
Writes the contents of
data_
as a hexadecimal string toos
. - Returns:
-
os
.
template<std::size_t N>
std::string to_string( digest<N> const& v );
- Returns:
-
A string containing the contents of
data_
in hexadecimal format.
<boost/hash2/endian.hpp>
namespace boost {
namespace hash2 {
enum class endian;
} // namespace hash2
} // namespace boost
endian
enum class endian
{
little = /*...*/,
big = /*...*/,
native = /*little or big*/
};
The enumeration type endian
corresponds to the standard
std::endian
type from C++20. Its values are little
,
signifying little-endian byte order, big
, signifying
big-endian byte order, and native
, which equals either
little
or big
depending on whether the current platform
is little- or big-endian.
Unlike std::endian
, platforms where little
equals big
,
or where native
equals neither little
or big
, aren’t
supported.
<boost/hash2/flavor.hpp>
#include <boost/hash2/endian.hpp>
namespace boost {
namespace hash2 {
struct default_flavor;
struct little_endian_flavor;
struct big_endian_flavor;
} // namespace hash2
} // namespace boost
The header boost/hash2/flavor.hpp
contains the predefined flavor types.
(A flavor is passed as the second argument to hash_append
in order to influence its behavior.)
Flavor types have two members, a type size_type
and a value byte_order
of type boost::hash2::endian
.
size_type
controls how the argument of hash_append_size
is treated (it’s converted to size_type
before hashing.)
byte_order
controls the endianness that is used to hash scalar types.
default_flavor
struct default_flavor
{
using size_type = std::uint64_t;
static constexpr auto byte_order = endian::native;
};
default_flavor
requests native, endian-dependent, hashing of scalar types.
This makes the hash values dependent on the endianness of the current platform, but has the potential of being substantially faster if large arrays of scalar types are being passed to hash_append
.
There is rarely a need to use default_flavor
explicitly, because it’s default when no flavor is given to hash_append
, like so: hash_append( h, {}, v );
little_endian_flavor
struct little_endian_flavor
{
using size_type = std::uint64_t;
static constexpr auto byte_order = endian::little;
};
little_endian_flavor
requests little endian hashing of scalar types.
This makes the hash values independent of the endianness of the underlying platform.
However, if the platform is big endian, hash_append
will be slower because it will need to convert scalar types to little endian.
big_endian_flavor
struct big_endian_flavor
{
using size_type = std::uint64_t;
static constexpr auto byte_order = endian::big;
};
big_endian_flavor
requests big endian hashing of scalar types.
This makes the hash values independent of the endianness of the underlying platform.
However, if the platform is little endian, which is very likely, hash_append
will be slower because it will need to convert scalar types to big endian.
<boost/hash2/get_integral_result.hpp>
namespace boost {
namespace hash2 {
template<class T, class R> constexpr T get_integral_result( R const& r ) noexcept;
} // namespace hash2
} // namespace boost
get_integral_result
template<class T, class R> constexpr T get_integral_result( R const& r ) noexcept;
- Requires:
-
T
must be an integral type that is notbool
.R
must be a valid hash algorithm result type; that is, it must be an unsigned integer type, or an array-like type with avalue_type
ofunsigned char
(std::array<unsigned char, N>
ordigest<N>
) and size of at least 8. - Returns:
-
A value that is derived from
r
in a way that is approximately uniformly distributed over the possible values ofT
.r
is assumed to have been produced by aresult()
invocation of a hash algorithm. - Remarks:
-
When
R
is an array-like type,get_integral_result
is allowed to assume thatr
has been produced by a high quality hash algorithm and that therefore its values are uniformly distributed over the entire domain ofR
. - Example:
-
template<class T, class Hash> struct my_hash { std::size_t operator()( std::string const& st ) const noexcept { Hash hash; boost::hash2::hash_append( hash, {}, st ); return boost::hash2::get_integral_result<std::size_t>( hash.result() ); } };
<boost/hash2/is_trivially_equality_comparable.hpp>
namespace boost {
namespace hash2 {
template<class T> struct is_trivially_equality_comparable;
} // namespace hash2
} // namespace boost
is_trivially_equality_comparable
template<class T> struct is_trivially_equality_comparable:
std::integral_constant< bool,
std::is_integral<T>::value || std::is_enum<T>::value || std::is_pointer<T>::value >
{
};
template<class T> struct is_trivially_equality_comparable<T const>:
is_trivially_equality_comparable<T>
{
};
The trait is_trivially_equality_comparable
is used by the library to detect types that are trivially equality comparable.
A type is trivially equality comparable if, for two values x
and y
of that type, x == y
is equivalent to std::memcmp( &x, &y, sizeof(x) ) == 0
.
That is, for trivially equality comparable types, comparing their values is the same as comparing their storage byte representations.
This allows hash_append
to assume that the message identifying an object’s value is the same as the storage bytes of the object.
is_trivially_equality_comparable
can be specialized for user-defined types if the default implementation does not give the correct result.
For example, for the following type
struct X
{
int v;
};
bool operator==( X const& x1, X const& x2 )
{
return x1.v == x2.v;
}
(under the assumption that it has no padding bytes, that is, sizeof(X) == sizeof(int)
)
is_trivially_equality_comparable<X>::value
will be false
by default, but the type meets the requirements for being trivially equality comparable, so a specialization can be added:
template<> struct boost::hash2::is_trivially_equality_comparable<X>: std::true_type {};
or, if you want to be on the safe side,
template<> struct boost::hash2::is_trivially_equality_comparable<X>:
std::integral_constant<bool, sizeof(X) == sizeof(int)> {};
On the other hand, the following type
enum E: unsigned {};
bool operator==( E e1, E e2 )
{
return e1 % 256 == e2 % 256;
}
is not trivially equality comparable (because (E)0x204 == (E)0x704
, but memcmp
will give a nonzero result), but is_trivially_equality_comparable<E>::value
will be true
by default.
In this (quite rare) case, a specialization can be added to report false
:
template<> struct boost::hash2::is_trivially_equality_comparable<E>: std::false_type {};
<boost/hash2/is_endian_independent.hpp>
namespace boost {
namespace hash2 {
template<class T> struct is_endian_independent;
} // namespace hash2
} // namespace boost
is_endian_independent
template<class T> struct is_endian_independent:
std::integral_constant< bool, sizeof(T) == 1 >
{
};
template<class T> struct is_endian_independent<T const>:
is_endian_independent<T>
{
};
The trait is_endian_independent
is used by the library to detect endian-independent types.
A type is endian-independent if its memory representation is the same on little-endian and big-endian platforms.
This includes all single byte types (with a sizeof
of 1) and all types whose constituent members are also endian-independent.
The default implementation of the trait only reports true
for single byte types. It can be specialized for endian independent user-defined types.
For example, the following type
struct X
{
unsigned char a;
unsigned char b;
unsigned char c;
};
is endian-independent, and is_endian_independent
can be specialized appropriately for it:
template<> struct boost::hash2::is_endian_independent<X>: std::true_type {};
<boost/hash2/is_contiguously_hashable.hpp>
#include <boost/hash2/endian.hpp>
#include <boost/hash2/is_trivially_equality_comparable.hpp>
#include <boost/hash2/is_endian_independent.hpp>
namespace boost {
namespace hash2 {
template<class T, endian E> struct is_contiguously_hashable;
} // namespace hash2
} // namespace boost
is_contiguously_hashable
template<class T, endian E> struct is_contiguously_hashable:
std::integral_constant<bool,
is_trivially_equality_comparable<T>::value &&
(E == endian::native || is_endian_independent<T>::value)>
{
};
template<class T, std::size_t N, endian E> struct is_contiguously_hashable<T[N], E>:
is_contiguously_hashable<T, E>
{
};
The trait is_contiguously_hashable
is used by the library to detect contiguously hashable types.
A type is contiguously hashable under a particular byte order E
if the message that would have been produced for the type if it weren’t considered contiguously hashable is the same as its underlying storage byte representation.
hash_append(hash, flavor, value)
, when the type of value
is contiguously hashable under the byte order requested by flavor
(decltype(flavor)::byte_order
), issues a single call to hash.update(&value, sizeof(value))
as an optimization.
is_contiguously_hashable
is not intended to be specialized for user-defined types. Its implementation relies on is_trivially_equality_comparable
and is_endian_independent
, and is correct as long as those underlying traits are correct.
<boost/hash2/has_constant_size.hpp>
namespace boost {
namespace hash2 {
template<class T> struct has_constant_size;
} // namespace hash2
} // namespace boost
has_constant_size
template<class T> struct has_constant_size<T>: std::integral_constant<bool, /*see below*/>
{
};
template<class T> struct has_constant_size<T const>: has_constant_size<T>
{
};
The trait has_constant_size
is used by the library to detect container and range types that have a constant size.
This allows hash_append
to not include the size in the message, as it doesn’t contribute to the object state.
A container or range type has constant size if for all values v
of that type, v.size()
has the same value.
The default implementation reports true
for tuple-like types (those for which std::tuple_size
is specialized), such as std::array
, for boost::array
, and for digest
.
has_constant_size
can be specialized for user-defined container and range types that have constant size.
For example, boost::uuids::uuid
has a constant size of 16, so a specialization can be added appropriately:
template<> struct boost::hash2::has_constant_size<boost::uuids::uuid>: std::true_type {};
Hashing C++ Objects
<boost/hash2/hash_append_fwd.hpp>
Synopsis
namespace boost {
namespace hash2 {
template<class Hash, class Flavor, class T>
constexpr void hash_append( Hash& h, Flavor const& f, T const& v );
template<class Hash, class Flavor, class It>
constexpr void hash_append_range( Hash& h, Flavor const& f, It first, It last );
template<class Hash, class Flavor, class T>
constexpr void hash_append_size( Hash& h, Flavor const& f, T const& v );
template<class Hash, class Flavor, class It>
constexpr void hash_append_sized_range( Hash& h, Flavor const& f, It first, It last );
template<class Hash, class Flavor, class It>
constexpr void hash_append_unordered_range( Hash& h, Flavor const& f, It first, It last );
struct hash_append_tag;
} // namespace hash2
} // namespace boost
The header boost/hash2/hash_append_fwd.hpp
declares the functions implemented in boost/hash2/hash_append.hpp
.
It can be used when code wishes to implement hash_append
support for a user-defined type without physically depending on the implementation of hash_append
.
Example
#include <boost/hash2/hash_append_fwd.hpp>
#include <vector>
class X
{
private:
int a = -1;
std::vector<int> b{ 1, 2, 3 };
template<class Hash, class Flavor>
friend void tag_invoke(
boost::hash2::hash_append_tag const&, Hash& h, Flavor const& f, X const& v )
{
boost::hash2::hash_append( h, f, v.a );
boost::hash2::hash_append( h, f, v.b );
}
public:
X() = default;
};
#include "X.hpp"
#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/md5.hpp>
#include <iostream>
int main()
{
X x;
boost::hash2::md5_128 hash;
boost::hash2::hash_append( hash, {}, x );
std::cout << "MD5 digest of x: " << hash.result() << std::endl;
}
<boost/hash2/hash_append.hpp>
#include <boost/hash2/flavor.hpp>
namespace boost {
namespace hash2 {
template<class Hash, class Flavor = default_flavor, class T>
constexpr void hash_append( Hash& h, Flavor const& f, T const& v );
template<class Hash, class Flavor = default_flavor, class It>
constexpr void hash_append_range( Hash& h, Flavor const& f, It first, It last );
template<class Hash, class Flavor = default_flavor, class T>
constexpr void hash_append_size( Hash& h, Flavor const& f, T const& v );
template<class Hash, class Flavor = default_flavor, class It>
constexpr void hash_append_sized_range( Hash& h, Flavor const& f, It first, It last );
template<class Hash, class Flavor = default_flavor, class It>
constexpr void hash_append_unordered_range( Hash& h, Flavor const& f, It first, It last );
struct hash_append_tag;
} // namespace hash2
} // namespace boost
hash_append
template<class Hash, class Flavor = default_flavor, class T>
constexpr void hash_append( Hash& h, Flavor const& f, T const& v );
Appends the representation of v
to the message stored in h
.
- Effects:
-
-
If
is_contiguously_hashable<T, Flavor::byte_order>::value
istrue
, callsh.update(&v, sizeof(v))
; -
If
std::is_integral<T>::value
istrue
, obtains a byte representation ofv
in the byte order requested byFlavor::byte_order
, then callsh.update(p, n)
wherep
is the address of that representation andn
issizeof(v)
; -
If
std::is_floating_point<T>::value
is true, first replacesv
with positive zero if it’s negative zero, then callshash_append(h, f, std::bit_cast<U>(v))
, whereU
is an unsigned integer type with the same size asT
; -
If
std::is_pointer<T>::value
istrue
, callshash_append(h, f, reinterpret_cast<std::uintptr_t>(v))
; -
If
T
isstd::nullptr_t
, callshash_append(h, f, static_cast<void*>(v))
; -
If
T
is an array typeU[N]
, callshash_append_range(h, f, v + 0, v + N)
; -
If a suitable overload of
tag_invoke
exists forT
, calls (unqualified)tag_invoke(hash_append_tag(), h, f, v)
; -
If
std::is_enum<T>::value
istrue
, callshash_append(h, f, w)
, wherew
isv
converted to the underlying type ofT
; -
If
boost::container_hash::is_unordered_range<T>::value
istrue
, callshash_append_unordered_range(h, f, v.begin(), v.end())
; -
If
boost::container_hash::is_contiguous_range<T>::value
istrue
and-
has_constant_size<T>::value
istrue
, callshash_append_range(h, f, v.data(), v.data() + v.size())
; -
has_constant_size<T>::value
isfalse
, callshash_append_sized_range(h, f, v.data(), v.data() + v.size())
;
-
-
If
boost::container_hash::is_range<T>::value
istrue
and-
has_constant_size<T>::value
istrue
, callshash_append_range(h, f, v.begin(), v.end())
; -
has_constant_size<T>::value
isfalse
, callshash_append_sized_range(h, f, v.begin(), v.end())
;
-
-
If
boost::container_hash::is_tuple_like<T>::value
istrue
, callshash_append(h, f, w)
for each tuple elementw
; -
If
boost::container_hash::is_described_class<T>::value
istrue
, callshash_append(h, f, b)
for each base class subobjectb
ofv
, thenhash_append(h, f, m)
for each member subobjectm
ofv
; -
Otherwise, the result is a compile-time error.
-
- Remarks:
-
In case the above description would result in no calls being made (e.g. for a range of constant size zero, or a described
struct
with no bases and members), a call tohash_append(h, f, '\x00')
is made to satisfy the requirement thathash_append
always results in at least one call toHash::update
.
hash_append_range
template<class Hash, class Flavor = default_flavor, class It>
constexpr void hash_append_range( Hash& h, Flavor const& f, It first, It last );
Appends the representations of the elements of the range [first, last)
to the message stored in h
.
- Requires:
-
It
must be an iterator type.[first, last)
must be a valid iterator range. - Effects:
-
-
If
It
isT*
andis_contiguously_hashable<T, Flavor::byte_order>::value
istrue
, callsh.update(first, (last - first) * sizeof(T));
. -
Otherwise, for each element
v
in the range denoted by[first, last)
, callshash_append(h, f, v);
.
-
- Remarks:
-
If
hash_append_range
is called in a constant expression, the contiguously hashable optimization is only applied forunsigned char*
andunsigned char const*
.
hash_append_size
template<class Hash, class Flavor = default_flavor, class T>
constexpr void hash_append_size( Hash& h, Flavor const& f, T const& v );
Appends the representation of v
, converted to Flavor::size_type
, to the message stored in h
.
- Requires:
-
T
must be an integral type. - Effects:
-
Equivalent to
hash_append(h, f, static_cast<typename Flavor::size_type>(v));
hash_append_sized_range
template<class Hash, class Flavor = default_flavor, class It>
constexpr void hash_append_sized_range( Hash& h, Flavor const& f, It first, It last );
Appends the representations of the elements of the range [first, last)
, followed by the size of the range, to the message stored in h
.
- Requires:
-
It
must be an iterator type.[first, last)
must be a valid iterator range. - Effects:
-
Equivalent to
hash_append_range(h, f, first, last); hash_append(h, f, m);
, wherem
isstd::distance(first, last)
.
hash_append_unordered_range
template<class Hash, class Flavor = default_flavor, class It>
constexpr void hash_append_unordered_range( Hash& h, Flavor const& f, It first, It last );
Constructs a value from the representations of the elements of the range [first, last)
, in a way such that their order doesn’t affect the result, then appends that value, followed by the size of the range, to the message stored in h
.
- Requires:
-
It
must be an iterator type.[first, last)
must be a valid iterator range. - Effects:
-
For each element
v
in the range denoted by[first, last)
, obtains a hash valuer
by doingHash h2(h); hash_append(h2, f, v); auto r = h2.result();
and then combines the so obtained
r
values in a way that is not sensitive to their order, producing a combined valueq
. Callshash_append(h, f, q)
, followed byhash_append(h, f, m)
, wherem
isstd::distance(first, last)
.
Copyright and License
This documentation is copyright 2020, 2024 Peter Dimov and Christian Mazakas and is distributed under the Boost Software License, Version 1.0.