From 6efb995861144931f72508f696390100bf3faac6 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 25 Apr 2025 10:26:19 -0700 Subject: [PATCH 1/4] add 32-bit, 64-bit byteswap implementation --- include/boost/hash2/detail/byteswap.hpp | 99 +++++++++++++++++++++++++ test/Jamfile | 2 + test/detail_byteswap.cpp | 18 +++++ test/detail_byteswap_cx.cpp | 26 +++++++ 4 files changed, 145 insertions(+) create mode 100644 include/boost/hash2/detail/byteswap.hpp create mode 100644 test/detail_byteswap.cpp create mode 100644 test/detail_byteswap_cx.cpp diff --git a/include/boost/hash2/detail/byteswap.hpp b/include/boost/hash2/detail/byteswap.hpp new file mode 100644 index 0000000..e09f96c --- /dev/null +++ b/include/boost/hash2/detail/byteswap.hpp @@ -0,0 +1,99 @@ +#ifndef BOOST_HASH2_DETAIL_BYTESWAP_HPP_INCLUDED +#define BOOST_HASH2_DETAIL_BYTESWAP_HPP_INCLUDED + +// Copyright 2025 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +#if defined(BOOST_MSVC) +#include +#endif + +namespace boost +{ +namespace hash2 +{ +namespace detail +{ + +#if defined(BOOST_GCC) || defined(BOOST_CLANG) + +BOOST_CXX14_CONSTEXPR inline std::uint32_t byteswap_impl( std::uint32_t x ) noexcept +{ + return __builtin_bswap32( x ); +} + +BOOST_CXX14_CONSTEXPR inline std::uint64_t byteswap_impl( std::uint64_t x ) noexcept +{ + return __builtin_bswap64( x ); +} + +#elif defined(BOOST_MSVC) + +BOOST_CXX14_CONSTEXPR inline std::uint32_t byteswap_impl( std::uint32_t x ) noexcept +{ + if( !detail::is_constant_evaluated() ) + { + return _byteswap_ulong( x ); + } + else + { + // copy-paste the approach used by Core in bit.hpp + std::uint32_t step16 = x << 16 | x >> 16; + return ( ( step16 << 8 ) & 0xff00ff00 ) | ( ( step16 >> 8 ) & 0x00ff00ff ); + } +} + +BOOST_CXX14_CONSTEXPR inline std::uint64_t byteswap_impl( std::uint64_t x ) noexcept +{ + if( !detail::is_constant_evaluated() ) + { + return _byteswap_uint64( x ); + } + else + { + // copy-paste the approach used by Core in bit.hpp + std::::uint64_t step32 = x << 32 | x >> 32; + std::::uint64_t step16 = ( step32 & 0x0000ffff0000ffffull ) << 16 | ( step32 & 0xffff0000ffff0000ull ) >> 16; + return ( step16 & 0x00ff00ff00ff00ffull ) << 8 | ( step16 & 0xff00ff00ff00ff00ull ) >> 8; + } +} + +#else + +BOOST_CXX14_CONSTEXPR inline std::uint32_t byteswap_impl( std::uint32_t x ) noexcept +{ + // copy-paste the approach used by Core in bit.hpp + std::uint32_t step16 = x << 16 | x >> 16; + return ( ( step16 << 8 ) & 0xff00ff00 ) | ( ( step16 >> 8 ) & 0x00ff00ff ); +} + +BOOST_CXX14_CONSTEXPR inline std::uint64_t byteswap_impl( std::uint64_t x ) noexcept +{ + // copy-paste the approach used by Core in bit.hpp + std::::uint64_t step32 = x << 32 | x >> 32; + std::::uint64_t step16 = ( step32 & 0x0000ffff0000ffffull ) << 16 | ( step32 & 0xffff0000ffff0000ull ) >> 16; + return ( step16 & 0x00ff00ff00ff00ffull ) << 8 | ( step16 & 0xff00ff00ff00ff00ull ) >> 8; +} + +#endif + +BOOST_CXX14_CONSTEXPR inline std::uint32_t byteswap( std::uint32_t x ) noexcept +{ + return byteswap_impl( x ); +} + +BOOST_CXX14_CONSTEXPR inline std::uint64_t byteswap( std::uint64_t x ) noexcept +{ + return byteswap_impl( x ); +} + +} // namespace detail +} // namespace hash2 +} // namespace boost + +#endif // #ifndef BOOST_HASH2_DETAIL_BYTESWAP_HPP_INCLUDED diff --git a/test/Jamfile b/test/Jamfile index b9ef8bb..d77a4e5 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -54,6 +54,8 @@ run detail_write.cpp ; run detail_write_2.cpp ; run detail_rot.cpp ; run detail_has_tag_invoke.cpp ; +run detail_byteswap.cpp ; +run detail_byteswap_cx.cpp ; # hash_append diff --git a/test/detail_byteswap.cpp b/test/detail_byteswap.cpp new file mode 100644 index 0000000..05f607a --- /dev/null +++ b/test/detail_byteswap.cpp @@ -0,0 +1,18 @@ +// Copyright 2025 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +// shamelessly steal pdimov's tests from Core + +int main() +{ + using boost::hash2::detail::byteswap; + + BOOST_TEST_EQ( byteswap( std::uint32_t{ 0xf1e2d3c4u } ), 0xc4d3e2f1u ); + BOOST_TEST_EQ( byteswap( std::uint64_t{ 0xf1e2d3c4u } << 32 | 0xb5a69788u ), ( std::uint64_t{ 0x8897a6b5u } << 32 | 0xc4d3e2f1u) ); + + return boost::report_errors(); +} diff --git a/test/detail_byteswap_cx.cpp b/test/detail_byteswap_cx.cpp new file mode 100644 index 0000000..5765748 --- /dev/null +++ b/test/detail_byteswap_cx.cpp @@ -0,0 +1,26 @@ +// Copyright 2025 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +#if defined(BOOST_NO_CXX14_CONSTEXPR) +# define TEST_EQ(x1, x2) BOOST_TEST_EQ(x1, x2) +#else +# define TEST_EQ(x1, x2) BOOST_TEST_EQ(x1, x2); STATIC_ASSERT( x1 == x2 ) +#endif + +// shamelessly steal pdimov's tests from Core + +int main() +{ + using boost::hash2::detail::byteswap; + + TEST_EQ( byteswap( std::uint32_t{ 0xf1e2d3c4u } ), 0xc4d3e2f1u ); + TEST_EQ( byteswap( std::uint64_t{ 0xf1e2d3c4u } << 32 | 0xb5a69788u ), ( std::uint64_t{ 0x8897a6b5u } << 32 | 0xc4d3e2f1u) ); + + return boost::report_errors(); +} From c8979facb4cadc339c0a05bd1906e23fe7426c99 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 25 Apr 2025 13:02:21 -0700 Subject: [PATCH 2/4] add 128-bit mul --- include/boost/hash2/detail/mul128.hpp | 129 ++++++++++++++++++++++++++ test/Jamfile | 2 + test/detail_mul128.cpp | 24 +++++ test/detail_mul128_cx.cpp | 34 +++++++ 4 files changed, 189 insertions(+) create mode 100644 include/boost/hash2/detail/mul128.hpp create mode 100644 test/detail_mul128.cpp create mode 100644 test/detail_mul128_cx.cpp diff --git a/include/boost/hash2/detail/mul128.hpp b/include/boost/hash2/detail/mul128.hpp new file mode 100644 index 0000000..18771bf --- /dev/null +++ b/include/boost/hash2/detail/mul128.hpp @@ -0,0 +1,129 @@ +#ifndef BOOST_HASH2_DETAIL_MUL128_HPP_INCLUDED +#define BOOST_HASH2_DETAIL_MUL128_HPP_INCLUDED + +// Copyright 2025 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +#if defined(BOOST_MSVC) +#include +#endif + +namespace boost +{ +namespace hash2 +{ +namespace detail +{ + +struct uint128_t +{ + std::uint64_t low; + std::uint64_t high; +}; + +#if defined(BOOST_HAS_INT128) + +BOOST_CXX14_CONSTEXPR inline uint128_t mul128_impl( std::uint64_t x, std::uint64_t y ) noexcept +{ + __uint128_t product = __uint128_t{ x } * __uint128_t{ y }; + uint128_t r = { 0, 0 }; + r.low = static_cast( product ); + r.high = static_cast( product >> 64 ); + return r; +} + +#elif ( defined(_M_X64) || defined(_M_IA64) ) && !defined(_M_ARM64EC) + +BOOST_CXX14_CONSTEXPR inline uint128_t mul128_impl( std::uint64_t x, std::uint64_t y ) noexcept +{ + if( !detail::is_constant_evaluated() ) + { + uint128_t r = { 0, 0 }; + std::uint64_t high_product = 0; + r.low = _umul128( x, y, &high_product ); + r.high = high_product; + return r; + } + else + { + std::uint64_t lo_lo = ( x & 0xffffffff ) * ( y & 0xffffffff ); + std::uint64_t hi_lo = ( x >> 32 ) * ( y & 0xffffffff ); + std::uint64_t lo_hi = ( x & 0xffffffff ) * ( y >> 32 ); + std::uint64_t hi_hi = ( x >> 32 ) * ( y >> 32 ); + + std::uint64_t cross = ( lo_lo >> 32 ) + ( hi_lo & 0xffffffff ) + lo_hi; + std::uint64_t upper = ( hi_lo >> 32 ) + ( cross >> 32 ) + hi_hi; + std::uint64_t lower = ( cross << 32 ) | ( lo_lo & 0xffffffff ); + + uint128_t r = { 0, 0 }; + r.low = lower; + r.high = upper; + return r; + } +} + +#elif defined(_M_ARM64) || defined(_M_ARM64EC) + +BOOST_CXX14_CONSTEXPR inline uint128_t mul128_impl( std::uint64_t x, std::uint64_t y ) noexcept +{ + if( !detail::is_constant_evaluated() ) + { + uint128_t r = { 0, 0 }; + r.low = x * y; + r.high = __umulh( x, y ); + return r; + } + else + { + std::uint64_t lo_lo = ( x & 0xffffffff ) * ( y & 0xffffffff ); + std::uint64_t hi_lo = ( x >> 32 ) * ( y & 0xffffffff ); + std::uint64_t lo_hi = ( x & 0xffffffff ) * ( y >> 32 ); + std::uint64_t hi_hi = ( x >> 32 ) * ( y >> 32 ); + + std::uint64_t cross = ( lo_lo >> 32 ) + ( hi_lo & 0xffffffff ) + lo_hi; + std::uint64_t upper = ( hi_lo >> 32 ) + ( cross >> 32 ) + hi_hi; + std::uint64_t lower = ( cross << 32 ) | ( lo_lo & 0xffffffff ); + + uint128_t r = { 0, 0 }; + r.low = lower; + r.high = upper; + return r; + } +} + +#else + +BOOST_CXX14_CONSTEXPR inline uint128_t mul128_impl( std::uint64_t x, std::uint64_t y ) noexcept +{ + std::uint64_t lo_lo = ( x & 0xffffffff ) * ( y & 0xffffffff ); + std::uint64_t hi_lo = ( x >> 32 ) * ( y & 0xffffffff ); + std::uint64_t lo_hi = ( x & 0xffffffff ) * ( y >> 32 ); + std::uint64_t hi_hi = ( x >> 32 ) * ( y >> 32 ); + + std::uint64_t cross = ( lo_lo >> 32 ) + ( hi_lo & 0xffffffff ) + lo_hi; + std::uint64_t upper = ( hi_lo >> 32 ) + ( cross >> 32 ) + hi_hi; + std::uint64_t lower = ( cross << 32 ) | ( lo_lo & 0xffffffff ); + + uint128_t r = { 0, 0 }; + r.low = lower; + r.high = upper; + return r; +} + +#endif + +BOOST_CXX14_CONSTEXPR inline uint128_t mul128( std::uint64_t x, std::uint64_t y ) noexcept +{ + return mul128_impl( x, y ); +} + +} // namespace detail +} // namespace hash2 +} // namespace boost + +#endif // #ifndef BOOST_HASH2_DETAIL_MUL128_HPP_INCLUDED diff --git a/test/Jamfile b/test/Jamfile index d77a4e5..0732fe4 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -56,6 +56,8 @@ run detail_rot.cpp ; run detail_has_tag_invoke.cpp ; run detail_byteswap.cpp ; run detail_byteswap_cx.cpp ; +run detail_mul128.cpp ; +run detail_mul128_cx.cpp ; # hash_append diff --git a/test/detail_mul128.cpp b/test/detail_mul128.cpp new file mode 100644 index 0000000..02e4a37 --- /dev/null +++ b/test/detail_mul128.cpp @@ -0,0 +1,24 @@ +// Copyright 2025 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +void test( std::uint64_t x, std::uint64_t y, boost::hash2::detail::uint128_t e ) +{ + auto r = boost::hash2::detail::mul128( x, y ); + BOOST_TEST_EQ( r.low, e.low ); + BOOST_TEST_EQ( r.high, e.high ); +} + +int main() +{ + test( ULLONG_MAX, ULLONG_MAX, { 0x0000000000000001ull, 0xfffffffffffffffeull } ); + test( 0, 0, { 0x0000000000000000ull, 0x0000000000000000ull } ); + test( 1, 2, { 0x0000000000000002ull, 0x0000000000000000ull } ); + test( 3, ULLONG_MAX, { 0xfffffffffffffffdull, 0x0000000000000002ull }); + + return boost::report_errors(); +} diff --git a/test/detail_mul128_cx.cpp b/test/detail_mul128_cx.cpp new file mode 100644 index 0000000..8c4ff89 --- /dev/null +++ b/test/detail_mul128_cx.cpp @@ -0,0 +1,34 @@ +// Copyright 2025 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +#if defined(BOOST_NO_CXX14_CONSTEXPR) +# define TEST_EQ(x1, x2) BOOST_TEST(x1 == x2) +#else +# define TEST_EQ(x1, x2) BOOST_TEST(x1 == x2); STATIC_ASSERT( x1 == x2 ) +#endif + +BOOST_CXX14_CONSTEXPR bool operator==( boost::hash2::detail::uint128_t x, boost::hash2::detail::uint128_t y ) noexcept +{ + return ( x.low == y.low ) && ( x.high == y.high ); +} + +int main() +{ + using boost::hash2::detail::mul128; + using boost::hash2::detail::uint128_t; + + TEST_EQ( mul128( ULLONG_MAX, ULLONG_MAX ), ( uint128_t{ 0x0000000000000001ull, 0xfffffffffffffffeull } ) ); + TEST_EQ( mul128( 0, 0 ), ( uint128_t{ 0x0000000000000000ull, 0x0000000000000000ull } ) ); + TEST_EQ( mul128( 1, 2 ), ( uint128_t{ 0x0000000000000002ull, 0x0000000000000000ull } ) ); + TEST_EQ( mul128( 3, ULLONG_MAX ), ( uint128_t{ 0xfffffffffffffffdull, 0x0000000000000002ull } ) ); + + return boost::report_errors(); +} From f8964e962c8fca1c9e68d5640a8cec1325a73b50 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 25 Apr 2025 10:26:40 -0700 Subject: [PATCH 3/4] add xxh3-128 implementation --- include/boost/hash2/xxh3.hpp | 664 +++++++++++++++++++++++++++++++++++ test/Jamfile | 3 + test/xxh3.cpp | 241 +++++++++++++ test/xxh3_cx.cpp | 230 ++++++++++++ 4 files changed, 1138 insertions(+) create mode 100644 include/boost/hash2/xxh3.hpp create mode 100644 test/xxh3.cpp create mode 100644 test/xxh3_cx.cpp diff --git a/include/boost/hash2/xxh3.hpp b/include/boost/hash2/xxh3.hpp new file mode 100644 index 0000000..c4081ae --- /dev/null +++ b/include/boost/hash2/xxh3.hpp @@ -0,0 +1,664 @@ +#ifndef BOOST_HASH2_XXH3_HPP_INCLUDED +#define BOOST_HASH2_XXH3_HPP_INCLUDED + +// Copyright 2025 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// xxHash, https://cyan4973.github.io/xxHash/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_MSVC) && BOOST_MSVC < 1920 +# pragma warning(push) +# pragma warning(disable: 4307) // '+': integral constant overflow +#endif + +namespace boost +{ +namespace hash2 +{ + +template +struct xxh3_128_constants +{ + constexpr static unsigned char const default_secret[ 192 ] = + { + 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, + 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, + 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, + 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, + 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, + 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, + 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, + 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, + 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, + 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, + 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, + 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, + }; +}; + +// copy-paste from Boost.Unordered's prime_fmod approach +#if defined(BOOST_NO_CXX17_INLINE_VARIABLES) + +// https://en.cppreference.com/w/cpp/language/static#Constant_static_members +// If a const non-inline (since C++17) static data member or a constexpr +// static data member (since C++11)(until C++17) is odr-used, a definition +// at namespace scope is still required, but it cannot have an +// initializer. +template +constexpr unsigned char xxh3_128_constants::default_secret[ 192 ]; + +#endif + +class xxh3_128 +{ +private: + + static constexpr std::size_t const default_secret_len = 192; + static constexpr std::size_t const min_secret_len = 136; + static constexpr std::size_t const buffer_size = 256; + + static constexpr std::uint64_t const P32_1 = 0x9E3779B1U; + static constexpr std::uint64_t const P32_2 = 0x85EBCA77U; + static constexpr std::uint64_t const P32_3 = 0xC2B2AE3DU; + static constexpr std::uint64_t const P64_1 = 0x9E3779B185EBCA87ULL; + static constexpr std::uint64_t const P64_2 = 0xC2B2AE3D27D4EB4FULL; + static constexpr std::uint64_t const P64_3 = 0x165667B19E3779F9ULL; + static constexpr std::uint64_t const P64_4 = 0x85EBCA77C2B2AE63ULL; + static constexpr std::uint64_t const P64_5 = 0x27D4EB2F165667C5ULL; + static constexpr std::uint64_t const PRIME_MX1 = 0x165667919E3779F9ULL; + static constexpr std::uint64_t const PRIME_MX2 = 0x9FB21C651E98DF25ULL; + + BOOST_CXX14_CONSTEXPR void init() + { + acc_[ 0 ] = P32_3; + acc_[ 1 ] = P64_1; + acc_[ 2 ] = P64_2; + acc_[ 3 ] = P64_3; + acc_[ 4 ] = P64_4; + acc_[ 5 ] = P32_2; + acc_[ 6 ] = P64_5; + acc_[ 7 ] = P32_1; + + stripes_per_block_ = ( secret_len_ - 64 ) / 8; + + if( seed_ != 0 ) + { + auto secret = xxh3_128_constants<>::default_secret; + + std::size_t num_rounds = default_secret_len / 16; + for( std::size_t i = 0; i < num_rounds; ++i ) + { + auto low = detail::read64le( secret + 16 * i ) + seed_; + auto high = detail::read64le( secret + 16 * i + 8 ) - seed_; + + detail::write64le( custom_secret_ + 16 * i, low ); + detail::write64le( custom_secret_ + 16 * i + 8, high ); + } + } + } + + BOOST_FORCEINLINE BOOST_CXX14_CONSTEXPR static std::uint64_t avalanche( std::uint64_t x ) + { + x ^= ( x >> 37 ); + x *= PRIME_MX1; + x ^= ( x >> 32 ); + return x; + } + + BOOST_FORCEINLINE BOOST_CXX14_CONSTEXPR static std::uint64_t avalanche_xxh64( std::uint64_t x ) + { + x ^= x >> 33; + x *= P64_2; + x ^= x >> 29; + x *= P64_3; + x ^= x >> 32; + return x; + } + + BOOST_CXX14_CONSTEXPR std::uint64_t mix_step( unsigned char const* data, std::size_t secret_offset, std::uint64_t seed ) + { + std::uint64_t data_words[ 2 ] = {}; + std::uint64_t secret_words[ 2 ] = {}; + for( int i = 0; i < 2; ++i ) + { + data_words[ i ] = detail::read64le( data + 8 * i ); + secret_words[ i ] = detail::read64le( secret_ + secret_offset + 8 * i ); + } + + detail::uint128_t r = detail::mul128( data_words[ 0 ] ^ ( secret_words[ 0 ] + seed ), data_words[ 1 ] ^ ( secret_words[ 1 ] - seed ) ); + return r.low ^ r.high; + } + + BOOST_CXX14_CONSTEXPR void mix_two_chunks( unsigned char const* x, unsigned char const* y, std::size_t secret_offset, std::uint64_t seed, std::uint64_t (&acc)[ 2 ] ) + { + std::uint64_t data_words1[ 2 ] = {}; + std::uint64_t data_words2[ 2 ] = {}; + + for( int i = 0; i < 2; ++i ) + { + data_words1[ i ] = detail::read64le( x + 8 * i ); + data_words2[ i ] = detail::read64le( y + 8 * i ); + } + + acc[ 0 ] += mix_step( x, secret_offset, seed ); + acc[ 1 ] += mix_step( y, secret_offset + 16, seed ); + acc[ 0 ] ^= ( data_words2[ 0 ] + data_words2[ 1 ] ); + acc[ 1 ] ^= ( data_words1[ 0 ] + data_words1[ 1 ] ); + } + + BOOST_CXX14_CONSTEXPR void accumulate( std::uint64_t stripe[ 8 ], std::size_t secret_offset ) + { + std::uint64_t secret_words[ 8 ] = {}; + for( int i = 0; i < 8; ++i ) + { + secret_words[ i ] = detail::read64le( secret_ + secret_offset + 8 * i ); + } + + for( int i = 0; i < 8; ++i ) + { + std::uint64_t value = stripe[ i ] ^ secret_words[ i ]; + acc_[ i ^ 1 ] = acc_[ i ^ 1 ] + stripe[ i ]; + acc_[ i ] = acc_[ i ] + ( value & 0xffffffff ) * ( value >> 32 ); + } + } + + BOOST_CXX14_CONSTEXPR void scramble() + { + std::uint64_t secret_words[ 8 ] = {}; + for( int i = 0; i < 8; ++i ) + { + secret_words[ i ] = detail::read64le( secret_ + ( secret_len_ - 64 ) + ( 8 * i ) ); + } + + for( int i = 0; i < 8; ++i ) + { + acc_[ i ] ^= acc_[ i ] >> 47; + acc_[ i ] ^= secret_words[ i ]; + acc_[ i ] *= P32_1; + } + } + + BOOST_CXX14_CONSTEXPR void last_round() + { + unsigned char last_stripe[ 64 ] = {}; + unsigned char* last_stripe_ptr = nullptr; + + if( m_ >= 64 ) + { + std::size_t num_stripes = ( m_ == 0 ? 0 : ( m_ - 1 ) / 64 ); + for( std::size_t n = 0; n < num_stripes; ++n ) + { + std::uint64_t stripe[ 8 ] = {}; + for( int i = 0; i < 8; ++i ) + { + stripe[ i ] = detail::read64le( buffer_ + ( 64 * n ) + ( 8 * i ) ); + } + accumulate( stripe, 8 * num_stripes_++ ); + + BOOST_ASSERT( num_stripes_ <= stripes_per_block_ ); + } + + last_stripe_ptr = buffer_ + m_ - 64; + } + else + { + std::size_t len = 64 - m_; + + detail::memcpy( last_stripe, buffer_ + buffer_size - len, len ); + detail::memcpy( last_stripe + len, buffer_, m_ ); + + last_stripe_ptr = last_stripe; + } + + std::uint64_t stripe[ 8 ] = {}; + for( int i = 0; i < 8; ++i ) + { + stripe[ i ] = detail::read64le( last_stripe_ptr + ( 8 * i ) ); + } + + accumulate( stripe, secret_len_ - 71 ); + } + + BOOST_CXX14_CONSTEXPR std::uint64_t final_merge( std::uint64_t init_value, std::size_t secret_offset ) + { + std::uint64_t secret_words[ 8 ] = {}; + for( int i = 0; i < 8; ++i ) + { + secret_words[ i ] = detail::read64le( secret_ + secret_offset + 8 * i ); + } + + std::uint64_t result = init_value; + for( int i = 0; i < 4; ++i ) + { + auto mul_result = detail::mul128( acc_[ 2 * i ] ^ secret_words[ 2 * i ], acc_[ 2 * i + 1 ] ^ secret_words[ 2 * i + 1 ] ); + result += mul_result.low ^ mul_result.high; + } + + return avalanche( result ); + } + + BOOST_CXX14_CONSTEXPR digest<16> xxh3_128_digest_empty() + { + std::uint64_t secret_words[ 4 ] = {}; + for( int i = 0; i < 4; ++i ) + { + secret_words[ i ] = detail::read64le( secret_ + 64 + 8 * i ); + } + + digest<16> r; + detail::write64be( r.data() + 8, avalanche_xxh64( seed_ ^ secret_words[ 0 ] ^ secret_words[ 1 ] ) ); + detail::write64be( r.data() + 0, avalanche_xxh64( seed_ ^ secret_words[ 2 ] ^ secret_words[ 3 ] ) ); + return r; + } + + BOOST_CXX14_CONSTEXPR digest<16> xxh3_128_digest_1to3() + { + std::uint32_t v1 = buffer_[ ( n_ - 1 ) ]; + std::uint32_t v2 = static_cast( n_ << 8 ); + std::uint32_t v3 = buffer_[ 0 ] << 16; + std::uint32_t v4 = buffer_[ ( n_ >> 1 ) ] << 24; + + std::uint32_t combined = v1 | v2 | v3 | v4; + + std::uint32_t secret_words[ 4 ] = {}; + for( int i = 0; i < 4; ++i ) + { + secret_words[ i ] = detail::read32le( secret_ + 4 * i ); + } + + std::uint64_t low = ( ( secret_words[ 0 ] ^ secret_words[ 1 ] ) + seed_ ) ^ combined; + std::uint64_t high = ( ( secret_words[ 2 ] ^ secret_words[ 3 ] ) - seed_ ) ^ ( detail::rotl( detail::byteswap( combined ), 13 ) ); + + digest<16> r; + detail::write64be( r.data() + 8, avalanche_xxh64( low ) ); + detail::write64be( r.data() + 0, avalanche_xxh64( high ) ); + + return r; + } + + BOOST_CXX14_CONSTEXPR digest<16> xxh3_128_digest_4to8() + { + std::uint32_t input_first = detail::read32le( buffer_ ); + std::uint32_t input_last = detail::read32le( buffer_ + ( n_ - 4 ) ); + std::uint64_t modified_seed = seed_ ^ ( std::uint64_t{ detail::byteswap( static_cast( seed_ ) ) } << 32 ); + + std::uint64_t secret_words[ 2 ] = {}; + for( int i = 0; i < 2; ++i ) + { + secret_words[ i ] = detail::read64le( secret_ + 16 + i * 8 ); + } + + std::uint64_t combined = std::uint64_t{ input_first } | ( std::uint64_t{ input_last } << 32 ); + std::uint64_t value = ( ( secret_words[ 0 ] ^ secret_words[ 1 ] ) + modified_seed ) ^ combined; + + detail::uint128_t mul_result = detail::mul128( value, P64_1 + ( n_ << 2 ) ); + std::uint64_t high = mul_result.high; + std::uint64_t low = mul_result.low; + + high += ( low << 1 ); + low ^= ( high >> 3 ); + low ^= ( low >> 35 ); + low *= PRIME_MX2; + low ^= ( low >> 28 ); + + high = avalanche( high ); + + digest<16> r; + detail::write64be( r.data() + 0, high ); + detail::write64be( r.data() + 8, low ); + + return r; + } + + BOOST_CXX14_CONSTEXPR digest<16> xxh3_128_digest_9to16() + { + std::uint64_t input_first = detail::read64le( buffer_ ); + std::uint64_t input_last = detail::read64le( buffer_ + ( n_ - 8 ) ); + + std::uint64_t secret_words[ 4 ] = {}; + for( int i = 0; i < 4; ++i ) + { + secret_words[ i ] = detail::read64le( secret_ + 32 + ( i * 8 ) ); + } + + std::uint64_t val1 = ( ( secret_words[ 0 ] ^ secret_words[ 1 ] ) - seed_ ) ^ input_first ^ input_last; + std::uint64_t val2 = ( ( secret_words[ 2 ] ^ secret_words[ 3 ] ) + seed_ ) ^ input_last; + + detail::uint128_t mul_result = detail::mul128( val1, P64_1 ); + std::uint64_t low = mul_result.low + ( std::uint64_t{ n_ - 1 } << 54 ); + std::uint64_t high = mul_result.high + val2 + ( val2 & 0x00000000ffffffff ) * ( P32_2 - 1 ); + + low ^= detail::byteswap( high ); + + detail::uint128_t mul_result2 = detail::mul128( low, P64_2 ); + low = mul_result2.low; + high = mul_result2.high + high * P64_2; + + digest<16> r; + detail::write64be( r.data() + 0, avalanche( high ) ); + detail::write64be( r.data() + 8, avalanche( low ) ); + + return r; + } + + BOOST_CXX14_CONSTEXPR digest<16> xxh3_128_digest_17to128() + { + std::uint64_t acc[ 2 ] = { n_ * P64_1, 0 }; + + std::uint64_t num_rounds = ( ( n_ - 1 ) >> 5 ) + 1; + for( std::int64_t i = num_rounds - 1; i >= 0; --i ) + { + std::size_t offset_start = static_cast( 16 * i ); + std::size_t offset_end = n_ - static_cast( 16 * i ) - 16; + + mix_two_chunks( buffer_ + offset_start, buffer_ + offset_end, static_cast( 32 * i ), seed_, acc ); + } + + std::uint64_t low = acc[ 0 ] + acc[ 1 ]; + std::uint64_t high = ( acc[ 0 ] * P64_1 ) + ( acc[ 1 ] * P64_4 ) + ( ( std::uint64_t{ n_ } - seed_ ) * P64_2 ); + + digest<16> r; + detail::write64be( r.data() + 0, std::uint64_t{ 0 } - avalanche( high ) ); + detail::write64be( r.data() + 8, avalanche( low ) ); + + return r; + } + + BOOST_CXX14_CONSTEXPR digest<16> xxh3_128_digest_129to240() + { + std::uint64_t acc[ 2 ] = { n_ * P64_1, 0 }; + + std::uint64_t num_chunks = n_ >> 5; + + for( std::size_t i = 0; i < 4; ++i ) + { + mix_two_chunks( buffer_ + 32 * i, buffer_ + ( 32 * i ) + 16, 32 * i, seed_, acc ); + } + + acc[ 0 ] = avalanche( acc[ 0 ] ); + acc[ 1 ] = avalanche( acc[ 1 ] ); + + for( std::size_t i = 4; i < num_chunks; ++i ) + { + mix_two_chunks( buffer_ + 32 * i, buffer_ + ( 32 * i ) + 16, ( i - 4 ) * 32 + 3, seed_, acc ); + } + + mix_two_chunks( buffer_ + n_ - 16, buffer_ + n_ - 32, 103, std::uint64_t{ 0 } - seed_, acc ); + + std::uint64_t low = acc[ 0 ] + acc[ 1 ]; + std::uint64_t high = ( acc[ 0 ] * P64_1 ) + ( acc[ 1 ] * P64_4 ) + ( ( std::uint64_t{ n_ } - seed_ ) * P64_2 ); + + digest<16> r; + detail::write64be( r.data() + 0, std::uint64_t{ 0 } - avalanche( high ) ); + detail::write64be( r.data() + 8, avalanche( low ) ); + + return r; + } + + BOOST_CXX14_CONSTEXPR digest<16> xxh3_128_digest_long() + { + last_round(); + + std::uint64_t low = final_merge( n_ * P64_1, 11 ); + std::uint64_t high = final_merge( ~( n_ * P64_2 ), secret_len_ - 75 ); + + digest<16> r; + detail::write64be( r.data() + 0, high ); + detail::write64be( r.data() + 8, low ); + + return r; + } + + unsigned char buffer_[ buffer_size ] = {}; + unsigned char custom_secret_[ default_secret_len ] = {}; + std::uint64_t acc_[ 8 ] = {}; + std::uint64_t seed_ = 0; + std::size_t n_ = 0; + std::size_t m_ = 0; + + unsigned char const* secret_ = xxh3_128_constants<>::default_secret; + std::size_t secret_len_ = sizeof( xxh3_128_constants<>::default_secret ); + std::size_t stripes_per_block_ = 0; + std::size_t num_stripes_ = 0; // current number of procssed stripes + +public: + + using result_type = digest<16>; + + BOOST_CXX14_CONSTEXPR xxh3_128() + { + init(); + } + + BOOST_CXX14_CONSTEXPR explicit xxh3_128( std::uint64_t seed ) : seed_{ seed } + { + init(); + } + + xxh3_128( void const* p, std::size_t n ) : xxh3_128( reinterpret_cast( p ), n ) + { + } + + BOOST_CXX14_CONSTEXPR xxh3_128( unsigned char const* p, std::size_t n ) + { + init(); + + if( n == 0 ) return; + + if( n < min_secret_len ) + { + update( p, n ); + result(); + return; + } + + secret_ = p; + secret_len_ = n; + stripes_per_block_ = ( secret_len_ - 64 ) / 8; + } + + BOOST_CXX14_CONSTEXPR xxh3_128( xxh3_128 const& rhs ) + { + detail::memcpy( buffer_, rhs.buffer_, buffer_size); + detail::memcpy( custom_secret_, rhs.custom_secret_, default_secret_len ); + + for( int i = 0; i < 8; ++i ) + { + acc_[ i ] = rhs.acc_[ i ]; + } + + seed_ = rhs.seed_; + n_ = rhs.n_; + m_ = rhs.m_; + + if( rhs.secret_ == rhs.custom_secret_ ) + { + secret_ = custom_secret_; + } + else + { + secret_ = rhs.secret_; + } + } + + BOOST_CXX14_CONSTEXPR xxh3_128& operator=( xxh3_128 const& rhs ) + { + if( this != &rhs ) + { + detail::memcpy( buffer_, rhs.buffer_, buffer_size); + detail::memcpy( custom_secret_, rhs.custom_secret_, default_secret_len ); + + for( int i = 0; i < 8; ++i ) + { + acc_[ i ] = rhs.acc_[ i ]; + } + + seed_ = rhs.seed_; + n_ = rhs.n_; + m_ = rhs.m_; + + if( rhs.secret_ == rhs.custom_secret_ ) + { + secret_ = custom_secret_; + } + else + { + secret_ = rhs.secret_; + } + } + return *this; + } + + void update( void const* p, std::size_t n ) + { + update( static_cast( p ), n ); + } + + BOOST_CXX14_CONSTEXPR void update( unsigned char const* p, std::size_t n ) + { + if( n == 0 ) return; + + n_ += n; + + // from the spec: + // + // There is one exception though: when input is large (> 240 bytes) and a seed is given, a secret + // is derived from the seed value and the default secret using the following procedure... + if( seed_ != 0 && n_ > 240 ) + { + secret_ = custom_secret_; + } + + if( n <= buffer_size - m_ ) + { + detail::memcpy( buffer_ + m_, p, n ); + m_ += n; + return; + } + + if( m_ > 0 ) + { + std::size_t k = buffer_size - m_; + detail::memcpy( buffer_ + m_, p, k ); + + p += k; + n -= k; + + for( std::size_t i = 0; i < 4; ++i ) + { + std::uint64_t stripe[ 8 ] = {}; + for( int j = 0; j < 8; ++j ) + { + stripe[ j ] = detail::read64le( buffer_ + ( 64 * i) + ( 8 * j ) ); + } + accumulate( stripe, 8 * num_stripes_ ); + ++num_stripes_; + + if( num_stripes_ == stripes_per_block_ ) + { + scramble(); + num_stripes_ = 0; + } + } + + m_ = 0; + } + + if( n > buffer_size ) + { + while( n > 64 ) + { + std::uint64_t stripe[ 8 ] = {}; + for( int j = 0; j < 8; ++j ) + { + stripe[ j ] = detail::read64le( p + ( 8 * j ) ); + } + accumulate( stripe, 8 * num_stripes_ ); + ++num_stripes_; + + if( num_stripes_ == stripes_per_block_ ) + { + scramble(); + num_stripes_ = 0; + } + + p += 64; + n -= 64; + } + + detail::memcpy( buffer_ + buffer_size - 64, p - 64, 64 ); + + BOOST_ASSERT( n <= 64 ); + } + + if( n > 0 ) + { + detail::memcpy( buffer_, p, n ); + m_ = n; + } + } + + BOOST_CXX14_CONSTEXPR result_type result() + { + result_type r; + + if( n_ == 0 ) + { + r = xxh3_128_digest_empty(); + } + else if( n_ < 4 ) + { + r = xxh3_128_digest_1to3(); + } + else if( n_ < 9 ) + { + r = xxh3_128_digest_4to8(); + } + else if( n_ < 17 ) + { + r = xxh3_128_digest_9to16(); + } + else if( n_ < 129 ) + { + r = xxh3_128_digest_17to128(); + } + else if( n_ < 241 ) + { + r = xxh3_128_digest_129to240(); + } + else + { + r = xxh3_128_digest_long(); + } + + detail::memset( buffer_, 0, buffer_size ); + + return r; + } +}; + +} // namespace hash2 +} // namespace boost + +#if defined(BOOST_MSVC) && BOOST_MSVC < 1920 +# pragma warning(pop) +#endif + +#endif // #ifndef BOOST_HASH2_XXH3_HPP_INCLUDED diff --git a/test/Jamfile b/test/Jamfile index 0732fe4..1eba259 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -123,6 +123,9 @@ run xxhash_2.cpp ; run xxhash_cx.cpp ; run xxhash_cx_2.cpp ; +run xxh3.cpp ; +run xxh3_cx.cpp ; + run siphash32.cpp ; run siphash64.cpp ; run siphash_cx.cpp ; diff --git a/test/xxh3.cpp b/test/xxh3.cpp new file mode 100644 index 0000000..d6440bc --- /dev/null +++ b/test/xxh3.cpp @@ -0,0 +1,241 @@ +// Copyright 2025 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +static std::vector make_test_bytes( std::size_t n ) +{ + std::vector v( n, 0 ); + + std::uint64_t gen = 2654435761u; + for( std::size_t i = 0; i < n; ++i ) + { + v[ i ] = static_cast( gen >> 56 ); + gen *= 11400714785074694797ull; + } + + return v; +} + +template typename H::result_type hash( char const * s, S seed ) +{ + H h( seed ); + + h.update( s, std::strlen( s ) ); + + return h.result(); +} + +template typename H::result_type hash( std::vector const& s, std::size_t n, S seed ) +{ + H h( seed ); + + h.update( s.data(), n ); + auto d = h.result(); + + H h2( seed ); + + std::size_t m = n / 3; + + h2.update( s.data(), m ); + h2.update( s.data() + m, n - m ); + BOOST_TEST_EQ( to_string( d ), to_string( h2.result() ) ); + + return d; +} + +template typename H::result_type hash( std::vector const& s, std::size_t n, unsigned char const* secret, std::size_t secret_len ) +{ + H h( secret, secret_len ); + + h.update( s.data(), n ); + auto d = h.result(); + + H h2( secret, secret_len ); + + std::size_t m = n / 3; + + h2.update( s.data(), m ); + h2.update( s.data() + m, n - m ); + BOOST_TEST_EQ( to_string( d ), to_string( h2.result() ) ); + + return d; +} + +struct xxh3_128_t +{ + std::uint64_t low; + std::uint64_t high; +}; + +static std::string hex_encode( xxh3_128_t x ) +{ + boost::hash2::digest<16> digest; + + boost::hash2::detail::write64be( digest.data() + 0 , x.high ); + boost::hash2::detail::write64be( digest.data() + 8 , x.low ); + + return to_string( digest ); +} + +static void test_xxh3_128() +{ + using namespace boost::hash2; + + // Test vectors from https://raw.githubusercontent.com/Cyan4973/xxHash/refs/heads/dev/tests/sanity_test_vectors.h + + auto const v = make_test_bytes( 4096 + 64 + 1 ); + + // empty + + BOOST_TEST_EQ( to_string( hash( "", 0 ) ), std::string( "99aa06d3014798d86001c324468d497f" ) ); + BOOST_TEST_EQ( to_string( hash( "", 0 ) ), hex_encode( { 0x6001c324468d497full, 0x99aa06d3014798d8ull } ) ); + + BOOST_TEST_EQ( to_string( hash( "", 0x000000009e3779b1ull ) ), hex_encode( { 0x5444f7869c671ab0ull, 0x92220ae55e14ab50ull } ) ); + BOOST_TEST_EQ( to_string( hash( "", 0x9e3779b185ebca8dull ) ), hex_encode( { 0xa986dfc5d7605bfeull, 0x00feaa732a3ce25eull } ) ); + + // 1-to-3 + + BOOST_TEST_EQ( to_string( hash( v, 1, 0x0000000000000000ull ) ), hex_encode( { 0xc44bdff4074eecdbull, 0xa6cd5e9392000f6aull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1, 0x000000009e3779b1ull ) ), hex_encode( { 0xb53d5557e7f76f8dull, 0x89b99554ba22467cull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x032be332dd766ef8ull, 0x20e49abcc53b3842ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 3, 0x0000000000000000ull ) ), hex_encode( { 0x54247382a8d6b94dull, 0x20efc49ff02422eaull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 3, 0x000000009e3779b1ull ) ), hex_encode( { 0xf173d14dad53a5dcull, 0x48f82c2fe0abd468ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 3, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x634b8990b4976373ull, 0x1c7ecf6a308cf00eull } ) ); + + // 4-to-8 + + BOOST_TEST_EQ( to_string( hash( v, 4, 0x0000000000000000ull ) ), hex_encode( { 0x2e7d8d6876a39fe9ull, 0x970d585ac632bf8eull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 4, 0x000000009e3779b1ull ) ), hex_encode( { 0xef78d5c489cfe10bull, 0x7170492a2aa08992ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 4, 0x9e3779b185ebca8dull ) ), hex_encode( { 0xbfaf51f1e67e0b0full, 0x3d53e5dfd837d927ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 8, 0x0000000000000000ull ) ), hex_encode( { 0x64c69cab4bb21dc5ull, 0x47a7f080d82bb456ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 8, 0x000000009e3779b1ull ) ), hex_encode( { 0x5f462f3de2e8b940ull, 0xf959013232655ff1ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 8, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x7b29471dc729b5ffull, 0xf50cec145bcd5c5aull } ) ); + + // 9-to-16 + + BOOST_TEST_EQ( to_string( hash( v, 9, 0x0000000000000000ull ) ), hex_encode( { 0xed7ccbc501eb7501ull, 0x564ef6078950d457ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 9, 0x000000009e3779b1ull ) ), hex_encode( { 0x07de00b45eee033aull, 0x75fb6d1bd353b45cull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 9, 0x9e3779b185ebca8dull ) ), hex_encode( { 0xaef5dfc0ac9f9044ull, 0x6b380b43ffa61042ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 16, 0x0000000000000000ull ) ), hex_encode( { 0x562980258a998629ull, 0xc68c368ecf8a9c05ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 16, 0x000000009e3779b1ull ) ), hex_encode( { 0xb07eeeab4c56392bull, 0x3767c90d0cdbb93dull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 16, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x0346d13a7a5498c7ull, 0x6ffcb80cd33085c8ull } ) ); + + // 17-to-128 + + BOOST_TEST_EQ( to_string( hash( v, 17, 0x0000000000000000ull ) ), hex_encode( { 0xabbc12d11973d7dbull, 0x955fa78643ed3669ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 17, 0x000000009e3779b1ull ) ), hex_encode( { 0x3cc9ff6cae79accbull, 0x99e7c628e75d6431ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 17, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x980a14119985a7dfull, 0xd77681219e464828ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 128, 0x0000000000000000ull ) ), hex_encode( { 0xebb15e34a7fb5ab1ull, 0x39992220e045260aull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 128, 0x000000009e3779b1ull ) ), hex_encode( { 0x1453819941d93c1dull, 0x98801187df8d614dull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 128, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x8394f5c51f1d8246ull, 0xa0f7ccb68ee02addull } ) ); + + // 129-to-240 + + BOOST_TEST_EQ( to_string( hash( v, 129, 0x0000000000000000ull ) ), hex_encode( { 0x86c9e3bc8f0a3b5cull, 0x03815fc91f1b30b6ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 129, 0x000000009e3779b1ull ) ), hex_encode( { 0xb37b716f66b40f02ull, 0xb7f7349a47b39e56ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 129, 0x9e3779b185ebca8dull ) ), hex_encode( { 0xd4aae26fcec7dc03ull, 0xad559266067c0bf3ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 240, 0x0000000000000000ull ) ), hex_encode( { 0x5c9aae94c8ebe5a0ull, 0xaa4202daa2769dc8ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 240, 0x000000009e3779b1ull ) ), hex_encode( { 0xca19087f1d335daeull, 0xda888104beae5ae0ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 240, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x604e98db085c1864ull, 0x29d2133d6ea58c5bull } ) ); + + // 240+ + + BOOST_TEST_EQ( to_string( hash( v, 241, 0x0000000000000000ull ) ), hex_encode( { 0xc5a639ecd2030e5eull, 0x99a80ecf0ecfc647ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 241, 0x000000009e3779b1ull ) ), hex_encode( { 0x5927e3637bac8149ull, 0x4bf2229c3a8fc3c3ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 241, 0x9e3779b185ebca8dull ) ), hex_encode( { 0xdda9b0a161d4829aull, 0xec64afae6a137582ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 255, 0x0000000000000000ull ) ), hex_encode( { 0xe98f979f4ed8a197ull, 0x961375c87e09efbcull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 255, 0x000000009e3779b1ull ) ), hex_encode( { 0x437ea109cb7ce24dull, 0xee657e12607adffeull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 255, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x2aca7901d9538c75ull, 0xe72ec0137d62df44ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 256, 0x0000000000000000ull ) ), hex_encode( { 0x55de574ad89d0ac5ull, 0x8b1c66091423d288ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 256, 0x000000009e3779b1ull ) ), hex_encode( { 0x443d04d43f60c57full, 0xd540cc8620d8dd65ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 256, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x4d30234b7a3aa61cull, 0xaaa57235b92d5e7cull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 257, 0x0000000000000000ull ) ), hex_encode( { 0xb17fd5a8ae75bb0bull, 0xf15fee7f9f457599ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 257, 0x000000009e3779b1ull ) ), hex_encode( { 0x02f16a1476c65d95ull, 0x52c36ca232fc662bull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 257, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x802a6fbf3cacd97cull, 0x15c1f9c667c815baull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 511, 0x0000000000000000ull ) ), hex_encode( { 0x8089715b163e7fc0ull, 0x9f7619cb8d250f0dull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 511, 0x000000009e3779b1ull ) ), hex_encode( { 0x96736274a52c7db2ull, 0x24e3bb97c7c584d4ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 511, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x90ec0377ba8d6002ull, 0xb52cae55536e9fb9ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 512, 0x0000000000000000ull ) ), hex_encode( { 0x617e49599013cb6bull, 0x18d2d110dcc9bca1ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 512, 0x000000009e3779b1ull ) ), hex_encode( { 0x545f610e9f5a78ecull, 0x06eeb0d56508040full } ) ); + BOOST_TEST_EQ( to_string( hash( v, 512, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x3ce457de14c27708ull, 0x925d06b8ec5b8040ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 1023, 0x0000000000000000ull ) ), hex_encode( { 0x87a8f7b2f2e22496ull, 0xe8083e4d83214c3cull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1023, 0x000000009e3779b1ull ) ), hex_encode( { 0xc38922d5971cd2d7ull, 0xfbd299789b9a9759ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1023, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x0f0f02de8590e1b5ull, 0x96b80fe329ce5e35ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 1024, 0x0000000000000000ull ) ), hex_encode( { 0xdd85c9b5c1109c5cull, 0x0d30d24071c64c57ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1024, 0x000000009e3779b1ull ) ), hex_encode( { 0xb8b95c07cd4a75faull, 0x885b0b4debe3d2ffull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1024, 0x9e3779b185ebca8dull ) ), hex_encode( { 0xef368a8a2ebabaefull, 0x17600efe2b493a18ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 1025, 0x0000000000000000ull ) ), hex_encode( { 0xd870c0fa13211c6aull, 0xfd3ee4fe7f2954c6ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1025, 0x000000009e3779b1ull ) ), hex_encode( { 0x2f15255340ae4f6cull, 0x3364fad6f5ff1741ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1025, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x96792bcf9af88519ull, 0x2c383949f57bf7e1ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 2047, 0x0000000000000000ull ) ), hex_encode( { 0xb36ece19fca2197full, 0x763a9143f0523d15ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2047, 0x000000009e3779b1ull ) ), hex_encode( { 0x8141f69f4bacdea2ull, 0xd2605592ab25dc1aull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2047, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x8111bb82842ed0aeull, 0x47c992688e710651ull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 2048, 0x0000000000000000ull ) ), hex_encode( { 0xdd59e2c3a5f038e0ull, 0xf736557fd47073a5ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2048, 0x000000009e3779b1ull ) ), hex_encode( { 0x230d43f30206260bull, 0x7fb03f7e7186c3eaull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2048, 0x9e3779b185ebca8dull ) ), hex_encode( { 0x66f81670669ababcull, 0x23cc3a2e75ebaaeaull } ) ); + + BOOST_TEST_EQ( to_string( hash( v, 2049, 0x0000000000000000ull ) ), hex_encode( { 0xd3afa4329779b921ull, 0x4cd2bd192f2d70bdull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2049, 0x000000009e3779b1ull ) ), hex_encode( { 0x60e0f49946d79dafull, 0x782bc6262b86bfe8ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2049, 0x9e3779b185ebca8dull ) ), hex_encode( { 0xe48083836cd58024ull, 0xe4000f7a288a82ceull } ) ); + + // test with custom secret + + unsigned char const* secret = v.data() + 7; + std::size_t const secret_len = 136 + 11; + + BOOST_TEST_EQ( to_string( hash( v, 0, secret, secret_len ) ), hex_encode( { 0x005923cceecbe8aeull, 0x5f70f4ea232f1d38ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1, secret, secret_len ) ), hex_encode( { 0x8a52451418b2da4dull, 0x3a66af5a9819198eull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 3, secret, secret_len ) ), hex_encode( { 0xe9af94712ffbc846ull, 0x51103173fa1f0727ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 4, secret, secret_len ) ), hex_encode( { 0x266a9b610a7a5641ull, 0xccc924914b0d8032ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 8, secret, secret_len ) ), hex_encode( { 0xf668474d2fee1f92ull, 0x20ed43ff46f7a0a1ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 9, secret, secret_len ) ), hex_encode( { 0xc3bbf94649c59dfcull, 0x6af09813af70cfd1ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 16, secret, secret_len ) ), hex_encode( { 0xfe396195466852b9ull, 0x4c317fd601bcda88ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 17, secret, secret_len ) ), hex_encode( { 0xe94eb4616009b975ull, 0x604cc5ee8f142950ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 128, secret, secret_len ) ), hex_encode( { 0xb8feec0b6b6eaf60ull, 0x1df8cce15fe35b2cull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 129, secret, secret_len ) ), hex_encode( { 0x9def70d87b89ed7bull, 0x72d4d4395002b150ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 240, secret, secret_len ) ), hex_encode( { 0x29dd17317e40cba2ull, 0x8033fd83d4336ca9ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 241, secret, secret_len ) ), hex_encode( { 0x454805371df98a91ull, 0x0ecde988107f17f2ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 255, secret, secret_len ) ), hex_encode( { 0xe1e3461712968b3eull, 0xf44f7290a7123665ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 256, secret, secret_len ) ), hex_encode( { 0xd4cba59e2e2cf9f0ull, 0xdc8cd5dc03c0da95ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 257, secret, secret_len ) ), hex_encode( { 0x1e4b71e703d08492ull, 0x15fda9442e840f61ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 511, secret, secret_len ) ), hex_encode( { 0x13e7046bc1c1f16aull, 0x86764f81bb226a35ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 512, secret, secret_len ) ), hex_encode( { 0x7564693dd526e28dull, 0x918c0f2c7656ab6dull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1023, secret, secret_len ) ), hex_encode( { 0x6df5a1773b876cfbull, 0x21fe7c4fbcebe042ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1024, secret, secret_len ) ), hex_encode( { 0x3538a2d1ea7410d0ull, 0x7663338d0b32666dull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 1025, secret, secret_len ) ), hex_encode( { 0xe33739f32d405604ull, 0x3644184c7d1e8f29ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2047, secret, secret_len ) ), hex_encode( { 0x209243520dbdb300ull, 0x47aa10ba88a049f3ull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2048, secret, secret_len ) ), hex_encode( { 0xd32e975821d6519full, 0xe862d841c07049afull } ) ); + BOOST_TEST_EQ( to_string( hash( v, 2049, secret, secret_len ) ), hex_encode( { 0xa21be3a04630def3ull, 0x545e67046af902fbull } ) ); + + // not testing the output, just making sure we don't segfault here + + BOOST_TEST_NE( to_string( hash( v, 0, secret, 1 ) ), std::string( "" )); + BOOST_TEST_NE( to_string( hash( v, 0, secret, 135 ) ), std::string( "" )); +} + +int main() +{ + test_xxh3_128(); + + return boost::report_errors(); +} diff --git a/test/xxh3_cx.cpp b/test/xxh3_cx.cpp new file mode 100644 index 0000000..3bd8056 --- /dev/null +++ b/test/xxh3_cx.cpp @@ -0,0 +1,230 @@ +// Copyright 2025 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +#include + +#if defined(BOOST_MSVC) && BOOST_MSVC < 1920 +# pragma warning(disable: 4307) // integral constant overflow +#endif + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +#if defined(BOOST_NO_CXX14_CONSTEXPR) +# define TEST_EQ(x1, x2) BOOST_TEST_EQ(x1, x2) +# define TEST_NE(x1, x2) BOOST_TEST_NE(x1, x2) +#else +# define TEST_EQ(x1, x2) BOOST_TEST_EQ(x1, x2); STATIC_ASSERT( x1 == x2 ) +# define TEST_NE(x1, x2) BOOST_TEST_NE(x1, x2); STATIC_ASSERT( x1 != x2 ) +#endif + +constexpr static unsigned char const test_bytes[] = +{ + 0, 82, 146, 155, 183, 50, 163, 36, 45, 0, 175, 149, 14, 236, 184, 147, 227, 223, 239, 147, 170, 214, 205, 42, 83, 139, 92, 63, 84, 90, 111, 213, 89, + 192, 255, 252, 143, 133, 185, 51, 29, 171, 116, 247, 182, 5, 147, 39, 176, 112, 132, 179, 103, 124, 159, 118, 72, 0, 114, 237, 123, 152, 23, 232, 221, + 72, 94, 12, 12, 203, 208, 101, 63, 173, 178, 143, 17, 176, 108, 232, 141, 176, 241, 134, 8, 97, 89, 86, 108, 142, 78, 120, 19, 99, 189, 171, 157, + 50, 115, 9, 234, 113, 47, 217, 122, 157, 85, 240, 202, 138, 208, 233, 94, 26, 54, 179, 107, 15, 202, 81, 239, 139, 162, 196, 98, 237, 0, 150, 243, + 52, 73, 235, 15, 209, 59, 146, 161, 169, 99, 219, 170, 237, 61, 207, 241, 9, 66, 205, 249, 179, 33, 162, 235, 242, 200, 244, 228, 47, 72, 209, 75, + 16, 244, 194, 239, 236, 248, 74, 181, 56, 116, 195, 164, 166, 98, 14, 191, 253, 99, 55, 65, 227, 134, 152, 26, 235, 76, 186, 86, 3, 102, 135, 237, + 0, 69, 89, 193, 133, 68, 182, 195, 104, 249, 65, 169, 234, 249, 135, 224, 159, 18, 208, 213, 20, 84, 72, 93, 68, 64, 81, 227, 56, 6, 154, 76, + 60, 13, 239, 100, 137, 248, 163, 97, 238, 227, 197, 28, 147, 104, 200, 229, 151, 215, 9, 152, 255, 151, 168, 76, 234, 164, 121, 171, 205, 44, 200, 159, + 27, 242, 241, 206, 226, 130, 80, 56, 130, 173, 142, 155, 184, 180, 136, 129, 142, 182, 54, 217, 169, 101, 72, 116, 60, 201, 219, 252, 187, 216, 42, 34, + 158, 180, 36, 226, 178, 103, 171, 170, 19, 226, 42, 77, 252, 113, 28, 7, 98, 145, 189, 248, 226, 151, 93, 12, 132, 253, 103, 69, 103, 27, 9, 154, + 75, 32, 162, 119, 48, 149, 218, 40, 177, 134, 63, 158, 242, 225, 219, 16, 223, 144, 248, 109, 145, 47, 6, 120, 179, 72, 91, 223, 246, 219, 216, 124, + 167, 174, 165, 137, 64, 76, 2, 115, 32, 240, 159, 221, 99, 121, 149, 177, 3, 196, 126, 111, 47, 224, 34, 246, 212, 136, 233, 102, 110, 157, 53, 25, + 189, 65, 27, 79, 26, 207, 213, 120, 176, 0, 73, 240, 31, 143, 141, 246, 20, 188, 181, 148, 109, 196, 83, 118, 167, 167, 83, 145, 207, 141, 182, 22, + 108, 57, 126, 54, 47, 6, 242, 35, 30, 142, 15, 200, 134, 244, 118, 97, 142, 246, 100, 54, 252, 140, 39, 161, 250, 18, 203, 254, 206, 88, 63, 129, + 103, 136, 108, 125, 192, 129, 231, 248, 192, 142, 84, 14, 201, 217, 81, 157, 33, 144, 53, 105, 10, 11, 172, 161, 113, 68, 75, 159, 69, 254, 157, 152, + 31, 30, 27, 119, 108, 93, 241, 251, 126, 44, 100, 254, 107, 87, 25, 104, 205, 221, 228, 208, 9, 54, 26, 96, 153, 98, 215, 7, 230, 77, 9, +}; + +struct xxh3_128_t +{ + std::uint64_t low; + std::uint64_t high; +}; + +BOOST_CXX14_CONSTEXPR static boost::hash2::digest<16> hex_encode( xxh3_128_t x ) +{ + boost::hash2::digest<16> digest; + + boost::hash2::detail::write64be( digest.data() + 0 , x.high ); + boost::hash2::detail::write64be( digest.data() + 8 , x.low ); + + return digest; +} + +template +BOOST_CXX14_CONSTEXPR boost::hash2::digest digest_from_hex( char const (&str)[ N ] ) +{ + boost::hash2::digest dgst = {}; + auto* p = dgst.data(); + for( unsigned i = 0; i < M; ++i ) { + auto c1 = to_byte( str[ 2 * i ] ); + auto c2 = to_byte( str[ 2 * i + 1 ] ); + p[ i ] = ( c1 << 4 ) | c2; + } + return dgst; +} + +BOOST_CXX14_CONSTEXPR unsigned char to_byte( char c ) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return 0xff; +} + +template BOOST_CXX14_CONSTEXPR typename H::result_type hash( std::size_t n, std::uint64_t seed ) +{ + H h( seed ); + + std::size_t m = n / 3; + + h.update( test_bytes, m ); + h.update( test_bytes + m, n - m ); + + auto d = h.result(); + + H h2( seed ); + h2.update( test_bytes, n ); + if( h2.result() != d ) + { + throw 1234; + } + + return d; +} + +template BOOST_CXX14_CONSTEXPR typename H::result_type hash( std::size_t n, unsigned char const* secret, std::size_t secret_len ) +{ + H h( secret, secret_len ); + + std::size_t m = n / 3; + + h.update( test_bytes, m ); + h.update( test_bytes + m, n - m ); + + return h.result(); +} + +int main() +{ + using namespace boost::hash2; + + // empty + + TEST_EQ( hash( 0, 0x0000000000000000ull ), hex_encode( { 0x6001c324468d497full, 0x99aa06d3014798d8ull } ) ); + + TEST_EQ( hash( 0, 0x000000009e3779b1ull ), hex_encode( { 0x5444f7869c671ab0ull, 0x92220ae55e14ab50ull } ) ); + TEST_EQ( hash( 0, 0x9e3779b185ebca8dull ), hex_encode( { 0xa986dfc5d7605bfeull, 0x00feaa732a3ce25eull } ) ); + + // 1-to-3 + + TEST_EQ( hash( 1, 0x0000000000000000ull ), hex_encode( { 0xc44bdff4074eecdbull, 0xa6cd5e9392000f6aull } ) ); + TEST_EQ( hash( 1, 0x000000009e3779b1ull ), hex_encode( { 0xb53d5557e7f76f8dull, 0x89b99554ba22467cull } ) ); + TEST_EQ( hash( 1, 0x9e3779b185ebca8dull ), hex_encode( { 0x032be332dd766ef8ull, 0x20e49abcc53b3842ull } ) ); + + TEST_EQ( hash( 3, 0x0000000000000000ull ), hex_encode( { 0x54247382a8d6b94dull, 0x20efc49ff02422eaull } ) ); + TEST_EQ( hash( 3, 0x000000009e3779b1ull ), hex_encode( { 0xf173d14dad53a5dcull, 0x48f82c2fe0abd468ull } ) ); + TEST_EQ( hash( 3, 0x9e3779b185ebca8dull ), hex_encode( { 0x634b8990b4976373ull, 0x1c7ecf6a308cf00eull } ) ); + + // 4-to-8 + + TEST_EQ( hash( 4, 0x0000000000000000ull ), hex_encode( { 0x2e7d8d6876a39fe9ull, 0x970d585ac632bf8eull } ) ); + TEST_EQ( hash( 4, 0x000000009e3779b1ull ), hex_encode( { 0xef78d5c489cfe10bull, 0x7170492a2aa08992ull } ) ); + TEST_EQ( hash( 4, 0x9e3779b185ebca8dull ), hex_encode( { 0xbfaf51f1e67e0b0full, 0x3d53e5dfd837d927ull } ) ); + + TEST_EQ( hash( 8, 0x0000000000000000ull ), hex_encode( { 0x64c69cab4bb21dc5ull, 0x47a7f080d82bb456ull } ) ); + TEST_EQ( hash( 8, 0x000000009e3779b1ull ), hex_encode( { 0x5f462f3de2e8b940ull, 0xf959013232655ff1ull } ) ); + TEST_EQ( hash( 8, 0x9e3779b185ebca8dull ), hex_encode( { 0x7b29471dc729b5ffull, 0xf50cec145bcd5c5aull } ) ); + + // 9-to-16 + + TEST_EQ( hash( 9, 0x0000000000000000ull ), hex_encode( { 0xed7ccbc501eb7501ull, 0x564ef6078950d457ull } ) ); + TEST_EQ( hash( 9, 0x000000009e3779b1ull ), hex_encode( { 0x07de00b45eee033aull, 0x75fb6d1bd353b45cull } ) ); + TEST_EQ( hash( 9, 0x9e3779b185ebca8dull ), hex_encode( { 0xaef5dfc0ac9f9044ull, 0x6b380b43ffa61042ull } ) ); + + TEST_EQ( hash( 16, 0x0000000000000000ull ), hex_encode( { 0x562980258a998629ull, 0xc68c368ecf8a9c05ull } ) ); + TEST_EQ( hash( 16, 0x000000009e3779b1ull ), hex_encode( { 0xb07eeeab4c56392bull, 0x3767c90d0cdbb93dull } ) ); + TEST_EQ( hash( 16, 0x9e3779b185ebca8dull ), hex_encode( { 0x0346d13a7a5498c7ull, 0x6ffcb80cd33085c8ull } ) ); + + // 17-to-128 + + TEST_EQ( hash( 17, 0x0000000000000000ull ), hex_encode( { 0xabbc12d11973d7dbull, 0x955fa78643ed3669ull } ) ); + TEST_EQ( hash( 17, 0x000000009e3779b1ull ), hex_encode( { 0x3cc9ff6cae79accbull, 0x99e7c628e75d6431ull } ) ); + TEST_EQ( hash( 17, 0x9e3779b185ebca8dull ), hex_encode( { 0x980a14119985a7dfull, 0xd77681219e464828ull } ) ); + + TEST_EQ( hash( 128, 0x0000000000000000ull ), hex_encode( { 0xebb15e34a7fb5ab1ull, 0x39992220e045260aull } ) ); + TEST_EQ( hash( 128, 0x000000009e3779b1ull ), hex_encode( { 0x1453819941d93c1dull, 0x98801187df8d614dull } ) ); + TEST_EQ( hash( 128, 0x9e3779b185ebca8dull ), hex_encode( { 0x8394f5c51f1d8246ull, 0xa0f7ccb68ee02addull } ) ); + + // 129-to-240 + + TEST_EQ( hash( 129, 0x0000000000000000ull ), hex_encode( { 0x86c9e3bc8f0a3b5cull, 0x03815fc91f1b30b6ull } ) ); + TEST_EQ( hash( 129, 0x000000009e3779b1ull ), hex_encode( { 0xb37b716f66b40f02ull, 0xb7f7349a47b39e56ull } ) ); + TEST_EQ( hash( 129, 0x9e3779b185ebca8dull ), hex_encode( { 0xd4aae26fcec7dc03ull, 0xad559266067c0bf3ull } ) ); + + TEST_EQ( hash( 240, 0x0000000000000000ull ), hex_encode( { 0x5c9aae94c8ebe5a0ull, 0xaa4202daa2769dc8ull } ) ); + TEST_EQ( hash( 240, 0x000000009e3779b1ull ), hex_encode( { 0xca19087f1d335daeull, 0xda888104beae5ae0ull } ) ); + TEST_EQ( hash( 240, 0x9e3779b185ebca8dull ), hex_encode( { 0x604e98db085c1864ull, 0x29d2133d6ea58c5bull } ) ); + + // 240+ + + TEST_EQ( hash( 241, 0x0000000000000000ull ), hex_encode( { 0xc5a639ecd2030e5eull, 0x99a80ecf0ecfc647ull } ) ); + TEST_EQ( hash( 241, 0x000000009e3779b1ull ), hex_encode( { 0x5927e3637bac8149ull, 0x4bf2229c3a8fc3c3ull } ) ); + TEST_EQ( hash( 241, 0x9e3779b185ebca8dull ), hex_encode( { 0xdda9b0a161d4829aull, 0xec64afae6a137582ull } ) ); + + TEST_EQ( hash( 255, 0x0000000000000000ull ), hex_encode( { 0xe98f979f4ed8a197ull, 0x961375c87e09efbcull } ) ); + TEST_EQ( hash( 255, 0x000000009e3779b1ull ), hex_encode( { 0x437ea109cb7ce24dull, 0xee657e12607adffeull } ) ); + TEST_EQ( hash( 255, 0x9e3779b185ebca8dull ), hex_encode( { 0x2aca7901d9538c75ull, 0xe72ec0137d62df44ull } ) ); + + TEST_EQ( hash( 256, 0x0000000000000000ull ), hex_encode( { 0x55de574ad89d0ac5ull, 0x8b1c66091423d288ull } ) ); + TEST_EQ( hash( 256, 0x000000009e3779b1ull ), hex_encode( { 0x443d04d43f60c57full, 0xd540cc8620d8dd65ull } ) ); + TEST_EQ( hash( 256, 0x9e3779b185ebca8dull ), hex_encode( { 0x4d30234b7a3aa61cull, 0xaaa57235b92d5e7cull } ) ); + + TEST_EQ( hash( 257, 0x0000000000000000ull ), hex_encode( { 0xb17fd5a8ae75bb0bull, 0xf15fee7f9f457599ull } ) ); + TEST_EQ( hash( 257, 0x000000009e3779b1ull ), hex_encode( { 0x02f16a1476c65d95ull, 0x52c36ca232fc662bull } ) ); + TEST_EQ( hash( 257, 0x9e3779b185ebca8dull ), hex_encode( { 0x802a6fbf3cacd97cull, 0x15c1f9c667c815baull } ) ); + + TEST_EQ( hash( 511, 0x0000000000000000ull ), hex_encode( { 0x8089715b163e7fc0ull, 0x9f7619cb8d250f0dull } ) ); + TEST_EQ( hash( 511, 0x000000009e3779b1ull ), hex_encode( { 0x96736274a52c7db2ull, 0x24e3bb97c7c584d4ull } ) ); + TEST_EQ( hash( 511, 0x9e3779b185ebca8dull ), hex_encode( { 0x90ec0377ba8d6002ull, 0xb52cae55536e9fb9ull } ) ); + + TEST_EQ( hash( 512, 0x0000000000000000ull ), hex_encode( { 0x617e49599013cb6bull, 0x18d2d110dcc9bca1ull } ) ); + TEST_EQ( hash( 512, 0x000000009e3779b1ull ), hex_encode( { 0x545f610e9f5a78ecull, 0x06eeb0d56508040full } ) ); + TEST_EQ( hash( 512, 0x9e3779b185ebca8dull ), hex_encode( { 0x3ce457de14c27708ull, 0x925d06b8ec5b8040ull } ) ); + + constexpr unsigned char const* secret = test_bytes + 7; + constexpr std::size_t const secret_len = 136 + 11; + + TEST_EQ( hash( 0, secret, secret_len ), hex_encode( { 0x005923cceecbe8aeull, 0x5f70f4ea232f1d38ull } ) ); + TEST_EQ( hash( 1, secret, secret_len ), hex_encode( { 0x8a52451418b2da4dull, 0x3a66af5a9819198eull } ) ); + TEST_EQ( hash( 3, secret, secret_len ), hex_encode( { 0xe9af94712ffbc846ull, 0x51103173fa1f0727ull } ) ); + TEST_EQ( hash( 4, secret, secret_len ), hex_encode( { 0x266a9b610a7a5641ull, 0xccc924914b0d8032ull } ) ); + TEST_EQ( hash( 8, secret, secret_len ), hex_encode( { 0xf668474d2fee1f92ull, 0x20ed43ff46f7a0a1ull } ) ); + TEST_EQ( hash( 9, secret, secret_len ), hex_encode( { 0xc3bbf94649c59dfcull, 0x6af09813af70cfd1ull } ) ); + TEST_EQ( hash( 16, secret, secret_len ), hex_encode( { 0xfe396195466852b9ull, 0x4c317fd601bcda88ull } ) ); + TEST_EQ( hash( 17, secret, secret_len ), hex_encode( { 0xe94eb4616009b975ull, 0x604cc5ee8f142950ull } ) ); + TEST_EQ( hash( 128, secret, secret_len ), hex_encode( { 0xb8feec0b6b6eaf60ull, 0x1df8cce15fe35b2cull } ) ); + TEST_EQ( hash( 129, secret, secret_len ), hex_encode( { 0x9def70d87b89ed7bull, 0x72d4d4395002b150ull } ) ); + TEST_EQ( hash( 240, secret, secret_len ), hex_encode( { 0x29dd17317e40cba2ull, 0x8033fd83d4336ca9ull } ) ); + TEST_EQ( hash( 241, secret, secret_len ), hex_encode( { 0x454805371df98a91ull, 0x0ecde988107f17f2ull } ) ); + TEST_EQ( hash( 255, secret, secret_len ), hex_encode( { 0xe1e3461712968b3eull, 0xf44f7290a7123665ull } ) ); + TEST_EQ( hash( 256, secret, secret_len ), hex_encode( { 0xd4cba59e2e2cf9f0ull, 0xdc8cd5dc03c0da95ull } ) ); + TEST_EQ( hash( 257, secret, secret_len ), hex_encode( { 0x1e4b71e703d08492ull, 0x15fda9442e840f61ull } ) ); + TEST_EQ( hash( 511, secret, secret_len ), hex_encode( { 0x13e7046bc1c1f16aull, 0x86764f81bb226a35ull } ) ); + TEST_EQ( hash( 512, secret, secret_len ), hex_encode( { 0x7564693dd526e28dull, 0x918c0f2c7656ab6dull } ) ); + + constexpr digest<16> d = {}; + + TEST_NE( hash( 0, secret, 1 ), d ); + TEST_NE( hash( 0, secret, 135 ), d ); + + return boost::report_errors(); +} From d66dcba3cdd4b2a75dafd9772c1f48922cc02b17 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 25 Apr 2025 07:51:48 -0700 Subject: [PATCH 4/4] add xxh3-128 to existing tests --- benchmark/buffer.cpp | 2 ++ test/concept.cpp | 15 +++++++++++++++ test/integral_result.cpp | 2 ++ test/multiple_result.cpp | 2 ++ test/plaintext_leak.cpp | 2 ++ test/quality.cpp | 2 ++ 6 files changed, 25 insertions(+) diff --git a/benchmark/buffer.cpp b/benchmark/buffer.cpp index 06b6146..b1b9aa0 100644 --- a/benchmark/buffer.cpp +++ b/benchmark/buffer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ void test( int N, int M ) test_( data, N, M ); test_( data, N, M ); test_( data, N, M ); + test_( data, N, M ); test_( data, N, M ); test_( data, N, M ); test_( data, N, M ); diff --git a/test/concept.cpp b/test/concept.cpp index 2e1e29c..41275f0 100644 --- a/test/concept.cpp +++ b/test/concept.cpp @@ -2,9 +2,23 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt +// due to aggressiving inlining effects, it seems we can only reliably silence this warning +// in the end TU itself +#include +#if BOOST_WORKAROUND(BOOST_GCC, >= 110000 && BOOST_GCC < 120000) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstringop-overread" +#endif + +#if BOOST_WORKAROUND(BOOST_GCC, >= 80000 && BOOST_GCC < 90000) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Warray-bounds" +#endif + #include #include #include +#include #include #include #include @@ -510,6 +524,7 @@ int main() test(); test(); test(); + test(); test(); test(); diff --git a/test/integral_result.cpp b/test/integral_result.cpp index 6c830d0..ac8d7c0 100644 --- a/test/integral_result.cpp +++ b/test/integral_result.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ int main() test(); test(); test(); + test(); test(); test(); diff --git a/test/multiple_result.cpp b/test/multiple_result.cpp index 6aada0c..4bfd009 100644 --- a/test/multiple_result.cpp +++ b/test/multiple_result.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,7 @@ int main() test(); test(); test(); + test(); test(); test(); diff --git a/test/plaintext_leak.cpp b/test/plaintext_leak.cpp index 8316454..9cbd224 100644 --- a/test/plaintext_leak.cpp +++ b/test/plaintext_leak.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,7 @@ int main() test(); test(); test(); + test(); test(); test(); diff --git a/test/quality.cpp b/test/quality.cpp index f80e6f0..516e01a 100644 --- a/test/quality.cpp +++ b/test/quality.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -80,6 +81,7 @@ int main() test(); test(); test(); + test(); test(); test();