From 6075f3e1f52a097c264f1c995c8d61385d555e4d Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 2 Feb 2023 06:00:00 +0200 Subject: [PATCH] Change 64 bit hash_range for char[] to mulxp1_hash --- .../container_hash/detail/hash_range.hpp | 198 +++++++++++++++++- include/boost/container_hash/detail/mulx.hpp | 79 +++++++ 2 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 include/boost/container_hash/detail/mulx.hpp diff --git a/include/boost/container_hash/detail/hash_range.hpp b/include/boost/container_hash/detail/hash_range.hpp index 5ddf358..d7b789b 100644 --- a/include/boost/container_hash/detail/hash_range.hpp +++ b/include/boost/container_hash/detail/hash_range.hpp @@ -6,11 +6,13 @@ #define BOOST_HASH_DETAIL_HASH_RANGE_HPP #include +#include #include #include #include #include #include +#include #include #include #include @@ -54,7 +56,7 @@ std::size_t >::type return seed; } -// specialized char[] version +// specialized char[] version, 32 bit template inline boost::uint32_t read32le( It p ) { @@ -90,7 +92,8 @@ inline boost::uint64_t mul32( boost::uint32_t x, boost::uint32_t y ) template inline typename boost::enable_if_< is_char_type::value_type>::value && - is_same::iterator_category, std::random_access_iterator_tag>::value, + is_same::iterator_category, std::random_access_iterator_tag>::value && + std::numeric_limits::digits <= 32, std::size_t>::type hash_range( std::size_t seed, It first, It last ) { @@ -143,7 +146,8 @@ std::size_t>::type template inline typename boost::enable_if_< is_char_type::value_type>::value && - !is_same::iterator_category, std::random_access_iterator_tag>::value, + !is_same::iterator_category, std::random_access_iterator_tag>::value && + std::numeric_limits::digits <= 32, std::size_t>::type hash_range( std::size_t seed, It first, It last ) { @@ -212,6 +216,194 @@ std::size_t>::type return static_cast( h ) ^ static_cast( h >> 32 ); } +// specialized char[] version, 64 bit + +template inline boost::uint64_t read64le( It p ) +{ + boost::uint64_t w = + static_cast( static_cast( p[0] ) ) | + static_cast( static_cast( p[1] ) ) << 8 | + static_cast( static_cast( p[2] ) ) << 16 | + static_cast( static_cast( p[3] ) ) << 24 | + static_cast( static_cast( p[4] ) ) << 32 | + static_cast( static_cast( p[5] ) ) << 40 | + static_cast( static_cast( p[6] ) ) << 48 | + static_cast( static_cast( p[7] ) ) << 56; + + return w; +} + +#if defined(_MSC_VER) && !defined(__clang__) + +template inline boost::uint64_t read64le( T* p ) +{ + boost::uint64_t w; + + std::memcpy( &w, p, 8 ); + return w; +} + +#endif + +template +inline typename boost::enable_if_< + is_char_type::value_type>::value && + is_same::iterator_category, std::random_access_iterator_tag>::value && + (std::numeric_limits::digits > 32), +std::size_t>::type + hash_range( std::size_t seed, It first, It last ) +{ + It p = first; + std::size_t n = static_cast( last - first ); + + boost::uint64_t const q = 0x9e3779b97f4a7c15ULL; + boost::uint64_t const k = q * q; + + boost::uint64_t w = mulx( seed + q, k ); + boost::uint64_t h = w ^ n; + + while( n >= 8 ) + { + boost::uint64_t v1 = read64le( p ); + + w += q; + h ^= mulx( v1 + w, k ); + + p += 8; + n -= 8; + } + + { + boost::uint64_t v1 = 0; + + if( n >= 4 ) + { + v1 = static_cast( read32le( p + n - 4 ) ) << ( n - 4 ) * 8 | read32le( p ); + } + else if( n >= 1 ) + { + std::size_t const x1 = ( n - 1 ) & 2; // 1: 0, 2: 0, 3: 2 + std::size_t const x2 = n >> 1; // 1: 0, 2: 1, 3: 1 + + v1 = + static_cast( static_cast( p[ x1 ] ) ) << x1 * 8 | + static_cast( static_cast( p[ x2 ] ) ) << x2 * 8 | + static_cast( static_cast( p[ 0 ] ) ); + } + + w += q; + h ^= mulx( v1 + w, k ); + } + + return mulx( h + w, k ); +} + +template +inline typename boost::enable_if_< + is_char_type::value_type>::value && + !is_same::iterator_category, std::random_access_iterator_tag>::value && + (std::numeric_limits::digits > 32), +std::size_t>::type + hash_range( std::size_t seed, It first, It last ) +{ + std::size_t n = 0; + + boost::uint64_t const q = 0x9e3779b97f4a7c15ULL; + boost::uint64_t const k = q * q; + + boost::uint64_t w = mulx( seed + q, k ); + boost::uint64_t h = w; + + boost::uint64_t v1 = 0; + + for( ;; ) + { + v1 = 0; + + if( first == last ) + { + break; + } + + v1 |= static_cast( static_cast( *first ) ); + ++first; + ++n; + + if( first == last ) + { + break; + } + + v1 |= static_cast( static_cast( *first ) ) << 8; + ++first; + ++n; + + if( first == last ) + { + break; + } + + v1 |= static_cast( static_cast( *first ) ) << 16; + ++first; + ++n; + + if( first == last ) + { + break; + } + + v1 |= static_cast( static_cast( *first ) ) << 24; + ++first; + ++n; + + if( first == last ) + { + break; + } + + v1 |= static_cast( static_cast( *first ) ) << 32; + ++first; + ++n; + + if( first == last ) + { + break; + } + + v1 |= static_cast( static_cast( *first ) ) << 40; + ++first; + ++n; + + if( first == last ) + { + break; + } + + v1 |= static_cast( static_cast( *first ) ) << 48; + ++first; + ++n; + + if( first == last ) + { + break; + } + + v1 |= static_cast( static_cast( *first ) ) << 56; + ++first; + ++n; + + w += q; + h ^= mulx( v1 + w, k ); + } + + h ^= n; + + w += q; + h ^= mulx( v1 + w, k ); + + return mulx( h + w, k ); +} + } // namespace hash_detail } // namespace boost diff --git a/include/boost/container_hash/detail/mulx.hpp b/include/boost/container_hash/detail/mulx.hpp new file mode 100644 index 0000000..da6f21a --- /dev/null +++ b/include/boost/container_hash/detail/mulx.hpp @@ -0,0 +1,79 @@ +// Copyright 2022, 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_HASH_DETAIL_MULX_HPP +#define BOOST_HASH_DETAIL_MULX_HPP + +#include +#if defined(_MSC_VER) +# include +#endif + +namespace boost +{ +namespace hash_detail +{ + +#if defined(_MSC_VER) && defined(_M_X64) && !defined(__clang__) + +__forceinline boost::uint64_t mulx( boost::uint64_t x, boost::uint64_t y ) +{ + boost::uint64_t r2; + boost::uint64_t r = _umul128( x, y, &r2 ); + return r ^ r2; +} + +#elif defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) + +__forceinline boost::uint64_t mulx( boost::uint64_t x, boost::uint64_t y ) +{ + boost::uint64_t r = x * y; + boost::uint64_t r2 = __umulh( x, y ); + return r ^ r2; +} + +#elif defined(__SIZEOF_INT128__) + +inline boost::uint64_t mulx( boost::uint64_t x, boost::uint64_t y ) +{ + __uint128_t r = static_cast<__uint128_t>( x ) * y; + return static_cast( r ) ^ static_cast( r >> 64 ); +} + +#else + +inline boost::uint64_t mulx( boost::uint64_t x, boost::uint64_t y ) +{ + boost::uint64_t x1 = static_cast( x ); + boost::uint64_t x2 = x >> 32; + + boost::uint64_t y1 = static_cast( y ); + boost::uint64_t y2 = y >> 32; + + boost::uint64_t r3 = x2 * y2; + + boost::uint64_t r2a = x1 * y2; + + r3 += r2a >> 32; + + boost::uint64_t r2b = x2 * y1; + + r3 += r2b >> 32; + + boost::uint64_t r1 = x1 * y1; + + boost::uint64_t r2 = (r1 >> 32) + static_cast( r2a ) + static_cast( r2b ); + + r1 = (r2 << 32) + static_cast( r1 ); + r3 += r2 >> 32; + + return r1 ^ r3; +} + +#endif + +} // namespace hash_detail +} // namespace boost + +#endif // #ifndef BOOST_HASH_DETAIL_MULX_HPP