From 59fb676e401fc67b72b92aa8f232e996e5f65664 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 03:34:17 +0200 Subject: [PATCH 01/12] Remove detail/reverse.hpp --- include/boost/hash2/detail/reverse.hpp | 105 ------------------------- test/Jamfile | 1 - test/detail_reverse.cpp | 40 ---------- 3 files changed, 146 deletions(-) delete mode 100644 include/boost/hash2/detail/reverse.hpp delete mode 100644 test/detail_reverse.cpp diff --git a/include/boost/hash2/detail/reverse.hpp b/include/boost/hash2/detail/reverse.hpp deleted file mode 100644 index bfab480..0000000 --- a/include/boost/hash2/detail/reverse.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef BOOST_HASH2_DETAIL_REVERSE_HPP_INCLUDED -#define BOOST_HASH2_DETAIL_REVERSE_HPP_INCLUDED - -// Copyright 2024 Peter Dimov. -// Distributed under the Boost Software License, Version 1.0. -// https://www.boost.org/LICENSE_1_0.txt - -#include -#include -#include - -namespace boost -{ -namespace hash2 -{ -namespace detail -{ - -template void reverse( unsigned char (&d)[ N ], void const* s ) -{ - unsigned char const* s2 = static_cast( s ); - - for( std::size_t i = 0; i < N; ++i ) - { - d[ i ] = s2[ N-1-i ]; - } -} - -#if defined(__GNUC__) || defined(__clang__) - -inline void reverse( unsigned char (&d)[ 2 ], void const* s ) -{ - std::uint16_t tmp; - std::memcpy( &tmp, s, 2 ); - tmp = __builtin_bswap16( tmp ); - std::memcpy( d, &tmp, 2 ); -} - -inline void reverse( unsigned char (&d)[ 4 ], void const* s ) -{ - std::uint32_t tmp; - std::memcpy( &tmp, s, 4 ); - tmp = __builtin_bswap32( tmp ); - std::memcpy( d, &tmp, 4 ); -} - -inline void reverse( unsigned char (&d)[ 8 ], void const* s ) -{ - std::uint64_t tmp; - std::memcpy( &tmp, s, 8 ); - tmp = __builtin_bswap64( tmp ); - std::memcpy( d, &tmp, 8 ); -} - -inline void reverse( unsigned char (&d)[ 16 ], void const* s ) -{ - std::uint64_t tmp[ 2 ]; - std::memcpy( tmp, s, 16 ); - - std::uint64_t tmp2[ 2 ] = { __builtin_bswap64( tmp[1] ), __builtin_bswap64( tmp[0] ) }; - std::memcpy( d, tmp2, 16 ); -} - -#elif defined(_MSC_VER) - -inline void reverse( unsigned char (&d)[ 2 ], void const* s ) -{ - unsigned short tmp; - std::memcpy( &tmp, s, 2 ); - tmp = _byteswap_ushort( tmp ); - std::memcpy( d, &tmp, 2 ); -} - -inline void reverse( unsigned char (&d)[ 4 ], void const* s ) -{ - unsigned long tmp; - std::memcpy( &tmp, s, 4 ); - tmp = _byteswap_ulong( tmp ); - std::memcpy( d, &tmp, 4 ); -} - -inline void reverse( unsigned char (&d)[ 8 ], void const* s ) -{ - unsigned long long tmp; - std::memcpy( &tmp, s, 8 ); - tmp = _byteswap_uint64( tmp ); - std::memcpy( d, &tmp, 8 ); -} - -inline void reverse( unsigned char (&d)[ 16 ], void const* s ) -{ - unsigned long long tmp[ 2 ]; - std::memcpy( tmp, s, 16 ); - - unsigned long long tmp2[ 2 ] = { _byteswap_uint64( tmp[1] ), _byteswap_uint64( tmp[0] ) }; - std::memcpy( d, tmp2, 16 ); -} - -#endif - -} // namespace detail -} // namespace hash2 -} // namespace boost - -#endif // #ifndef BOOST_HASH2_DETAIL_REVERSE_HPP_INCLUDED diff --git a/test/Jamfile b/test/Jamfile index 166a3b3..b7036d1 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -41,7 +41,6 @@ run detail_read.cpp ; run detail_write.cpp ; run detail_write_2.cpp ; run detail_rot.cpp ; -run detail_reverse.cpp ; run detail_has_tag_invoke.cpp ; # hash_append diff --git a/test/detail_reverse.cpp b/test/detail_reverse.cpp deleted file mode 100644 index 93f252e..0000000 --- a/test/detail_reverse.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2024 Peter Dimov. -// Distributed under the Boost Software License, Version 1.0. -// https://www.boost.org/LICENSE_1_0.txt - -#include -#include - -template void test() -{ - unsigned char v[ N ] = {}; - for( std::size_t i = 0; i < N; ++i ) v[ i ] = static_cast( i + 1 ); - - unsigned char w[ N ] = {}; - boost::hash2::detail::reverse( w, v ); - - for( std::size_t i = 0; i < N; ++i ) BOOST_TEST_EQ( w[ i ], v[ N - 1 - i ] ); -} - -int main() -{ - test<1>(); - test<2>(); - test<3>(); - test<4>(); - test<5>(); - test<6>(); - test<7>(); - test<8>(); - test<9>(); - test<10>(); - test<11>(); - test<12>(); - test<13>(); - test<14>(); - test<15>(); - test<16>(); - test<17>(); - - return boost::report_errors(); -} From 8b3d1a8e05d8fad714c0fecd9a43dae3fcd49cb6 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 18:42:35 +0200 Subject: [PATCH 02/12] Add static assertions to get_integral_result --- include/boost/hash2/get_integral_result.hpp | 30 +++++++++++++-------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/include/boost/hash2/get_integral_result.hpp b/include/boost/hash2/get_integral_result.hpp index 7ccef06..d56e411 100644 --- a/include/boost/hash2/get_integral_result.hpp +++ b/include/boost/hash2/get_integral_result.hpp @@ -5,9 +5,7 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -#include #include -#include #include #include @@ -20,7 +18,13 @@ template typename std::enable_if::value && (sizeof(R) >= sizeof(T)), T>::type get_integral_result( R const & r ) { + static_assert( std::is_integral::value, "T must be integral" ); + static_assert( !std::is_same::type, bool>::value, "T must not be bool" ); + + static_assert( std::is_unsigned::value, "R must be unsigned" ); + typedef typename std::make_unsigned::type U; + return static_cast( static_cast( r ) ); } @@ -28,21 +32,25 @@ template typename std::enable_if::value && sizeof(R) == 4 && sizeof(T) == 8, T>::type get_integral_result( R const & r ) { + static_assert( std::is_integral::value, "T must be integral" ); + static_assert( !std::is_same::type, bool>::value, "T must not be bool" ); + + static_assert( std::is_unsigned::value, "R must be unsigned" ); + typedef typename std::make_unsigned::type U; + return static_cast( ( static_cast( r ) << 32 ) + r ); } -template - T get_integral_result( std::array const & r ) +template + typename std::enable_if< !std::is_integral::value, T >::type + get_integral_result( R const & r ) { - static_assert( N >= 8, "Array result type is too short" ); - return static_cast( detail::read64le( r.data() ) ); -} + static_assert( std::is_integral::value, "T must be integral" ); + static_assert( !std::is_same::type, bool>::value, "T must not be bool" ); + + static_assert( R().size() >= 8, "Array-like result type is too short" ); -template - T get_integral_result( digest const & r ) -{ - static_assert( N >= 8, "Digest result type is too short" ); return static_cast( detail::read64le( r.data() ) ); } From a7c172b0ac5762a67fa0c780ea59b0e366d99559 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 18:48:22 +0200 Subject: [PATCH 03/12] Generalize the expansion case of get_integral_result --- include/boost/hash2/get_integral_result.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/boost/hash2/get_integral_result.hpp b/include/boost/hash2/get_integral_result.hpp index d56e411..8bf9ac1 100644 --- a/include/boost/hash2/get_integral_result.hpp +++ b/include/boost/hash2/get_integral_result.hpp @@ -7,6 +7,7 @@ #include #include +#include #include namespace boost @@ -29,7 +30,7 @@ template } template - typename std::enable_if::value && sizeof(R) == 4 && sizeof(T) == 8, T>::type + typename std::enable_if::value && (sizeof(R) < sizeof(T)), T>::type get_integral_result( R const & r ) { static_assert( std::is_integral::value, "T must be integral" ); @@ -39,7 +40,9 @@ template typedef typename std::make_unsigned::type U; - return static_cast( ( static_cast( r ) << 32 ) + r ); + constexpr U m = std::numeric_limits::max() / std::numeric_limits::max(); + + return static_cast( r * m ); } template From 275890b7d0550f568a447ac28545417224b26744 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 19:20:29 +0200 Subject: [PATCH 04/12] Change test/get_integral_result.cpp to only test whether invocations compile, and not the exact output values --- test/get_integral_result.cpp | 131 ++++++++++++++--------------------- 1 file changed, 51 insertions(+), 80 deletions(-) diff --git a/test/get_integral_result.cpp b/test/get_integral_result.cpp index c844979..58f2495 100644 --- a/test/get_integral_result.cpp +++ b/test/get_integral_result.cpp @@ -1,94 +1,65 @@ -// Copyright 2017, 2018 Peter Dimov. +// Copyright 2017, 2018, 2024 Peter Dimov // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #include -#include -#include +#include +#include #include -#include + +template void test() +{ + using boost::hash2::get_integral_result; + + R r = {}; + + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); + + get_integral_result( r ); + get_integral_result( r ); + get_integral_result( r ); +} int main() { using boost::hash2::get_integral_result; - { - std::uint32_t u32 = 0x12345678; + test(); + test(); + test(); + test(); + test(); - BOOST_TEST_EQ( get_integral_result( u32 ), 0x78 ); - BOOST_TEST_EQ( get_integral_result( u32 ), 0x78 ); - BOOST_TEST_EQ( get_integral_result( u32 ), 0x5678 ); - BOOST_TEST_EQ( get_integral_result( u32 ), 0x5678 ); - BOOST_TEST_EQ( get_integral_result( u32 ), 0x12345678 ); - BOOST_TEST_EQ( get_integral_result( u32 ), 0x12345678 ); - BOOST_TEST_EQ( get_integral_result( u32 ), 0x1234567812345678ull ); - BOOST_TEST_EQ( get_integral_result( u32 ), 0x1234567812345678ull ); - } + test< std::array >(); + test< std::array >(); + test< std::array >(); + test< std::array >(); + test< std::array >(); + test< std::array >(); + test< std::array >(); - { - std::uint32_t s32 = 0xFFFFFFFFu; + test< boost::array >(); + test< boost::array >(); + test< boost::array >(); + test< boost::array >(); + test< boost::array >(); + test< boost::array >(); + test< boost::array >(); - BOOST_TEST_EQ( get_integral_result( s32 ), 0xFF ); - BOOST_TEST_EQ( get_integral_result( s32 ), -1 ); - BOOST_TEST_EQ( get_integral_result( s32 ), 0xFFFF ); - BOOST_TEST_EQ( get_integral_result( s32 ), -1 ); - BOOST_TEST_EQ( get_integral_result( s32 ), 0xFFFFFFFFu ); - BOOST_TEST_EQ( get_integral_result( s32 ), -1 ); - BOOST_TEST_EQ( get_integral_result( s32 ), 0xFFFFFFFFFFFFFFFFull ); - BOOST_TEST_EQ( get_integral_result( s32 ), -1 ); - } - - { - std::uint64_t u64 = 0x0123456789ABCDEFull; - - BOOST_TEST_EQ( get_integral_result( u64 ), 0xEF ); - BOOST_TEST_EQ( get_integral_result( u64 ), -0x11 ); - BOOST_TEST_EQ( get_integral_result( u64 ), 0xCDEF ); - BOOST_TEST_EQ( get_integral_result( u64 ), -0x3211 ); - BOOST_TEST_EQ( get_integral_result( u64 ), 0x89ABCDEF ); - BOOST_TEST_EQ( get_integral_result( u64 ), 0x89ABCDEF ); - BOOST_TEST_EQ( get_integral_result( u64 ), 0x0123456789ABCDEFull ); - BOOST_TEST_EQ( get_integral_result( u64 ), 0x0123456789ABCDEFull ); - } - - { - std::uint64_t s64 = 0xFFFFFFFFFFFFFFFFull; - - BOOST_TEST_EQ( get_integral_result( s64 ), 0xFF ); - BOOST_TEST_EQ( get_integral_result( s64 ), -1 ); - BOOST_TEST_EQ( get_integral_result( s64 ), 0xFFFF ); - BOOST_TEST_EQ( get_integral_result( s64 ), -1 ); - BOOST_TEST_EQ( get_integral_result( s64 ), 0xFFFFFFFFu ); - BOOST_TEST_EQ( get_integral_result( s64 ), -1 ); - BOOST_TEST_EQ( get_integral_result( s64 ), 0xFFFFFFFFFFFFFFFFull ); - BOOST_TEST_EQ( get_integral_result( s64 ), -1 ); - } - - { - std::array a64 = {{ 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01 }}; - - BOOST_TEST_EQ( get_integral_result( a64 ), 0xEF ); - BOOST_TEST_EQ( get_integral_result( a64 ), -0x11 ); - BOOST_TEST_EQ( get_integral_result( a64 ), 0xCDEF ); - BOOST_TEST_EQ( get_integral_result( a64 ), -0x3211 ); - BOOST_TEST_EQ( get_integral_result( a64 ), 0x89ABCDEF ); - BOOST_TEST_EQ( get_integral_result( a64 ), 0x89ABCDEF ); - BOOST_TEST_EQ( get_integral_result( a64 ), 0x0123456789ABCDEFull ); - BOOST_TEST_EQ( get_integral_result( a64 ), 0x0123456789ABCDEFull ); - } - - { - std::array b64 = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }}; - - BOOST_TEST_EQ( get_integral_result( b64 ), 0xFF ); - BOOST_TEST_EQ( get_integral_result( b64 ), -1 ); - BOOST_TEST_EQ( get_integral_result( b64 ), 0xFFFF ); - BOOST_TEST_EQ( get_integral_result( b64 ), -1 ); - BOOST_TEST_EQ( get_integral_result( b64 ), 0xFFFFFFFFu ); - BOOST_TEST_EQ( get_integral_result( b64 ), -1 ); - BOOST_TEST_EQ( get_integral_result( b64 ), 0xFFFFFFFFFFFFFFFFull ); - BOOST_TEST_EQ( get_integral_result( b64 ), -1 ); - } - - return boost::report_errors(); + test< boost::hash2::digest<8> >(); + test< boost::hash2::digest<16> >(); + test< boost::hash2::digest<20> >(); + test< boost::hash2::digest<28> >(); + test< boost::hash2::digest<32> >(); + test< boost::hash2::digest<48> >(); + test< boost::hash2::digest<64> >(); } From d4a36acb7e605bb0105131777a9d962d77a886f3 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 19:34:01 +0200 Subject: [PATCH 05/12] Add test/get_integral_result_2.cpp --- test/Jamfile | 6 ++- test/get_integral_result_2.cpp | 78 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/get_integral_result_2.cpp diff --git a/test/Jamfile b/test/Jamfile index b7036d1..470c2b7 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -30,9 +30,13 @@ run is_trivially_equality_comparable.cpp ; run is_endian_independent.cpp ; run has_constant_size.cpp ; -# helpers +# get_integral_result run get_integral_result.cpp ; +run get_integral_result_2.cpp ; + +# digest + run digest.cpp ; # detail diff --git a/test/get_integral_result_2.cpp b/test/get_integral_result_2.cpp new file mode 100644 index 0000000..0697d9f --- /dev/null +++ b/test/get_integral_result_2.cpp @@ -0,0 +1,78 @@ +// Copyright 2017, 2018, 2024 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +template void test( R const& r ) +{ + using boost::hash2::get_integral_result; + + { + auto t1 = get_integral_result( r ); + auto t2 = get_integral_result( r ); + + BOOST_TEST_EQ( static_cast( t1 ), t2 ); + } + + { + auto t1 = get_integral_result( r ); + auto t2 = get_integral_result( r ); + + BOOST_TEST_EQ( static_cast( t1 ), t2 ); + } + + { + auto t1 = get_integral_result( r ); + auto t2 = get_integral_result( r ); + + BOOST_TEST_EQ( static_cast( t1 ), t2 ); + } + + { + auto t1 = get_integral_result( r ); + auto t2 = get_integral_result( r ); + + BOOST_TEST_EQ( static_cast( t1 ), t2 ); + } + + { + auto t1 = get_integral_result( r ); + auto t2 = get_integral_result( r ); + + BOOST_TEST_EQ( static_cast( t1 ), t2 ); + } +} + +int main() +{ + using boost::hash2::get_integral_result; + + test( 0x1E ); + test( 0xE1 ); + + test( 0x1E1E ); + test( 0xE1E1 ); + + test( 0x1E1E1E1Eu ); + test( 0xE1E1E1E1u ); + + test( 0x1E1E1E1E1E1E1E1Eull ); + test( 0xE1E1E1E1E1E1E1E1ull ); + + test< std::array >( {{ 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E }} ); + test< std::array >( {{ 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1 }} ); + + test< boost::array >( {{ 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E }} ); + test< boost::array >( {{ 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1 }} ); + + test< boost::hash2::digest<8> >( {{ 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E }} ); + test< boost::hash2::digest<8> >( {{ 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1 }} ); + + return boost::report_errors(); +} From 1a00a202967c9542344cac4836fffe16be875bb5 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 19:39:28 +0200 Subject: [PATCH 06/12] Add test/get_integral_result_3.cpp --- test/Jamfile | 1 + test/get_integral_result_3.cpp | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 test/get_integral_result_3.cpp diff --git a/test/Jamfile b/test/Jamfile index 470c2b7..9be1e18 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -34,6 +34,7 @@ run has_constant_size.cpp ; run get_integral_result.cpp ; run get_integral_result_2.cpp ; +run get_integral_result_3.cpp ; # digest diff --git a/test/get_integral_result_3.cpp b/test/get_integral_result_3.cpp new file mode 100644 index 0000000..0e561eb --- /dev/null +++ b/test/get_integral_result_3.cpp @@ -0,0 +1,50 @@ +// Copyright 2017, 2018, 2024 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +template void test() +{ + using boost::hash2::get_integral_result; + + { + R a64 = {{ 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01 }}; + + BOOST_TEST_EQ( get_integral_result( a64 ), 0xEF ); + BOOST_TEST_EQ( get_integral_result( a64 ), -0x11 ); + BOOST_TEST_EQ( get_integral_result( a64 ), 0xCDEF ); + BOOST_TEST_EQ( get_integral_result( a64 ), -0x3211 ); + BOOST_TEST_EQ( get_integral_result( a64 ), 0x89ABCDEF ); + BOOST_TEST_EQ( get_integral_result( a64 ), 0x89ABCDEF ); + BOOST_TEST_EQ( get_integral_result( a64 ), 0x0123456789ABCDEFull ); + BOOST_TEST_EQ( get_integral_result( a64 ), 0x0123456789ABCDEFull ); + } + + { + R b64 = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }}; + + BOOST_TEST_EQ( get_integral_result( b64 ), 0xFF ); + BOOST_TEST_EQ( get_integral_result( b64 ), -1 ); + BOOST_TEST_EQ( get_integral_result( b64 ), 0xFFFF ); + BOOST_TEST_EQ( get_integral_result( b64 ), -1 ); + BOOST_TEST_EQ( get_integral_result( b64 ), 0xFFFFFFFFu ); + BOOST_TEST_EQ( get_integral_result( b64 ), -1 ); + BOOST_TEST_EQ( get_integral_result( b64 ), 0xFFFFFFFFFFFFFFFFull ); + BOOST_TEST_EQ( get_integral_result( b64 ), -1 ); + } +} + +int main() +{ + test< std::array >(); + test< boost::array >(); + test< boost::hash2::digest<8> >(); + + return boost::report_errors(); +} From 97c52f533c91b5f239cfb6d8003dbe1892f34cb4 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 19:59:02 +0200 Subject: [PATCH 07/12] Add test/get_integral_result_4.cpp --- test/Jamfile | 1 + test/get_integral_result_4.cpp | 86 ++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 test/get_integral_result_4.cpp diff --git a/test/Jamfile b/test/Jamfile index 9be1e18..df6197b 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -35,6 +35,7 @@ run has_constant_size.cpp ; run get_integral_result.cpp ; run get_integral_result_2.cpp ; run get_integral_result_3.cpp ; +run get_integral_result_4.cpp ; # digest diff --git a/test/get_integral_result_4.cpp b/test/get_integral_result_4.cpp new file mode 100644 index 0000000..caa46c8 --- /dev/null +++ b/test/get_integral_result_4.cpp @@ -0,0 +1,86 @@ +// Copyright 2017, 2018, 2024 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include + +template void test_identity() +{ + using boost::hash2::get_integral_result; + + using R = T; + + for( unsigned i = 0; i <= std::numeric_limits::max(); ++i ) + { + R r = static_cast( i ); + T t = get_integral_result( r ); + + BOOST_TEST_EQ( t, r ); + } +} + +template void test_permutation( R offset = 0, R scale = 1 ) +{ + using boost::hash2::get_integral_result; + + std::set dist; + + for( unsigned i = 0; i <= std::numeric_limits::max(); ++i ) + { + R r = static_cast( i * scale + offset ); + T t = get_integral_result( r ); + + dist.insert( t ); + } + + BOOST_TEST_EQ( dist.size(), std::numeric_limits::max() + 1u ); +} + +template void test_roundtrip() +{ + using boost::hash2::get_integral_result; + + std::set dist; + + for( unsigned i = 0; i <= std::numeric_limits::max(); ++i ) + { + T t1 = static_cast( i ); + R r = get_integral_result( t1 ); + T t2 = get_integral_result( r ); + + dist.insert( t2 ); + } + + BOOST_TEST_EQ( dist.size(), std::numeric_limits::max() + 1u ); +} + +int main() +{ + test_identity(); + + test_permutation(); + test_permutation(); + test_permutation(); + test_permutation(); + + test_roundtrip(); + test_roundtrip(); + test_roundtrip(); + test_roundtrip(); + + test_identity(); + + test_permutation(); + test_permutation(); + test_permutation(); + + test_roundtrip(); + test_roundtrip(); + test_roundtrip(); + + return boost::report_errors(); +} From 37dc776e69c9d9b2cfee93fd2f5109706a70efa0 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 20:12:13 +0200 Subject: [PATCH 08/12] Avoid GCC 4.8 warning --- test/get_integral_result.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/get_integral_result.cpp b/test/get_integral_result.cpp index 58f2495..c910b55 100644 --- a/test/get_integral_result.cpp +++ b/test/get_integral_result.cpp @@ -11,7 +11,7 @@ template void test() { using boost::hash2::get_integral_result; - R r = {}; + R r = R(); get_integral_result( r ); get_integral_result( r ); From 7981f63b61b4306b3624e79619b580419472e864 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 16 Nov 2024 20:16:37 +0200 Subject: [PATCH 09/12] Add test/get_integral_result_5.cpp --- test/Jamfile | 1 + test/get_integral_result_5.cpp | 74 ++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 test/get_integral_result_5.cpp diff --git a/test/Jamfile b/test/Jamfile index df6197b..037da97 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -36,6 +36,7 @@ run get_integral_result.cpp ; run get_integral_result_2.cpp ; run get_integral_result_3.cpp ; run get_integral_result_4.cpp ; +run get_integral_result_5.cpp ; # digest diff --git a/test/get_integral_result_5.cpp b/test/get_integral_result_5.cpp new file mode 100644 index 0000000..20e2226 --- /dev/null +++ b/test/get_integral_result_5.cpp @@ -0,0 +1,74 @@ +// Copyright 2017, 2018, 2024 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include + +using T = std::uint32_t; + +void test_identity() +{ + using boost::hash2::get_integral_result; + + using R = T; + + for( unsigned i = 0; i < 65536; ++i ) + { + R r = static_cast( i * 0x9E3779B9u ); + T t = get_integral_result( r ); + + BOOST_TEST_EQ( t, r ); + } +} + +template void test_permutation( R offset = 0, R scale = 1 ) +{ + using boost::hash2::get_integral_result; + + std::set dist; + + for( unsigned i = 0; i < 65536; ++i ) + { + R r = static_cast( i * 0x9E3779B9u * scale + offset ); + T t = get_integral_result( r ); + + dist.insert( t ); + } + + BOOST_TEST_EQ( dist.size(), 65536u ); +} + +template void test_roundtrip() +{ + using boost::hash2::get_integral_result; + + std::set dist; + + for( unsigned i = 0; i < 65536; ++i ) + { + T t1 = static_cast( i * 0x9E3779B9u ); + R r = get_integral_result( t1 ); + T t2 = get_integral_result( r ); + + dist.insert( t2 ); + } + + BOOST_TEST_EQ( dist.size(), 65536u ); +} + +int main() +{ + test_identity(); + + test_permutation(); + test_permutation(); + + test_roundtrip(); + test_roundtrip(); + + return boost::report_errors(); +} From 6699f2c36d62216227cce97ac9a32c24d801b93a Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sun, 17 Nov 2024 21:31:52 +0200 Subject: [PATCH 10/12] Update implementation of get_integral_result --- include/boost/hash2/get_integral_result.hpp | 132 +++++++++++++++++++- test/get_integral_result_4.cpp | 71 ++++++++--- 2 files changed, 179 insertions(+), 24 deletions(-) diff --git a/include/boost/hash2/get_integral_result.hpp b/include/boost/hash2/get_integral_result.hpp index 8bf9ac1..c6742f4 100644 --- a/include/boost/hash2/get_integral_result.hpp +++ b/include/boost/hash2/get_integral_result.hpp @@ -15,8 +15,124 @@ namespace boost namespace hash2 { +namespace detail +{ + +// identity + +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 1; +} + +// contraction + +// 2 -> 1 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0xBF01; +} + +// 4 -> 1 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0x7F7F7F7Fu; +} + +// 8 -> 1 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0x7F7F7F7F7F7F7F7Full; +} + +// 4 -> 2 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0xBFFF0001u; +} + +// 8 -> 2 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0xBFFFBFFFBFFFBFFFull; +} + +// 8 -> 4 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0xBFFFFFFF00000001ull; +} + +// expansion + +// 1 -> 2 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0x7F7F; +} + +// 1 -> 4 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0xBFBFBFBFu; +} + +// 1 -> 8 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0xDFDFDFDFDFDFDFDFull; +} + +// 2 -> 4 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0x7FFF7FFFu; +} + +// 2 -> 8 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0x7FFF7FFF7FFF7FFFull; +} + +// 4 -> 8 +template + constexpr typename std::enable_if::type + get_result_multiplier() +{ + return 0x7FFFFFFF7FFFFFFFull; +} + +} // namespace detail + +// contraction + template - typename std::enable_if::value && (sizeof(R) >= sizeof(T)), T>::type + typename std::enable_if::value && (sizeof(R) > sizeof(T)), T>::type get_integral_result( R const & r ) { static_assert( std::is_integral::value, "T must be integral" ); @@ -26,11 +142,15 @@ template typedef typename std::make_unsigned::type U; - return static_cast( static_cast( r ) ); + constexpr auto m = detail::get_result_multiplier(); + + return static_cast( static_cast( ( r * m ) >> ( std::numeric_limits::digits - std::numeric_limits::digits ) ) ); } +// identity or expansion + template - typename std::enable_if::value && (sizeof(R) < sizeof(T)), T>::type + typename std::enable_if::value && (sizeof(R) <= sizeof(T)), T>::type get_integral_result( R const & r ) { static_assert( std::is_integral::value, "T must be integral" ); @@ -40,11 +160,13 @@ template typedef typename std::make_unsigned::type U; - constexpr U m = std::numeric_limits::max() / std::numeric_limits::max(); + constexpr auto m = detail::get_result_multiplier(); - return static_cast( r * m ); + return static_cast( static_cast( r * m ) ); } +// array-like R + template typename std::enable_if< !std::is_integral::value, T >::type get_integral_result( R const & r ) diff --git a/test/get_integral_result_4.cpp b/test/get_integral_result_4.cpp index caa46c8..b7c4a46 100644 --- a/test/get_integral_result_4.cpp +++ b/test/get_integral_result_4.cpp @@ -23,7 +23,7 @@ template void test_identity() } } -template void test_permutation( R offset = 0, R scale = 1 ) +template std::size_t test_permutation( int shift ) { using boost::hash2::get_integral_result; @@ -31,16 +31,16 @@ template void test_permutation( R offset = 0, R scale = 1 ) for( unsigned i = 0; i <= std::numeric_limits::max(); ++i ) { - R r = static_cast( i * scale + offset ); + R r = static_cast( i << shift ); T t = get_integral_result( r ); dist.insert( t ); } - BOOST_TEST_EQ( dist.size(), std::numeric_limits::max() + 1u ); + return dist.size(); } -template void test_roundtrip() +template std::size_t test_roundtrip() { using boost::hash2::get_integral_result; @@ -55,32 +55,65 @@ template void test_roundtrip() dist.insert( t2 ); } - BOOST_TEST_EQ( dist.size(), std::numeric_limits::max() + 1u ); + return dist.size(); } int main() { + // 1 -> 1 + test_identity(); - test_permutation(); - test_permutation(); - test_permutation(); - test_permutation(); + // 1 -> 2 - test_roundtrip(); - test_roundtrip(); - test_roundtrip(); - test_roundtrip(); + BOOST_TEST_EQ( (test_permutation( 0 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 8 )), 256u ); + + BOOST_TEST_GE( (test_roundtrip()), 255u ); // ! + + // 1 -> 4 + + BOOST_TEST_EQ( (test_permutation( 0 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 8 )), 256u ); + BOOST_TEST_GE( (test_permutation( 16 )), 255u ); // ! + BOOST_TEST_EQ( (test_permutation( 24 )), 256u ); + + BOOST_TEST_EQ( (test_roundtrip()), 256u ); + + // 1 -> 8 + + BOOST_TEST_EQ( (test_permutation( 0 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 8 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 16 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 24 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 32 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 40 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 48 )), 256u ); + BOOST_TEST_EQ( (test_permutation( 56 )), 256u ); + + BOOST_TEST_EQ( (test_roundtrip()), 256u ); + + // 2 -> 2 test_identity(); - test_permutation(); - test_permutation(); - test_permutation(); + // 2 -> 4 - test_roundtrip(); - test_roundtrip(); - test_roundtrip(); + BOOST_TEST_EQ( (test_permutation( 0 )), 65536u ); + BOOST_TEST_EQ( (test_permutation( 16 )), 65536u ); + + BOOST_TEST_GE( (test_roundtrip()), 65535u ); // ! + + // 2 -> 8 + + BOOST_TEST_EQ( (test_permutation( 0 )), 65536u ); + BOOST_TEST_EQ( (test_permutation( 16 )), 65536u ); + BOOST_TEST_EQ( (test_permutation( 32 )), 65536u ); + BOOST_TEST_EQ( (test_permutation( 48 )), 65536u ); + + BOOST_TEST_EQ( (test_roundtrip()), 65536u ); + + // return boost::report_errors(); } From 75c33fd4b374ac08c52640092b65371436ff0c02 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sun, 17 Nov 2024 21:36:37 +0200 Subject: [PATCH 11/12] Update test/append_set.cpp, append_map.cpp; hash_append_unordered_range is sensitive to get_integral_result( h.result() ) --- test/append_map.cpp | 4 ++-- test/append_set.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/append_map.cpp b/test/append_map.cpp index 59e6aca..c4b57cb 100644 --- a/test/append_map.cpp +++ b/test/append_map.cpp @@ -71,10 +71,10 @@ int main() test< fnv1a_32, little_endian_flavor, std::multimap >( 1082144933ul ); test< fnv1a_64, little_endian_flavor, std::multimap >( 12051529320333828229ull ); - test< fnv1a_32, little_endian_flavor, std::unordered_map >( 1094735330ul ); + test< fnv1a_32, little_endian_flavor, std::unordered_map >( 2445282413ul ); test< fnv1a_64, little_endian_flavor, std::unordered_map >( 2617313294186790738ull ); - test< fnv1a_32, little_endian_flavor, std::unordered_multimap >( 1094735330ul ); + test< fnv1a_32, little_endian_flavor, std::unordered_multimap >( 2445282413ul ); test< fnv1a_64, little_endian_flavor, std::unordered_multimap >( 2617313294186790738ull ); return boost::report_errors(); diff --git a/test/append_set.cpp b/test/append_set.cpp index d5296a2..9c1f3a4 100644 --- a/test/append_set.cpp +++ b/test/append_set.cpp @@ -69,10 +69,10 @@ int main() test< fnv1a_32, little_endian_flavor, std::multiset >( 3576652581ul ); test< fnv1a_64, little_endian_flavor, std::multiset >( 17046016161958689285ull ); - test< fnv1a_32, little_endian_flavor, std::unordered_set >( 3782055292ul ); + test< fnv1a_32, little_endian_flavor, std::unordered_set >( 776982489ul ); test< fnv1a_64, little_endian_flavor, std::unordered_set >( 3232503781718511241ull ); - test< fnv1a_32, little_endian_flavor, std::unordered_multiset >( 3782055292ul ); + test< fnv1a_32, little_endian_flavor, std::unordered_multiset >( 776982489ul ); test< fnv1a_64, little_endian_flavor, std::unordered_multiset >( 3232503781718511241ull ); return boost::report_errors(); From 393e6c967ce5cf94c59cfdb12e5ae0de11625892 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Mon, 18 Nov 2024 19:43:58 +0200 Subject: [PATCH 12/12] Update hash_append_fwd.adoc --- doc/hash2/reference/hash_append_fwd.adoc | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/doc/hash2/reference/hash_append_fwd.adoc b/doc/hash2/reference/hash_append_fwd.adoc index 8b7c5c7..45849b8 100644 --- a/doc/hash2/reference/hash_append_fwd.adoc +++ b/doc/hash2/reference/hash_append_fwd.adoc @@ -35,3 +35,55 @@ struct hash_append_tag; } // namespace boost ``` +The header `boost/hash2/hash_append_fwd.hpp` declares the functions implemented in `boost/hash2/hash_append.hpp`. + +It can be used when code wishes to implement `hash_append` support for a user-defined type without physically depending on the implementation of `hash_append`. + +## Example + +.X.hpp +[source] +---- +#include +#include + +class X +{ +private: + + int a = -1; + std::vector b{ 1, 2, 3 }; + + template + friend void tag_invoke( + boost::hash2::hash_append_tag const&, Hash& h, Flavor const& f, X const& v ) + { + boost::hash2::hash_append( h, f, v.a ); + boost::hash2::hash_append( h, f, v.b ); + } + +public: + + X() = default; +}; +---- + +.main.cpp +[source] +---- +#include "X.hpp" +#include +#include +#include + +int main() +{ + X x; + + boost::hash2::md5_128 hash; + boost::hash2::hash_append( hash, {}, x ); + + std::cout << "MD5 digest of x: " << hash.result() << std::endl; +} +---- +