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 +#include +#include + +namespace beast { +namespace hash_metrics { + +// Metrics for measuring the quality of container hash functions + +/** Returns the fraction of duplicate items in the sequence. */ +template +float +collision_factor (FwdIter first, FwdIter last) +{ + std::set s (first, last); + return 1 - static_cast (s.size()) / std::distance (first, last); +} + +//------------------------------------------------------------------------------ + +/** Returns the deviation of the sequence from the ideal distribution. */ +template +float +distribution_factor (FwdIter first, FwdIter last) +{ + typedef typename FwdIter::value_type value_type; + static_assert (std::is_unsigned ::value, ""); + + const unsigned nbits = CHAR_BIT * sizeof(std::size_t); + const unsigned rows = nbits / 4; + unsigned counts[rows][16] = {}; + std::for_each (first, last, [&](typename FwdIter::value_type h) + { + std::size_t mask = 0xF; + for (unsigned i = 0; i < rows; ++i, mask <<= 4) + counts[i][(h & mask) >> 4*i] += 1; + }); + float mean_rows[rows] = {0}; + float mean_cols[16] = {0}; + for (unsigned i = 0; i < rows; ++i) + { + for (unsigned j = 0; j < 16; ++j) + { + mean_rows[i] += counts[i][j]; + mean_cols[j] += counts[i][j]; + } + } + for (unsigned i = 0; i < rows; ++i) + mean_rows[i] /= 16; + for (unsigned j = 0; j < 16; ++j) + mean_cols[j] /= rows; + std::pair dev[rows][16]; + for (unsigned i = 0; i < rows; ++i) + { + for (unsigned j = 0; j < 16; ++j) + { + dev[i][j].first = std::abs(counts[i][j] - mean_rows[i]) / mean_rows[i]; + dev[i][j].second = std::abs(counts[i][j] - mean_cols[j]) / mean_cols[j]; + } + } + float max_err = 0; + for (unsigned i = 0; i < rows; ++i) + { + for (unsigned j = 0; j < 16; ++j) + { + if (max_err < dev[i][j].first) + max_err = dev[i][j].first; + if (max_err < dev[i][j].second) + max_err = dev[i][j].second; + } + } + return max_err; +} + +//------------------------------------------------------------------------------ + +namespace detail { + +template +inline +T +sqr(T t) +{ + return t*t; +} + +double +score (int const* bins, std::size_t const bincount, double const k) +{ + double const n = bincount; + // compute rms^2 value + double rms_sq = 0; + for(std::size_t i = 0; i < bincount; ++i) + rms_sq += sqr(bins[i]);; + rms_sq /= n; + // compute fill factor + double const f = (sqr(k) - 1) / (n*rms_sq - k); + // rescale to (0,1) with 0 = good, 1 = bad + return 1 - (f / n); +} + +template +std::uint32_t +window (T* blob, int start, int count ) +{ + std::size_t const len = sizeof(T); + static_assert((len & 3) == 0, ""); + if(count == 0) + return 0; + int const nbits = len * CHAR_BIT; + start %= nbits; + int ndwords = len / 4; + std::uint32_t const* k = static_cast < + std::uint32_t const*>(static_cast(blob)); + int c = start & (32-1); + int d = start / 32; + if(c == 0) + return (k[d] & ((1 << count) - 1)); + int ia = (d + 1) % ndwords; + int ib = (d + 0) % ndwords; + std::uint32_t a = k[ia]; + std::uint32_t b = k[ib]; + std::uint32_t t = (a << (32-c)) | (b >> c); + t &= ((1 << count)-1); + return t; +} + +} // detail + +/** Calculated a windowed metric using bins. + TODO Need reference (SMHasher?) +*/ +template +double +windowed_score (FwdIter first, FwdIter last) +{ + auto const size (std::distance (first, last)); + int maxwidth = 20; + // We need at least 5 keys per bin to reliably test distribution biases + // down to 1%, so don't bother to test sparser distributions than that + while (static_cast(size) / (1 << maxwidth) < 5.0) + maxwidth--; + double worst = 0; + int worstStart = -1; + int worstWidth = -1; + std::vector bins (1 << maxwidth); + int const hashbits = sizeof(std::size_t) * CHAR_BIT; + for (int start = 0; start < hashbits; ++start) + { + int width = maxwidth; + bins.assign (1 << width, 0); + for (auto iter (first); iter != last; ++iter) + ++bins[detail::window(&*iter, start, width)]; + // Test the distribution, then fold the bins in half, + // repeat until we're down to 256 bins + while (bins.size() >= 256) + { + double score (detail::score ( + bins.data(), bins.size(), size)); + if (score > worst) + { + worst = score; + worstStart = start; + worstWidth = width; + } + if (--width < 8) + break; + for (std::size_t i = 0, j = bins.size() / 2; j < bins.size(); ++i, ++j) + bins[i] += bins[j]; + bins.resize(bins.size() / 2); + } + } + return worst; +} + +} // hash_metrics +} // beast + +#endif diff --git a/beast/crypto/UnsignedInteger.h b/beast/crypto/UnsignedInteger.h index 26847d8271..e20badaba0 100644 --- a/beast/crypto/UnsignedInteger.h +++ b/beast/crypto/UnsignedInteger.h @@ -60,14 +60,6 @@ public: typedef value_type* iterator; typedef value_type const* const_iterator; - void - hash_combine (std::size_t& seed) const noexcept - { - std::size_t result; - Murmur::Hash (get(), size, seed, &result); - seed = result; - } - /** Hardened hash function for use with hash based containers. The seed is used to make the hash unpredictable. This prevents attackers from exploiting crafted inputs to produce degenerate diff --git a/beast/streams/debug_ostream.h b/beast/streams/debug_ostream.h index 6dc16448fc..d9f12ea18a 100644 --- a/beast/streams/debug_ostream.h +++ b/beast/streams/debug_ostream.h @@ -32,6 +32,12 @@ # else # include # endif +# ifdef min +# undef min +# endif +# ifdef max +# undef max +# endif #endif namespace beast { diff --git a/beast/utility/meta.h b/beast/utility/meta.h new file mode 100644 index 0000000000..b76df42d88 --- /dev/null +++ b/beast/utility/meta.h @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2014, Howard Hinnant + + 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. +*/ +//============================================================================== + +#ifndef BEAST_UTILITY_META_H_INCLUDED +#define BEAST_UTILITY_META_H_INCLUDED + +#include + +namespace beast { + +template struct static_and; + +template +struct static_and + : public std::integral_constant < + bool, b0 && static_and::value> +{ +}; + +template <> +struct static_and<> + : public std::true_type +{ +}; + +static_assert( static_and::value, ""); +static_assert(!static_and::value, ""); + +template +struct static_sum; + +template +struct static_sum + : public std::integral_constant < + std::size_t, s0 + static_sum::value> +{ +}; + +template <> +struct static_sum<> + : public std::integral_constant +{ +}; + +static_assert(static_sum<5, 2, 17, 0>::value == 24, ""); + +} // beast + +#endif // BEAST_UTILITY_META_H_INCLUDED diff --git a/beast/utility/type_name.h b/beast/utility/type_name.h new file mode 100644 index 0000000000..3e02fdfe82 --- /dev/null +++ b/beast/utility/type_name.h @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2014, Howard Hinnant + + 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. +*/ +//============================================================================== + +#ifndef BEAST_UTILITY_TYPE_NAME_H_INCLUDED +#define BEAST_UTILITY_TYPE_NAME_H_INCLUDED + +#include +#include +#include +#ifndef _MSC_VER +# include +#endif +#include +#include +#include +#include + +namespace beast { + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4127) // conditional expression is constant +#endif + +template +std::string +type_name() +{ + typedef typename std::remove_reference::type TR; + std::unique_ptr own ( + #ifndef _MSC_VER + abi::__cxa_demangle (typeid(TR).name(), nullptr, + nullptr, nullptr), + #else + nullptr, + #endif + std::free + ); + std::string r = own != nullptr ? own.get() : typeid(TR).name(); + if (std::is_const::value) + r += " const"; + if (std::is_volatile::value) + r += " volatile"; + if (std::is_lvalue_reference::value) + r += "&"; + else if (std::is_rvalue_reference::value) + r += "&&"; + return r; +} + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +} // beast + +#endif