diff --git a/.gitignore b/.gitignore
index 5e162614aa..0cda894e60 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,4 @@ Builds/VisualStudio2012/Debug
Builds/VisualStudio2012/Release
project.xcworkspace
modules/beast_cryptopp
+bin/
diff --git a/Builds/VisualStudio2013/beast.vcxproj b/Builds/VisualStudio2013/beast.vcxproj
index 41e22745e2..b104bb7b51 100644
--- a/Builds/VisualStudio2013/beast.vcxproj
+++ b/Builds/VisualStudio2013/beast.vcxproj
@@ -66,6 +66,7 @@
true
+
@@ -130,6 +131,9 @@
+
+
+
@@ -245,9 +249,11 @@
+
+
@@ -430,6 +436,12 @@
true
+
+ true
+ true
+ true
+ true
+
true
true
@@ -448,6 +460,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
diff --git a/Builds/VisualStudio2013/beast.vcxproj.filters b/Builds/VisualStudio2013/beast.vcxproj.filters
index cce6101fc1..c827959fec 100644
--- a/Builds/VisualStudio2013/beast.vcxproj.filters
+++ b/Builds/VisualStudio2013/beast.vcxproj.filters
@@ -70,6 +70,7 @@
beast\asio
+
@@ -303,6 +304,9 @@
{c65af439-8c23-46c3-9b95-7da15651e5f6}
+
+ {18193ca8-bfb0-48af-b401-52ead0015ba5}
+
@@ -1185,6 +1189,21 @@
beast\container
+
+ beast\utility
+
+
+ beast\container\impl
+
+
+ beast\container
+
+
+ beast\container\tests
+
+
+ beast\utility
+
@@ -1643,6 +1662,12 @@
beast\container\tests
+
+ beast\container\tests
+
+
+ beast\container\impl
+
diff --git a/SConstruct b/SConstruct
index 2c6df89d6a..4ace976a43 100644
--- a/SConstruct
+++ b/SConstruct
@@ -45,7 +45,7 @@ def print_build_vars(env,var):
def print_build_config(env):
config_vars = ['CC', 'CXX', 'CFLAGS', 'CCFLAGS', 'CPPFLAGS',
- 'CXXFLAGS', 'LIBPATH', 'LINKFLAGS', 'LIBS']
+ 'CXXFLAGS', 'LIBPATH', 'LINKFLAGS', 'LIBS', 'BOOST_HOME']
sys.stdout.write("\nConfiguration:\n")
for var in config_vars:
print_build_vars(env,var)
@@ -143,6 +143,8 @@ def main():
env.Append(CXXFLAGS = [
'-std=c++11',
'-frtti',
+ '-O3',
+ '-fno-strict-aliasing',
'-g'
])
diff --git a/beast/config/PlatformConfig.h b/beast/config/PlatformConfig.h
index 5ee181322a..5e94998da3 100644
--- a/beast/config/PlatformConfig.h
+++ b/beast/config/PlatformConfig.h
@@ -102,7 +102,7 @@
#endif
#if ! (defined (DEBUG) || defined (_DEBUG) || defined (NDEBUG) || defined (_NDEBUG))
- #warning "Neither NDEBUG or DEBUG has been defined - you should set one of these to make it clear whether this is a release build,"
+// #warning "Neither NDEBUG or DEBUG has been defined - you should set one of these to make it clear whether this is a release build,"
#endif
#ifdef __LITTLE_ENDIAN__
diff --git a/beast/container/Container.cpp b/beast/container/Container.cpp
index d94f3087a8..d8eac03c3c 100644
--- a/beast/container/Container.cpp
+++ b/beast/container/Container.cpp
@@ -21,6 +21,9 @@
#include "../../BeastConfig.h"
#endif
+#include "impl/spookyv2.cpp"
+
#include "tests/aged_associative_container.test.cpp"
#include "tests/buffer_view.test.cpp"
#include "tests/hardened_hash.test.cpp"
+#include "tests/hash_append.test.cpp"
diff --git a/beast/container/hardened_hash.h b/beast/container/hardened_hash.h
index 6c76a5a091..ff1e519170 100644
--- a/beast/container/hardened_hash.h
+++ b/beast/container/hardened_hash.h
@@ -20,12 +20,16 @@
#ifndef BEAST_CONTAINER_HARDENED_HASH_H_INCLUDED
#define BEAST_CONTAINER_HARDENED_HASH_H_INCLUDED
-#include "../utility/is_call_possible.h"
+#include "hash_append.h"
+
+#include "impl/spookyv2.h"
#include "../utility/noexcept.h"
+#include
#include
#include
#include
+#include "../cxx14/type_traits.h" //
#include "../cxx14/utility.h" //
// When set to 1, makes the seed per-process instead
@@ -40,7 +44,6 @@
#endif
namespace beast {
-
namespace detail {
template
@@ -72,6 +75,12 @@ private:
#if BEAST_NO_HARDENED_HASH_INSTANCE_SEED
protected:
+ hardened_hash_base() noexcept = default;
+
+ hardened_hash_base(result_type) noexcept
+ {
+ }
+
result_type
seed() const noexcept
{
@@ -86,6 +95,11 @@ protected:
{
}
+ hardened_hash_base(result_type seed) noexcept
+ : m_seed (seed)
+ {
+ }
+
result_type
seed() const noexcept
{
@@ -99,70 +113,83 @@ private:
#endif
};
-}
+//------------------------------------------------------------------------------
+
+class spooky_wrapper
+{
+ SpookyHash state_;
+public:
+ spooky_wrapper (std::size_t seed1 = 1, std::size_t seed2 = 2) noexcept
+ {
+ state_.Init (seed1, seed2);
+ }
+
+ void
+ append (void const* key, std::size_t len) noexcept
+ {
+ state_.Update (key, len);
+ }
+
+ explicit
+ operator std::size_t() noexcept
+ {
+ std::uint64_t h1, h2;
+ state_.Final (&h1, &h2);
+ return static_cast (h1);
+ }
+};
+
+} // detail
+
+//------------------------------------------------------------------------------
/** A std compatible hash adapter that resists adversarial inputs.
- For this to work, one of the following must exist:
-
- * A member function of `T` called `hash_combine` with
- this signature:
+ For this to work, T must implement in its own namespace:
- @code
-
- void hash_combine (std::size_t&) const noexcept;
+ @code
- @endcode
+ template
+ void
+ hash_append (Hasher& h, T const& t) noexcept
+ {
+ // hash_append each base and member that should
+ // participate in forming the hash
+ using beast::hash_append;
+ hash_append (h, static_cast(t));
+ hash_append (h, static_cast(t));
+ // ...
+ hash_append (h, t.member1);
+ hash_append (h, t.member2);
+ // ...
+ }
- * A free function called `hash_combine`, found via argument
- dependent lookup, callable with this signature:
-
- @code
-
- void hash_combine (std::size_t, T const& t) noexcept;
-
- @endcode
+ @endcode
*/
-template
+template
class hardened_hash
: public detail::hardened_hash_base
{
+ typedef detail::hardened_hash_base base;
public:
typedef T argument_type;
using detail::hardened_hash_base ::result_type;
-private:
- BEAST_DEFINE_IS_CALL_POSSIBLE(has_hash_combine,hash_combine);
-
- typedef detail::hardened_hash_base base;
-
- // Called when hash_combine is a member function
- result_type
- operator() (argument_type const& key, std::true_type) const noexcept
- {
- result_type result (base::seed());
- key.hash_combine (result);
- return result;
- }
-
- result_type
- operator() (argument_type const& key, std::false_type) const noexcept
- {
- result_type result (base::seed());
- hash_combine (result, key);
- return result;
- }
-
public:
hardened_hash() = default;
+ explicit hardened_hash(result_type seed)
+ : base (seed)
+ {
+ }
result_type
operator() (argument_type const& key) const noexcept
{
- return operator() (key, std::integral_constant ::value>());
+ Hasher h {base::seed()};
+ hash_append (h, key);
+ return static_cast (h);
}
};
-}
+} // beast
#endif
diff --git a/beast/container/hash_append.h b/beast/container/hash_append.h
new file mode 100644
index 0000000000..9ea2ba7c5a
--- /dev/null
+++ b/beast/container/hash_append.h
@@ -0,0 +1,985 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2014, Howard Hinnant ,
+ Vinnie Falco
+#include
+#endif
+
+#include "../utility/noexcept.h"
+#include
+#include
+#include
+#include
+#include
+#include "../cxx14/type_traits.h" //
+#include "../cxx14/utility.h" //
+#include
+
+// Set to 1 to disable variadic hash_append for tuple. When set, overloads
+// will be manually provided for tuples up to 10-arity. This also causes
+// is_contiguously_hashable<> to always return false for tuples.
+//
+#ifndef BEAST_NO_TUPLE_VARIADICS
+# ifdef _MSC_VER
+# define BEAST_NO_TUPLE_VARIADICS 1
+# ifndef BEAST_VARIADIC_MAX
+# ifdef _VARIADIC_MAX
+# define BEAST_VARIADIC_MAX _VARIADIC_MAX
+# else
+# define BEAST_VARIADIC_MAX 10
+# endif
+# endif
+# else
+# define BEAST_NO_TUPLE_VARIADICS 0
+# endif
+#endif
+
+// Set to 1 if std::pair fails the trait test on a platform.
+#ifndef BEAST_NO_IS_CONTIGUOUS_HASHABLE_PAIR
+#define BEAST_NO_IS_CONTIGUOUS_HASHABLE_PAIR 0
+#endif
+
+// Set to 1 if std::tuple fails the trait test on a platform.
+#ifndef BEAST_NO_IS_CONTIGUOUS_HASHABLE_TUPLE
+# ifdef _MSC_VER
+# define BEAST_NO_IS_CONTIGUOUS_HASHABLE_TUPLE 1
+# else
+# define BEAST_NO_IS_CONTIGUOUS_HASHABLE_TUPLE 0
+# endif
+#endif
+
+namespace beast {
+
+/** Metafunction returning `true` if the type can be hashed in one call.
+
+ For `is_contiguously_hashable::value` to be true, then for every
+ combination of possible values of `T` held in `x` and `y`,
+ if `x == y`, then it must be true that `memcmp(&x, &y, sizeof(T))`
+ return 0; i.e. that `x` and `y` are represented by the same bit pattern.
+
+ For example: A two's complement `int` should be contiguously hashable.
+ Every bit pattern produces a unique value that does not compare equal to
+ any other bit pattern's value. A IEEE floating point should not be
+ contiguously hashable because -0. and 0. have different bit patterns,
+ though they compare equal.
+*/
+/** @{ */
+// scalars
+template
+struct is_contiguously_hashable
+ : public std::integral_constant ::value ||
+ std::is_enum::value ||
+ std::is_pointer::value>
+{
+};
+
+// If this fails, something is wrong with the trait
+static_assert (is_contiguously_hashable::value, "");
+
+// pair
+template
+struct is_contiguously_hashable >
+ : public std::integral_constant ::value &&
+ is_contiguously_hashable::value &&
+ sizeof(T) + sizeof(U) == sizeof(std::pair)>
+{
+};
+
+#if ! BEAST_NO_IS_CONTIGUOUS_HASHABLE_PAIR
+static_assert (is_contiguously_hashable >::value, "");
+#endif
+
+#if ! BEAST_NO_TUPLE_VARIADICS
+// std::tuple
+template
+struct is_contiguously_hashable >
+ : public std::integral_constant ::value...>::value &&
+ static_sum ::value == sizeof(std::tuple)>
+{
+};
+#endif
+
+// std::array
+template
+struct is_contiguously_hashable >
+ : public std::integral_constant ::value &&
+ sizeof(T)*N == sizeof(std::array)>
+{
+};
+
+static_assert (is_contiguously_hashable >::value, "");
+
+#if ! BEAST_NO_IS_CONTIGUOUS_HASHABLE_TUPLE
+static_assert (is_contiguously_hashable <
+ std::tuple >::value, "");
+#endif
+
+#if BEAST_USE_BOOST_FEATURES
+
+#if ! BEAST_NO_TUPLE_VARIADICS
+// boost::tuple
+template
+struct is_contiguously_hashable >
+ : public std::integral_constant ::value...>::value &&
+ static_sum ::value == sizeof(boost::tuple)>
+{
+};
+#endif
+
+// boost::array
+template
+struct is_contiguously_hashable >
+ : public std::integral_constant ::value &&
+ sizeof(T)*N == sizeof(boost::array)>
+{
+};
+
+static_assert (is_contiguously_hashable >::value, "");
+
+#endif // BEAST_USE_BOOST_FEATURES
+/** @} */
+
+//------------------------------------------------------------------------------
+
+/** Logically concatenate input data to a `Hasher`.
+
+ Hasher requirements:
+
+ `X` is the type `Hasher`
+ `h` is a value of type `x`
+ `p` is a value convertible to `void const*`
+ `n` is a value of type `std::size_t`, greater than zero
+
+ Expression:
+ `h.append (p, n);`
+ Throws:
+ Never
+ Effect:
+ Adds the input data to the hasher state.
+
+ Expression:
+ `static_cast(j)`
+ Throws:
+ Never
+ Effect:
+ Returns the reslting hash of all the input data.
+*/
+/** @{ */
+
+// scalars
+
+template
+inline
+typename std::enable_if
+<
+ is_contiguously_hashable::value
+>::type
+hash_append (Hasher& h, T const& t) noexcept
+{
+ h.append (&t, sizeof(t));
+}
+
+template
+inline
+typename std::enable_if
+<
+ std::is_floating_point::value
+>::type
+hash_append (Hasher& h, T t) noexcept
+{
+ // hash both signed zeroes identically
+ if (t == 0)
+ t = 0;
+ h.append (&t, sizeof(t));
+}
+
+// arrays
+
+template
+inline
+typename std::enable_if
+<
+ !is_contiguously_hashable::value
+>::type
+hash_append (Hasher& h, T (&a)[N]) noexcept
+{
+ for (auto const& t : a)
+ hash_append (h, t);
+}
+
+template
+inline
+typename std::enable_if
+<
+ is_contiguously_hashable::value
+>::type
+hash_append (Hasher& h, T (&a)[N]) noexcept
+{
+ h.append (a, N*sizeof(T));
+}
+
+// nullptr_t
+
+template
+inline
+void
+hash_append (Hasher& h, std::nullptr_t p) noexcept
+{
+ h.append (&p, sizeof(p));
+}
+
+// strings
+
+template
+inline
+void
+hash_append (Hasher& h,
+ std::basic_string const& s) noexcept
+{
+ h.append (s.data (), (s.size()+1)*sizeof(CharT));
+}
+
+//------------------------------------------------------------------------------
+
+// Forward declare hash_append for all containers. This is required so that
+// argument dependent lookup works recursively (i.e. containers of containers).
+
+template
+typename std::enable_if
+<
+ !is_contiguously_hashable>::value
+>::type
+hash_append (Hasher& h, std::pair const& p) noexcept;
+
+template
+typename std::enable_if
+<
+ !is_contiguously_hashable::value
+>::type
+hash_append (Hasher& h, std::vector const& v) noexcept;
+
+template
+typename std::enable_if
+<
+ is_contiguously_hashable::value
+>::type
+hash_append (Hasher& h, std::vector const& v) noexcept;
+
+template
+typename std::enable_if
+<
+ !is_contiguously_hashable>::value
+>::type
+hash_append (Hasher& h, std::array const& a) noexcept;
+
+#if BEAST_USE_BOOST_FEATURES
+
+template
+typename std::enable_if
+<
+ !is_contiguously_hashable>::value
+>::type
+hash_append (Hasher& h, boost::array const& a) noexcept;
+
+#endif // BEAST_USE_BOOST_FEATURES
+
+// std::tuple
+
+template
+inline
+void
+hash_append (Hasher& h, std::tuple<> const& t) noexcept;
+
+#if BEAST_NO_TUPLE_VARIADICS
+
+#if BEAST_VARIADIC_MAX >= 1
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 2
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 3
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 4
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 5
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 6
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6> const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 7
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6, T7> const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 8
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8> const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 9
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8, T9> const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 10
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> const& t) noexcept;
+#endif
+
+// boost::tuple
+
+#if BEAST_USE_BOOST_FEATURES
+
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple<> const& t) noexcept;
+
+#if BEAST_VARIADIC_MAX >= 1
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 2
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 3
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 4
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 5
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 6
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6> const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 7
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6, T7> const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 8
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8> const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 9
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8, T9> const& t) noexcept;
+#endif
+
+#if BEAST_VARIADIC_MAX >= 10
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> const& t) noexcept;
+#endif
+
+#endif // BEAST_USE_BOOST_FEATURES
+
+#endif // BEAST_NO_TUPLE_VARIADICS
+
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+template
+inline
+int
+hash_one (Hasher& h, T const& t) noexcept
+{
+ hash_append (h, t);
+ return 0;
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+// std::tuple
+
+template
+inline
+void
+hash_append (Hasher& h, std::tuple<> const& t) noexcept
+{
+ hash_append (h, nullptr);
+}
+
+#if BEAST_NO_TUPLE_VARIADICS
+
+#if BEAST_VARIADIC_MAX >= 1
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 2
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 3
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+ hash_append (h, std::get<2>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 4
+template
+inline
+void
+hash_append (Hasher& h, std::tuple const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+ hash_append (h, std::get<2>(t));
+ hash_append (h, std::get<3>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 5
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5> const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+ hash_append (h, std::get<2>(t));
+ hash_append (h, std::get<3>(t));
+ hash_append (h, std::get<4>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 6
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6> const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+ hash_append (h, std::get<2>(t));
+ hash_append (h, std::get<3>(t));
+ hash_append (h, std::get<4>(t));
+ hash_append (h, std::get<5>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 7
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6, T7> const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+ hash_append (h, std::get<2>(t));
+ hash_append (h, std::get<3>(t));
+ hash_append (h, std::get<4>(t));
+ hash_append (h, std::get<5>(t));
+ hash_append (h, std::get<6>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 8
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8> const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+ hash_append (h, std::get<2>(t));
+ hash_append (h, std::get<3>(t));
+ hash_append (h, std::get<4>(t));
+ hash_append (h, std::get<5>(t));
+ hash_append (h, std::get<6>(t));
+ hash_append (h, std::get<7>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 9
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8, T9> const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+ hash_append (h, std::get<2>(t));
+ hash_append (h, std::get<3>(t));
+ hash_append (h, std::get<4>(t));
+ hash_append (h, std::get<5>(t));
+ hash_append (h, std::get<6>(t));
+ hash_append (h, std::get<7>(t));
+ hash_append (h, std::get<8>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 10
+template
+inline
+void
+hash_append (Hasher& h, std::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> const& t) noexcept
+{
+ hash_append (h, std::get<0>(t));
+ hash_append (h, std::get<1>(t));
+ hash_append (h, std::get<2>(t));
+ hash_append (h, std::get<3>(t));
+ hash_append (h, std::get<4>(t));
+ hash_append (h, std::get<5>(t));
+ hash_append (h, std::get<6>(t));
+ hash_append (h, std::get<7>(t));
+ hash_append (h, std::get<8>(t));
+ hash_append (h, std::get<9>(t));
+}
+#endif
+
+#else // BEAST_NO_TUPLE_VARIADICS
+
+namespace detail {
+
+template
+inline
+void
+tuple_hash (Hasher& h, std::tuple const& t,
+ std::index_sequence) noexcept
+{
+ struct for_each_item {
+ for_each_item (...) { }
+ };
+ for_each_item (hash_one(h, std::get(t))...);
+}
+
+} // detail
+
+template
+inline
+typename std::enable_if
+<
+ !is_contiguously_hashable>::value
+>::type
+hash_append (Hasher& h, std::tuple const& t) noexcept
+{
+ detail::tuple_hash(h, t, std::index_sequence_for{});
+}
+
+#endif // BEAST_NO_TUPLE_VARIADICS
+
+// boost::tuple
+
+#if BEAST_USE_BOOST_FEATURES
+
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple<> const& t) noexcept
+{
+ hash_append (h, nullptr);
+}
+
+#if BEAST_NO_TUPLE_VARIADICS
+
+#if BEAST_VARIADIC_MAX >= 1
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 2
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 3
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+ hash_append (h, boost::get<2>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 4
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+ hash_append (h, boost::get<2>(t));
+ hash_append (h, boost::get<3>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 5
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5> const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+ hash_append (h, boost::get<2>(t));
+ hash_append (h, boost::get<3>(t));
+ hash_append (h, boost::get<4>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 6
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6> const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+ hash_append (h, boost::get<2>(t));
+ hash_append (h, boost::get<3>(t));
+ hash_append (h, boost::get<4>(t));
+ hash_append (h, boost::get<5>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 7
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6, T7> const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+ hash_append (h, boost::get<2>(t));
+ hash_append (h, boost::get<3>(t));
+ hash_append (h, boost::get<4>(t));
+ hash_append (h, boost::get<5>(t));
+ hash_append (h, boost::get<6>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 8
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8> const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+ hash_append (h, boost::get<2>(t));
+ hash_append (h, boost::get<3>(t));
+ hash_append (h, boost::get<4>(t));
+ hash_append (h, boost::get<5>(t));
+ hash_append (h, boost::get<6>(t));
+ hash_append (h, boost::get<7>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 9
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8, T9> const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+ hash_append (h, boost::get<2>(t));
+ hash_append (h, boost::get<3>(t));
+ hash_append (h, boost::get<4>(t));
+ hash_append (h, boost::get<5>(t));
+ hash_append (h, boost::get<6>(t));
+ hash_append (h, boost::get<7>(t));
+ hash_append (h, boost::get<8>(t));
+}
+#endif
+
+#if BEAST_VARIADIC_MAX >= 10
+template
+inline
+void
+hash_append (Hasher& h, boost::tuple <
+ T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> const& t) noexcept
+{
+ hash_append (h, boost::get<0>(t));
+ hash_append (h, boost::get<1>(t));
+ hash_append (h, boost::get<2>(t));
+ hash_append (h, boost::get<3>(t));
+ hash_append (h, boost::get<4>(t));
+ hash_append (h, boost::get<5>(t));
+ hash_append (h, boost::get<6>(t));
+ hash_append (h, boost::get<7>(t));
+ hash_append (h, boost::get<8>(t));
+ hash_append (h, boost::get<9>(t));
+}
+#endif
+
+#else // BEAST_NO_TUPLE_VARIADICS
+
+namespace detail {
+
+template
+inline
+void
+tuple_hash (Hasher& h, boost::tuple const& t,
+ std::index_sequence) noexcept
+{
+ struct for_each_item {
+ for_each_item (...) { }
+ };
+ for_each_item (hash_one(h, boost::get(t))...);
+}
+
+} // detail
+
+template
+inline
+typename std::enable_if
+<
+ !is_contiguously_hashable>::value
+>::type
+hash_append (Hasher& h, boost::tuple const& t) noexcept
+{
+ detail::tuple_hash(h, t, std::index_sequence_for{});
+}
+
+#endif // BEAST_NO_TUPLE_VARIADICS
+
+#endif // BEAST_USE_BOOST_FEATURES
+
+// pair
+
+template
+inline
+typename std::enable_if
+<
+ !is_contiguously_hashable>::value
+>::type
+hash_append (Hasher& h, std::pair const& p) noexcept
+{
+ hash_append (h, p.first);
+ hash_append (h, p.second);
+}
+
+// vector
+
+template
+inline
+typename std::enable_if
+<
+ !is_contiguously_hashable::value
+>::type
+hash_append (Hasher& h, std::vector const& v) noexcept
+{
+ for (auto const& t : v)
+ hash_append (h, t);
+}
+
+template
+inline
+typename std::enable_if
+<
+ is_contiguously_hashable::value
+>::type
+hash_append (Hasher& h, std::vector const& v) noexcept
+{
+ h.append (v.data(), v.size()*sizeof(T));
+}
+
+#if BEAST_USE_BOOST_FEATURES
+
+// boost::array
+
+template
+inline
+std::enable_if_t
+<
+ !is_contiguously_hashable::value
+>
+hash_append (Hasher& h, boost::array const& v) noexcept
+{
+ for (auto const& t : v)
+ hash_append (h, t);
+}
+
+#endif // BEAST_USE_BOOST_FEATURES
+
+template
+inline
+void
+hash_append (Hasher& h, T0 const& t0, T1 const& t1, T const& ...t) noexcept
+{
+ hash_append (h, t0);
+ hash_append (h, t1, t...);
+}
+
+} // beast
+
+#endif
diff --git a/beast/container/impl/spookyv2.cpp b/beast/container/impl/spookyv2.cpp
new file mode 100644
index 0000000000..9b7721c38b
--- /dev/null
+++ b/beast/container/impl/spookyv2.cpp
@@ -0,0 +1,360 @@
+// Spooky Hash
+// A 128-bit noncryptographic hash, for checksums and table lookup
+// By Bob Jenkins. Public domain.
+// Oct 31 2010: published framework, disclaimer ShortHash isn't right
+// Nov 7 2010: disabled ShortHash
+// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again
+// April 10 2012: buffer overflow on platforms without unaligned reads
+// July 12 2012: was passing out variables in final to in/out in short
+// July 30 2012: I reintroduced the buffer overflow
+// August 5 2012: SpookyV2: d = should be d += in short hash, and remove extra mix from long hash
+
+#include
+#include "spookyv2.h"
+
+#ifdef _MSC_VER
+#pragma warning (push)
+#pragma warning (disable: 4127) // conditional expression is constant
+#pragma warning (disable: 4244) // conversion from 'size_t' to 'uint8', possible loss of data
+#endif
+
+#define ALLOW_UNALIGNED_READS 1
+
+//
+// short hash ... it could be used on any message,
+// but it's used by Spooky just for short messages.
+//
+void SpookyHash::Short(
+ const void *message,
+ size_t length,
+ uint64 *hash1,
+ uint64 *hash2)
+{
+ uint64 buf[2*sc_numVars];
+ union
+ {
+ const uint8 *p8;
+ uint32 *p32;
+ uint64 *p64;
+ size_t i;
+ } u;
+
+ u.p8 = (const uint8 *)message;
+
+ if (!ALLOW_UNALIGNED_READS && (u.i & 0x7))
+ {
+ memcpy(buf, message, length);
+ u.p64 = buf;
+ }
+
+ size_t remainder = length%32;
+ uint64 a=*hash1;
+ uint64 b=*hash2;
+ uint64 c=sc_const;
+ uint64 d=sc_const;
+
+ if (length > 15)
+ {
+ const uint64 *end = u.p64 + (length/32)*4;
+
+ // handle all complete sets of 32 bytes
+ for (; u.p64 < end; u.p64 += 4)
+ {
+ c += u.p64[0];
+ d += u.p64[1];
+ ShortMix(a,b,c,d);
+ a += u.p64[2];
+ b += u.p64[3];
+ }
+
+ //Handle the case of 16+ remaining bytes.
+ if (remainder >= 16)
+ {
+ c += u.p64[0];
+ d += u.p64[1];
+ ShortMix(a,b,c,d);
+ u.p64 += 2;
+ remainder -= 16;
+ }
+ }
+
+ // Handle the last 0..15 bytes, and its length
+ d += ((uint64)length) << 56;
+ switch (remainder)
+ {
+ case 15:
+ d += ((uint64)u.p8[14]) << 48;
+ case 14:
+ d += ((uint64)u.p8[13]) << 40;
+ case 13:
+ d += ((uint64)u.p8[12]) << 32;
+ case 12:
+ d += u.p32[2];
+ c += u.p64[0];
+ break;
+ case 11:
+ d += ((uint64)u.p8[10]) << 16;
+ case 10:
+ d += ((uint64)u.p8[9]) << 8;
+ case 9:
+ d += (uint64)u.p8[8];
+ case 8:
+ c += u.p64[0];
+ break;
+ case 7:
+ c += ((uint64)u.p8[6]) << 48;
+ case 6:
+ c += ((uint64)u.p8[5]) << 40;
+ case 5:
+ c += ((uint64)u.p8[4]) << 32;
+ case 4:
+ c += u.p32[0];
+ break;
+ case 3:
+ c += ((uint64)u.p8[2]) << 16;
+ case 2:
+ c += ((uint64)u.p8[1]) << 8;
+ case 1:
+ c += (uint64)u.p8[0];
+ break;
+ case 0:
+ c += sc_const;
+ d += sc_const;
+ }
+ ShortEnd(a,b,c,d);
+ *hash1 = a;
+ *hash2 = b;
+}
+
+
+
+
+// do the whole hash in one call
+void SpookyHash::Hash128(
+ const void *message,
+ size_t length,
+ uint64 *hash1,
+ uint64 *hash2)
+{
+ if (length < sc_bufSize)
+ {
+ Short(message, length, hash1, hash2);
+ return;
+ }
+
+ uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
+ uint64 buf[sc_numVars];
+ uint64 *end;
+ union
+ {
+ const uint8 *p8;
+ uint64 *p64;
+ size_t i;
+ } u;
+ size_t remainder;
+
+ h0=h3=h6=h9 = *hash1;
+ h1=h4=h7=h10 = *hash2;
+ h2=h5=h8=h11 = sc_const;
+
+ u.p8 = (const uint8 *)message;
+ end = u.p64 + (length/sc_blockSize)*sc_numVars;
+
+ // handle all whole sc_blockSize blocks of bytes
+ if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0))
+ {
+ while (u.p64 < end)
+ {
+ Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p64 += sc_numVars;
+ }
+ }
+ else
+ {
+ while (u.p64 < end)
+ {
+ memcpy(buf, u.p64, sc_blockSize);
+ Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p64 += sc_numVars;
+ }
+ }
+
+ // handle the last partial block of sc_blockSize bytes
+ remainder = (length - ((const uint8 *)end-(const uint8 *)message));
+ memcpy(buf, end, remainder);
+ memset(((uint8 *)buf)+remainder, 0, sc_blockSize-remainder);
+ ((uint8 *)buf)[sc_blockSize-1] = remainder;
+
+ // do some final mixing
+ End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ *hash1 = h0;
+ *hash2 = h1;
+}
+
+
+
+// init spooky state
+void SpookyHash::Init(uint64 seed1, uint64 seed2)
+{
+ m_length = 0;
+ m_remainder = 0;
+ m_state[0] = seed1;
+ m_state[1] = seed2;
+}
+
+
+// add a message fragment to the state
+void SpookyHash::Update(const void *message, size_t length)
+{
+ uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
+ size_t newLength = length + m_remainder;
+ uint8 remainder;
+ union
+ {
+ const uint8 *p8;
+ uint64 *p64;
+ size_t i;
+ } u;
+ const uint64 *end;
+
+ // Is this message fragment too short? If it is, stuff it away.
+ if (newLength < sc_bufSize)
+ {
+ memcpy(&((uint8 *)m_data)[m_remainder], message, length);
+ m_length = length + m_length;
+ m_remainder = (uint8)newLength;
+ return;
+ }
+
+ // init the variables
+ if (m_length < sc_bufSize)
+ {
+ h0=h3=h6=h9 = m_state[0];
+ h1=h4=h7=h10 = m_state[1];
+ h2=h5=h8=h11 = sc_const;
+ }
+ else
+ {
+ h0 = m_state[0];
+ h1 = m_state[1];
+ h2 = m_state[2];
+ h3 = m_state[3];
+ h4 = m_state[4];
+ h5 = m_state[5];
+ h6 = m_state[6];
+ h7 = m_state[7];
+ h8 = m_state[8];
+ h9 = m_state[9];
+ h10 = m_state[10];
+ h11 = m_state[11];
+ }
+ m_length = length + m_length;
+
+ // if we've got anything stuffed away, use it now
+ if (m_remainder)
+ {
+ uint8 prefix = sc_bufSize-m_remainder;
+ memcpy(&(((uint8 *)m_data)[m_remainder]), message, prefix);
+ u.p64 = m_data;
+ Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p8 = ((const uint8 *)message) + prefix;
+ length -= prefix;
+ }
+ else
+ {
+ u.p8 = (const uint8 *)message;
+ }
+
+ // handle all whole blocks of sc_blockSize bytes
+ end = u.p64 + (length/sc_blockSize)*sc_numVars;
+ remainder = (uint8)(length-((const uint8 *)end-u.p8));
+ if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0)
+ {
+ while (u.p64 < end)
+ {
+ Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p64 += sc_numVars;
+ }
+ }
+ else
+ {
+ while (u.p64 < end)
+ {
+ memcpy(m_data, u.p8, sc_blockSize);
+ Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p64 += sc_numVars;
+ }
+ }
+
+ // stuff away the last few bytes
+ m_remainder = remainder;
+ memcpy(m_data, end, remainder);
+
+ // stuff away the variables
+ m_state[0] = h0;
+ m_state[1] = h1;
+ m_state[2] = h2;
+ m_state[3] = h3;
+ m_state[4] = h4;
+ m_state[5] = h5;
+ m_state[6] = h6;
+ m_state[7] = h7;
+ m_state[8] = h8;
+ m_state[9] = h9;
+ m_state[10] = h10;
+ m_state[11] = h11;
+}
+
+
+// report the hash for the concatenation of all message fragments so far
+void SpookyHash::Final(uint64 *hash1, uint64 *hash2)
+{
+ // init the variables
+ if (m_length < sc_bufSize)
+ {
+ *hash1 = m_state[0];
+ *hash2 = m_state[1];
+ Short( m_data, m_length, hash1, hash2);
+ return;
+ }
+
+ const uint64 *data = (const uint64 *)m_data;
+ uint8 remainder = m_remainder;
+
+ uint64 h0 = m_state[0];
+ uint64 h1 = m_state[1];
+ uint64 h2 = m_state[2];
+ uint64 h3 = m_state[3];
+ uint64 h4 = m_state[4];
+ uint64 h5 = m_state[5];
+ uint64 h6 = m_state[6];
+ uint64 h7 = m_state[7];
+ uint64 h8 = m_state[8];
+ uint64 h9 = m_state[9];
+ uint64 h10 = m_state[10];
+ uint64 h11 = m_state[11];
+
+ if (remainder >= sc_blockSize)
+ {
+ // m_data can contain two blocks; handle any whole first block
+ Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ data += sc_numVars;
+ remainder -= sc_blockSize;
+ }
+
+ // mix in the last partial block, and the length mod sc_blockSize
+ memset(&((uint8 *)data)[remainder], 0, (sc_blockSize-remainder));
+
+ ((uint8 *)data)[sc_blockSize-1] = remainder;
+
+ // do some final mixing
+ End(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+
+ *hash1 = h0;
+ *hash2 = h1;
+}
+
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
diff --git a/beast/container/impl/spookyv2.h b/beast/container/impl/spookyv2.h
new file mode 100644
index 0000000000..9e3b56c02c
--- /dev/null
+++ b/beast/container/impl/spookyv2.h
@@ -0,0 +1,301 @@
+//
+// SpookyHash: a 128-bit noncryptographic hash function
+// By Bob Jenkins, public domain
+// Oct 31 2010: alpha, framework + SpookyHash::Mix appears right
+// Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right
+// Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas
+// Feb 2 2012: production, same bits as beta
+// Feb 5 2012: adjusted definitions of uint* to be more portable
+// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough.
+// August 5 2012: SpookyV2 (different results)
+//
+// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages.
+// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit.
+//
+// This was developed for and tested on 64-bit x86-compatible processors.
+// It assumes the processor is little-endian. There is a macro
+// controlling whether unaligned reads are allowed (by default they are).
+// This should be an equally good hash on big-endian machines, but it will
+// compute different results on them than on little-endian machines.
+//
+// Google's CityHash has similar specs to SpookyHash, and CityHash is faster
+// on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders
+// of magnitude slower. CRCs are two or more times slower, but unlike
+// SpookyHash, they have nice math for combining the CRCs of pieces to form
+// the CRCs of wholes. There are also cryptographic hashes, but those are even
+// slower than MD5.
+//
+
+#ifndef BEAST_SPOOKYV2_H_INCLUDED
+#define BEAST_SPOOKYV2_H_INCLUDED
+
+#include
+
+#ifdef _MSC_VER
+# define INLINE __forceinline
+ typedef unsigned __int64 uint64;
+ typedef unsigned __int32 uint32;
+ typedef unsigned __int16 uint16;
+ typedef unsigned __int8 uint8;
+#else
+# include
+# define INLINE inline
+ typedef uint64_t uint64;
+ typedef uint32_t uint32;
+ typedef uint16_t uint16;
+ typedef uint8_t uint8;
+#endif
+
+
+class SpookyHash
+{
+public:
+ //
+ // SpookyHash: hash a single message in one call, produce 128-bit output
+ //
+ static void Hash128(
+ const void *message, // message to hash
+ size_t length, // length of message in bytes
+ uint64 *hash1, // in/out: in seed 1, out hash value 1
+ uint64 *hash2); // in/out: in seed 2, out hash value 2
+
+ //
+ // Hash64: hash a single message in one call, return 64-bit output
+ //
+ static uint64 Hash64(
+ const void *message, // message to hash
+ size_t length, // length of message in bytes
+ uint64 seed) // seed
+ {
+ uint64 hash1 = seed;
+ Hash128(message, length, &hash1, &seed);
+ return hash1;
+ }
+
+ //
+ // Hash32: hash a single message in one call, produce 32-bit output
+ //
+ static uint32 Hash32(
+ const void *message, // message to hash
+ size_t length, // length of message in bytes
+ uint32 seed) // seed
+ {
+ uint64 hash1 = seed, hash2 = seed;
+ Hash128(message, length, &hash1, &hash2);
+ return (uint32)hash1;
+ }
+
+ //
+ // Init: initialize the context of a SpookyHash
+ //
+ void Init(
+ uint64 seed1, // any 64-bit value will do, including 0
+ uint64 seed2); // different seeds produce independent hashes
+
+ //
+ // Update: add a piece of a message to a SpookyHash state
+ //
+ void Update(
+ const void *message, // message fragment
+ size_t length); // length of message fragment in bytes
+
+
+ //
+ // Final: compute the hash for the current SpookyHash state
+ //
+ // This does not modify the state; you can keep updating it afterward
+ //
+ // The result is the same as if SpookyHash() had been called with
+ // all the pieces concatenated into one message.
+ //
+ void Final(
+ uint64 *hash1, // out only: first 64 bits of hash value.
+ uint64 *hash2); // out only: second 64 bits of hash value.
+
+ //
+ // left rotate a 64-bit value by k bytes
+ //
+ static INLINE uint64 Rot64(uint64 x, int k)
+ {
+ return (x << k) | (x >> (64 - k));
+ }
+
+ //
+ // This is used if the input is 96 bytes long or longer.
+ //
+ // The internal state is fully overwritten every 96 bytes.
+ // Every input bit appears to cause at least 128 bits of entropy
+ // before 96 other bytes are combined, when run forward or backward
+ // For every input bit,
+ // Two inputs differing in just that input bit
+ // Where "differ" means xor or subtraction
+ // And the base value is random
+ // When run forward or backwards one Mix
+ // I tried 3 pairs of each; they all differed by at least 212 bits.
+ //
+ static INLINE void Mix(
+ const uint64 *data,
+ uint64 &s0, uint64 &s1, uint64 &s2, uint64 &s3,
+ uint64 &s4, uint64 &s5, uint64 &s6, uint64 &s7,
+ uint64 &s8, uint64 &s9, uint64 &s10,uint64 &s11)
+ {
+ s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = Rot64(s0,11); s11 += s1;
+ s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = Rot64(s1,32); s0 += s2;
+ s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = Rot64(s2,43); s1 += s3;
+ s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = Rot64(s3,31); s2 += s4;
+ s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = Rot64(s4,17); s3 += s5;
+ s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = Rot64(s5,28); s4 += s6;
+ s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = Rot64(s6,39); s5 += s7;
+ s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = Rot64(s7,57); s6 += s8;
+ s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = Rot64(s8,55); s7 += s9;
+ s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = Rot64(s9,54); s8 += s10;
+ s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = Rot64(s10,22); s9 += s11;
+ s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = Rot64(s11,46); s10 += s0;
+ }
+
+ //
+ // Mix all 12 inputs together so that h0, h1 are a hash of them all.
+ //
+ // For two inputs differing in just the input bits
+ // Where "differ" means xor or subtraction
+ // And the base value is random, or a counting value starting at that bit
+ // The final result will have each bit of h0, h1 flip
+ // For every input bit,
+ // with probability 50 +- .3%
+ // For every pair of input bits,
+ // with probability 50 +- 3%
+ //
+ // This does not rely on the last Mix() call having already mixed some.
+ // Two iterations was almost good enough for a 64-bit result, but a
+ // 128-bit result is reported, so End() does three iterations.
+ //
+ static INLINE void EndPartial(
+ uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3,
+ uint64 &h4, uint64 &h5, uint64 &h6, uint64 &h7,
+ uint64 &h8, uint64 &h9, uint64 &h10,uint64 &h11)
+ {
+ h11+= h1; h2 ^= h11; h1 = Rot64(h1,44);
+ h0 += h2; h3 ^= h0; h2 = Rot64(h2,15);
+ h1 += h3; h4 ^= h1; h3 = Rot64(h3,34);
+ h2 += h4; h5 ^= h2; h4 = Rot64(h4,21);
+ h3 += h5; h6 ^= h3; h5 = Rot64(h5,38);
+ h4 += h6; h7 ^= h4; h6 = Rot64(h6,33);
+ h5 += h7; h8 ^= h5; h7 = Rot64(h7,10);
+ h6 += h8; h9 ^= h6; h8 = Rot64(h8,13);
+ h7 += h9; h10^= h7; h9 = Rot64(h9,38);
+ h8 += h10; h11^= h8; h10= Rot64(h10,53);
+ h9 += h11; h0 ^= h9; h11= Rot64(h11,42);
+ h10+= h0; h1 ^= h10; h0 = Rot64(h0,54);
+ }
+
+ static INLINE void End(
+ const uint64 *data,
+ uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3,
+ uint64 &h4, uint64 &h5, uint64 &h6, uint64 &h7,
+ uint64 &h8, uint64 &h9, uint64 &h10,uint64 &h11)
+ {
+ h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3];
+ h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7];
+ h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11];
+ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ }
+
+ //
+ // The goal is for each bit of the input to expand into 128 bits of
+ // apparent entropy before it is fully overwritten.
+ // n trials both set and cleared at least m bits of h0 h1 h2 h3
+ // n: 2 m: 29
+ // n: 3 m: 46
+ // n: 4 m: 57
+ // n: 5 m: 107
+ // n: 6 m: 146
+ // n: 7 m: 152
+ // when run forwards or backwards
+ // for all 1-bit and 2-bit diffs
+ // with diffs defined by either xor or subtraction
+ // with a base of all zeros plus a counter, or plus another bit, or random
+ //
+ static INLINE void ShortMix(uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3)
+ {
+ h2 = Rot64(h2,50); h2 += h3; h0 ^= h2;
+ h3 = Rot64(h3,52); h3 += h0; h1 ^= h3;
+ h0 = Rot64(h0,30); h0 += h1; h2 ^= h0;
+ h1 = Rot64(h1,41); h1 += h2; h3 ^= h1;
+ h2 = Rot64(h2,54); h2 += h3; h0 ^= h2;
+ h3 = Rot64(h3,48); h3 += h0; h1 ^= h3;
+ h0 = Rot64(h0,38); h0 += h1; h2 ^= h0;
+ h1 = Rot64(h1,37); h1 += h2; h3 ^= h1;
+ h2 = Rot64(h2,62); h2 += h3; h0 ^= h2;
+ h3 = Rot64(h3,34); h3 += h0; h1 ^= h3;
+ h0 = Rot64(h0,5); h0 += h1; h2 ^= h0;
+ h1 = Rot64(h1,36); h1 += h2; h3 ^= h1;
+ }
+
+ //
+ // Mix all 4 inputs together so that h0, h1 are a hash of them all.
+ //
+ // For two inputs differing in just the input bits
+ // Where "differ" means xor or subtraction
+ // And the base value is random, or a counting value starting at that bit
+ // The final result will have each bit of h0, h1 flip
+ // For every input bit,
+ // with probability 50 +- .3% (it is probably better than that)
+ // For every pair of input bits,
+ // with probability 50 +- .75% (the worst case is approximately that)
+ //
+ static INLINE void ShortEnd(uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3)
+ {
+ h3 ^= h2; h2 = Rot64(h2,15); h3 += h2;
+ h0 ^= h3; h3 = Rot64(h3,52); h0 += h3;
+ h1 ^= h0; h0 = Rot64(h0,26); h1 += h0;
+ h2 ^= h1; h1 = Rot64(h1,51); h2 += h1;
+ h3 ^= h2; h2 = Rot64(h2,28); h3 += h2;
+ h0 ^= h3; h3 = Rot64(h3,9); h0 += h3;
+ h1 ^= h0; h0 = Rot64(h0,47); h1 += h0;
+ h2 ^= h1; h1 = Rot64(h1,54); h2 += h1;
+ h3 ^= h2; h2 = Rot64(h2,32); h3 += h2;
+ h0 ^= h3; h3 = Rot64(h3,25); h0 += h3;
+ h1 ^= h0; h0 = Rot64(h0,63); h1 += h0;
+ }
+
+private:
+
+ //
+ // Short is used for messages under 192 bytes in length
+ // Short has a low startup cost, the normal mode is good for long
+ // keys, the cost crossover is at about 192 bytes. The two modes were
+ // held to the same quality bar.
+ //
+ static void Short(
+ const void *message, // message (array of bytes, not necessarily aligned)
+ size_t length, // length of message (in bytes)
+ uint64 *hash1, // in/out: in the seed, out the hash value
+ uint64 *hash2); // in/out: in the seed, out the hash value
+
+ // number of uint64's in internal state
+ static const size_t sc_numVars = 12;
+
+ // size of the internal state
+ static const size_t sc_blockSize = sc_numVars*8;
+
+ // size of buffer of unhashed data, in bytes
+ static const size_t sc_bufSize = 2*sc_blockSize;
+
+ //
+ // sc_const: a constant which:
+ // * is not zero
+ // * is odd
+ // * is a not-very-regular mix of 1's and 0's
+ // * does not need any other special mathematical properties
+ //
+ static const uint64 sc_const = 0xdeadbeefdeadbeefUL;
+
+ uint64 m_data[2*sc_numVars]; // unhashed data, for partial messages
+ uint64 m_state[sc_numVars]; // internal state of the hash
+ size_t m_length; // total length of the input so far
+ uint8 m_remainder; // length of unhashed data stashed in m_data
+};
+
+#endif // BEAST_SPOOKYV2_H_INCLUDED
diff --git a/beast/container/tests/hardened_hash.test.cpp b/beast/container/tests/hardened_hash.test.cpp
index c086137367..a225b49941 100644
--- a/beast/container/tests/hardened_hash.test.cpp
+++ b/beast/container/tests/hardened_hash.test.cpp
@@ -51,10 +51,11 @@ public:
{
}
- void
- hash_combine (std::size_t& seed) const noexcept
+ template
+ friend void hash_append (Hasher& h, test_user_type_member const& a) noexcept
{
- boost::hash_combine (seed, t);
+ using beast::hash_append;
+ hash_append (h, a.t);
}
};
@@ -70,12 +71,11 @@ public:
{
}
- friend
- void
- hash_combine (std::size_t& seed,
- test_user_type_free const& v) noexcept
+ template
+ friend void hash_append (Hasher& h, test_user_type_free const& a) noexcept
{
- boost::hash_combine (seed, v.t);
+ using beast::hash_append;
+ hash_append (h, a.t);
}
};
@@ -154,11 +154,11 @@ public:
return &m_vec[0];
}
- void
- hash_combine (std::size_t& seed) const noexcept
+ template
+ friend void hash_append(Hasher& h, unsigned_integer const& a) noexcept
{
- for (std::size_t i (0); i < size; ++i)
- boost::hash_combine (seed, m_vec[i]);
+ using beast::hash_append;
+ hash_append (h, a.m_vec);
}
friend
diff --git a/beast/container/tests/hash_append.test.cpp b/beast/container/tests/hash_append.test.cpp
new file mode 100644
index 0000000000..a0d0c6ca1d
--- /dev/null
+++ b/beast/container/tests/hash_append.test.cpp
@@ -0,0 +1,457 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+// MODULES: ../impl/spookyv2.cpp
+
+#if BEAST_INCLUDE_BEASTCONFIG
+#include "../../../BeastConfig.h"
+#endif
+
+#include "hash_metrics.h"
+
+#include "../hash_append.h"
+#include "../impl/spookyv2.h"
+
+#include "../../chrono/chrono_io.h"
+#include "../../unit_test/suite.h"
+#include "../../utility/type_name.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace beast {
+
+//------------------------------------------------------------------------------
+
+template
+class block_stream
+{
+private:
+ Block m_block;
+ std::size_t m_size;
+
+ std::size_t
+ needed() const noexcept
+ {
+ return sizeof(Block) - m_size;
+ }
+
+ void*
+ tail() noexcept
+ {
+ return ((char *)&m_block) + m_size;
+ }
+
+protected:
+ void
+ finish()
+ {
+ if (m_size > 0)
+ {
+ // zero-pad
+ memset (tail(), 0, needed());
+ static_cast (this)->process_block (m_block);
+ }
+ }
+
+public:
+ block_stream ()
+ : m_size(0)
+ {
+ }
+
+ void
+ operator() (void const* data, std::size_t bytes) noexcept
+ {
+ // handle leftovers
+ if (m_size > 0)
+ {
+ std::size_t const n (std::min (needed(), bytes));
+ std::memcpy (tail(), data, n);
+ data = ((char const*)data) + n;
+ bytes -= n;
+ m_size += n;
+
+ if (m_size < sizeof(Block))
+ return;
+
+ static_cast (this)->process_block (m_block);
+ }
+
+ // loop over complete blocks
+ while (bytes >= sizeof(Block))
+ {
+ m_block = *((Block const*)data);
+ static_cast (this)->process_block (m_block);
+ data = ((char const*)data) + sizeof(m_block);
+ bytes -= sizeof(m_block);
+ }
+
+ // save leftovers
+ if (bytes > 0)
+ {
+ memcpy (tail(), data, bytes);
+ m_size += bytes;
+ }
+ }
+};
+
+//------------------------------------------------------------------------------
+
+namespace hash_append_tests {
+
+class fnv1a
+{
+private:
+ std::size_t state_ = 14695981039346656037u;
+
+public:
+ void
+ append (void const* key, std::size_t len) noexcept
+ {
+ unsigned char const* p = static_cast(key);
+ unsigned char const* const e = p + len;
+ for (; p < e; ++p)
+ state_ = (state_ ^ *p) * 1099511628211u;
+ }
+
+ explicit
+ operator std::size_t() noexcept
+ {
+ return state_;
+ }
+};
+
+class jenkins1
+{
+private:
+ std::size_t state_ = 0;
+
+public:
+ void
+ append (void const* key, std::size_t len) noexcept
+ {
+ unsigned char const* p = static_cast (key);
+ unsigned char const* const e = p + len;
+ for (; p < e; ++p)
+ {
+ state_ += *p;
+ state_ += state_ << 10;
+ state_ ^= state_ >> 6;
+ }
+ }
+
+ explicit
+ operator std::size_t() noexcept
+ {
+ state_ += state_ << 3;
+ state_ ^= state_ >> 11;
+ state_ += state_ << 15;
+ return state_;
+ }
+};
+
+class spooky
+{
+private:
+ SpookyHash state_;
+
+public:
+ spooky(std::size_t seed1 = 1, std::size_t seed2 = 2) noexcept
+ {
+ state_.Init(seed1, seed2);
+ }
+
+ void
+ append(void const* key, std::size_t len) noexcept
+ {
+ state_.Update(key, len);
+ }
+
+ explicit
+ operator std::size_t() noexcept
+ {
+ std::uint64_t h1, h2;
+ state_.Final(&h1, &h2);
+ return h1;
+ }
+
+};
+
+template <
+ class PRNG = std::conditional_t <
+ sizeof(std::size_t)==sizeof(std::uint64_t),
+ std::mt19937_64,
+ std::mt19937
+ >
+>
+class prng_hasher
+ : public block_stream >
+{
+private:
+ std::size_t m_seed;
+ PRNG m_prng;
+
+ typedef block_stream > base;
+ friend base;
+
+ // compress
+ void
+ process_block (std::size_t block)
+ {
+ m_prng.seed (m_seed + block);
+ m_seed = m_prng();
+ }
+
+public:
+ prng_hasher (std::size_t seed = 0)
+ : m_seed (seed)
+ {
+ }
+
+ void
+ append (void const* data, std::size_t bytes) noexcept
+ {
+ base::operator() (data, bytes);
+ }
+
+ explicit
+ operator std::size_t() noexcept
+ {
+ base::finish();
+ return m_seed;
+ }
+};
+
+class SlowKey
+{
+private:
+ std::tuple date_;
+ std::vector > data_;
+
+public:
+ SlowKey()
+ {
+ static std::mt19937_64 eng;
+ std::uniform_int_distribution yeardata(1900, 2014);
+ std::uniform_int_distribution monthdata(1, 12);
+ std::uniform_int_distribution daydata(1, 28);
+ std::uniform_int_distribution veclen(0, 100);
+ std::uniform_int_distribution int1data(1, 10);
+ std::uniform_int_distribution int2data(-3, 5000);
+ std::get<0>(date_) = yeardata(eng);
+ std::get<1>(date_) = (unsigned char)monthdata(eng);
+ std::get<2>(date_) = (unsigned char)daydata(eng);
+ data_.resize(veclen(eng));
+ for (auto& p : data_)
+ {
+ p.first = int1data(eng);
+ p.second = int2data(eng);
+ }
+ }
+
+ // Hook into the system like this
+ template
+ friend
+ void
+ hash_append (Hasher& h, SlowKey const& x) noexcept
+ {
+ using beast::hash_append;
+ hash_append (h, x.date_, x.data_);
+ }
+
+ friend
+ bool operator< (SlowKey const& x, SlowKey const& y) noexcept
+ {
+ return std::tie(x.date_, x.data_) < std::tie(y.date_, y.data_);
+ }
+
+ // Hook into the std::system like this
+ friend struct std::hash;
+ friend struct X_fnv1a;
+};
+
+struct FastKey
+{
+private:
+ std::array m_values;
+
+public:
+ FastKey()
+ {
+ static std::conditional_t eng;
+ for (auto& v : m_values)
+ v = eng();
+ }
+
+ friend
+ bool
+ operator< (FastKey const& x, FastKey const& y) noexcept
+ {
+ return x.m_values < y.m_values;
+ }
+};
+
+} // hash_append_tests
+
+//------------------------------------------------------------------------------
+
+template<>
+struct is_contiguously_hashable
+ : std::true_type
+{
+};
+
+//------------------------------------------------------------------------------
+
+class hash_append_test : public unit_test::suite
+{
+public:
+ typedef hash_append_tests::SlowKey SlowKey;
+ typedef hash_append_tests::FastKey FastKey;
+
+ struct results_t
+ {
+ results_t()
+ : collision_factor (0)
+ , distribution_factor (0)
+ , elapsed (0)
+ {
+ }
+
+ float collision_factor;
+ float distribution_factor;
+ float windowed_score;
+ std::chrono::milliseconds elapsed;
+ };
+
+ // Generate a set of keys
+ template
+ std::set
+ make_keys (std::size_t count)
+ {
+ std::set keys;
+ while (count--)
+ keys.emplace();
+ return keys;
+ }
+
+ // Generate a set of hashes from a container
+ template
+ std::vector
+ make_hashes (Keys const& keys)
+ {
+ std::vector hashes;
+ hashes.reserve (keys.size());
+ for (auto const& key : keys)
+ {
+ Hasher h;
+ hash_append (h, key);
+ hashes.push_back (static_cast (h));
+ }
+ return hashes;
+ }
+
+ template
+ void
+ measure_hashes (results_t& results, Hashes const& hashes)
+ {
+ results.collision_factor =
+ hash_metrics::collision_factor (
+ hashes.begin(), hashes.end());
+
+ results.distribution_factor =
+ hash_metrics::distribution_factor (
+ hashes.begin(), hashes.end());
+
+ results.windowed_score =
+ hash_metrics::windowed_score (
+ hashes.begin(), hashes.end());
+ }
+
+ template
+ void
+ measure_keys (results_t& results, Keys const& keys)
+ {
+ auto const start (
+ std::chrono::high_resolution_clock::now());
+
+ auto const hashes (make_hashes (keys));
+
+ results.elapsed = std::chrono::duration_cast (
+ std::chrono::high_resolution_clock::now() - start);
+
+ measure_hashes (results, hashes);
+ }
+
+ template
+ void
+ test_hasher (std::string const& name, std::size_t n)
+ {
+ results_t results;
+ auto const keys (make_keys (n));
+ measure_keys (results, keys);
+ report (name, results);
+ }
+
+ void
+ report (std::string const& name, results_t const& results)
+ {
+ log <<
+ std::left <<
+ std::setw (39) << name << " | " <<
+ std::right <<
+ std::setw (13) << std::setprecision (5) <<
+ results.collision_factor << " | " <<
+ std::setw (13) << std::setprecision (5) <<
+ results.distribution_factor << " | " <<
+ std::setw (13) << std::setprecision (5) <<
+ results.windowed_score << " | " <<
+ std::left <<
+ results.elapsed.count();
+ pass ();
+ }
+
+ void
+ run()
+ {
+ log <<
+ "name | collision | distribution | windowed | time (milliseconds)" << std::endl <<
+ "----------------------------------------+---------------+---------------+---------------+--------------------";
+
+ //test_hasher , SlowKey> ("prng_hasher ", 10000);
+ //test_hasher , FastKey> ("prng_hasher ", 100000);
+
+ test_hasher ("jenkins1 ", 1000000);
+ test_hasher ("spooky ", 1000000);
+ test_hasher ("fnv1a ", 1000000);
+
+ test_hasher ("jenkins1 ", 1000000);
+ test_hasher ("spooky ", 1000000);
+ test_hasher ("fnv1a ", 1000000);
+ }
+};
+
+BEAST_DEFINE_TESTSUITE_MANUAL(hash_append,container,beast);
+
+}
diff --git a/beast/container/tests/hash_metrics.h b/beast/container/tests/hash_metrics.h
new file mode 100644
index 0000000000..1cb2ec4202
--- /dev/null
+++ b/beast/container/tests/hash_metrics.h
@@ -0,0 +1,207 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2014, Howard Hinnant ,
+ Vinnie Falco
+#include
+#include
+#include
+#include
+#include