| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- #ifndef BOOST_HASH2_HASH_APPEND_HPP_INCLUDED
- #define BOOST_HASH2_HASH_APPEND_HPP_INCLUDED
- // Copyright 2017, 2018, 2023, 2024 Peter Dimov.
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- //
- // Based on
- //
- // Types Don't Know #
- // Howard E. Hinnant, Vinnie Falco, John Bytheway
- // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3980.html
- #include <boost/hash2/hash_append_fwd.hpp>
- #include <boost/hash2/is_contiguously_hashable.hpp>
- #include <boost/hash2/has_constant_size.hpp>
- #include <boost/hash2/get_integral_result.hpp>
- #include <boost/hash2/flavor.hpp>
- #include <boost/hash2/detail/is_constant_evaluated.hpp>
- #include <boost/hash2/detail/bit_cast.hpp>
- #include <boost/hash2/detail/write.hpp>
- #include <boost/hash2/detail/has_tag_invoke.hpp>
- #include <boost/container_hash/is_range.hpp>
- #include <boost/container_hash/is_contiguous_range.hpp>
- #include <boost/container_hash/is_unordered_range.hpp>
- #include <boost/container_hash/is_tuple_like.hpp>
- #include <boost/container_hash/is_described_class.hpp>
- #include <boost/describe/bases.hpp>
- #include <boost/describe/members.hpp>
- #include <boost/mp11/algorithm.hpp>
- #include <boost/mp11/integer_sequence.hpp>
- #include <cstdint>
- #include <type_traits>
- #include <iterator>
- namespace boost
- {
- template<class T, std::size_t N> class array;
- namespace hash2
- {
- // hash_append_range
- namespace detail
- {
- template<class Hash, class Flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_range_( Hash& h, Flavor const& f, It first, It last )
- {
- for( ; first != last; ++first )
- {
- typename std::iterator_traits<It>::value_type const& v = *first;
- hash2::hash_append( h, f, v );
- }
- }
- template<class Hash, class Flavor> BOOST_CXX14_CONSTEXPR void hash_append_range_( Hash& h, Flavor const& /*f*/, unsigned char* first, unsigned char* last )
- {
- h.update( first, last - first );
- }
- template<class Hash, class Flavor> BOOST_CXX14_CONSTEXPR void hash_append_range_( Hash& h, Flavor const& /*f*/, unsigned char const* first, unsigned char const* last )
- {
- h.update( first, last - first );
- }
- #if defined(BOOST_NO_CXX14_CONSTEXPR)
- template<class Hash, class Flavor, class T>
- typename std::enable_if<
- is_contiguously_hashable<T, Flavor::byte_order>::value, void >::type
- hash_append_range_( Hash& h, Flavor const& /*f*/, T* first, T* last )
- {
- h.update( first, (last - first) * sizeof(T) );
- }
- #else
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if<
- is_contiguously_hashable<T, Flavor::byte_order>::value, void >::type
- hash_append_range_( Hash& h, Flavor const& f, T* first, T* last )
- {
- if( !detail::is_constant_evaluated() )
- {
- h.update( first, (last - first) * sizeof(T) );
- }
- else
- {
- for( ; first != last; ++first )
- {
- hash2::hash_append( h, f, *first );
- }
- }
- }
- #endif
- } // namespace detail
- template<class Hash, class Flavor = default_flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_range( Hash& h, Flavor const& f, It first, It last )
- {
- detail::hash_append_range_( h, f, first, last );
- }
- // hash_append_size
- template<class Hash, class Flavor = default_flavor, class T> BOOST_CXX14_CONSTEXPR void hash_append_size( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append( h, f, static_cast<typename Flavor::size_type>( v ) );
- }
- // hash_append_range_and_size
- namespace detail
- {
- template<class Hash, class Flavor, class It> void BOOST_CXX14_CONSTEXPR hash_append_range_and_size_( Hash& h, Flavor const& f, It first, It last, std::input_iterator_tag )
- {
- typename std::iterator_traits<It>::difference_type m = 0;
- for( ; first != last; ++first, ++m )
- {
- hash2::hash_append( h, f, *first );
- }
- hash2::hash_append_size( h, f, m );
- }
- template<class Hash, class Flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_range_and_size_( Hash& h, Flavor const& f, It first, It last, std::random_access_iterator_tag )
- {
- hash2::hash_append_range( h, f, first, last );
- hash2::hash_append_size( h, f, last - first );
- }
- } // namespace detail
- template<class Hash, class Flavor = default_flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_range_and_size( Hash& h, Flavor const& f, It first, It last )
- {
- detail::hash_append_range_and_size_( h, f, first, last, typename std::iterator_traits<It>::iterator_category() );
- }
- // hash_append_unordered_range
- template<class Hash, class Flavor = default_flavor, class It> BOOST_CXX14_CONSTEXPR void hash_append_unordered_range( Hash& h, Flavor const& f, It first, It last )
- {
- typename std::iterator_traits<It>::difference_type m = 0;
- std::uint64_t w = 0;
- for( ; first != last; ++first, ++m )
- {
- Hash h2( h );
- hash2::hash_append( h2, f, *first );
- w += hash2::get_integral_result<std::uint64_t>( h2 );
- }
- hash2::hash_append( h, f, w );
- hash2::hash_append_size( h, f, m );
- }
- // do_hash_append
- namespace detail
- {
- // integral types
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< std::is_integral<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& /*f*/, T const& v )
- {
- constexpr auto N = sizeof(T);
- unsigned char tmp[ N ] = {};
- detail::write( v, Flavor::byte_order, tmp );
- h.update( tmp, N );
- }
- // enum types
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< std::is_enum<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append( h, f, static_cast<typename std::underlying_type<T>::type>( v ) );
- }
- // pointer types
- // never constexpr
- template<class Hash, class Flavor, class T>
- typename std::enable_if< std::is_pointer<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append( h, f, reinterpret_cast<std::uintptr_t>( v ) );
- }
- // floating point
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< std::is_floating_point<T>::value && sizeof(T) == 4, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append( h, f, detail::bit_cast<std::uint32_t>( v + 0 ) );
- }
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< std::is_floating_point<T>::value && sizeof(T) == 8, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append( h, f, detail::bit_cast<std::uint64_t>( v + 0 ) );
- }
- // std::nullptr_t
- // not constexpr for consistency with T*
- template<class Hash, class Flavor, class T>
- typename std::enable_if< std::is_same<T, std::nullptr_t>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append( h, f, static_cast<void*>( v ) );
- }
- // C arrays
- template<class Hash, class Flavor, class T, std::size_t N> BOOST_CXX14_CONSTEXPR void do_hash_append( Hash& h, Flavor const& f, T const (&v)[ N ] )
- {
- hash2::hash_append_range( h, f, v + 0, v + N );
- }
- // contiguous containers and ranges, w/ size
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< container_hash::is_contiguous_range<T>::value && !has_constant_size<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append_range( h, f, v.data(), v.data() + v.size() );
- hash2::hash_append_size( h, f, v.size() );
- }
- // containers and ranges, w/ size
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< container_hash::is_range<T>::value && !has_constant_size<T>::value && !container_hash::is_contiguous_range<T>::value && !container_hash::is_unordered_range<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append_range_and_size( h, f, v.begin(), v.end() );
- }
- #if defined(BOOST_MSVC)
- # pragma warning(push)
- # pragma warning(disable: 4702) // unreachable code
- #endif
- // constant size contiguous containers and ranges (std::array, boost::array, hash2::digest)
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< container_hash::is_contiguous_range<T>::value && has_constant_size<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- if( v.size() == 0 )
- {
- // A hash_append call must always result in a call to Hash::update
- hash2::hash_append( h, f, '\x00' );
- }
- else
- {
- // std::array<>::data() is only constexpr in C++17; boost::array<>::operator[] isn't constexpr
- hash2::hash_append_range( h, f, &v.front(), &v.front() + v.size() );
- }
- }
- // constant size non-contiguous containers and ranges
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< container_hash::is_range<T>::value && has_constant_size<T>::value && !container_hash::is_contiguous_range<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- if( v.begin() == v.end() )
- {
- // A hash_append call must always result in a call to Hash::update
- hash2::hash_append( h, f, '\x00' );
- }
- else
- {
- hash2::hash_append_range( h, f, v.begin(), v.end() );
- }
- }
- #if defined(BOOST_MSVC)
- # pragma warning(pop)
- #endif
- // unordered containers (is_unordered_range implies is_range)
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< container_hash::is_unordered_range<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append_unordered_range( h, f, v.begin(), v.end() );
- }
- // tuple-likes
- template<class Hash, class Flavor, class T, std::size_t... J> BOOST_CXX14_CONSTEXPR void hash_append_tuple( Hash& h, Flavor const& f, T const& v, mp11::integer_sequence<std::size_t, J...> )
- {
- using std::get;
- int a[] = { ((void)hash2::hash_append( h, f, get<J>(v) ), 0)... };
- (void)a;
- }
- template<class Hash, class Flavor, class T> BOOST_CXX14_CONSTEXPR void hash_append_tuple( Hash& h, Flavor const& f, T const& /*v*/, mp11::integer_sequence<std::size_t> )
- {
- // A hash_append call must always result in a call to Hash::update
- hash2::hash_append( h, f, '\x00' );
- }
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< !container_hash::is_range<T>::value && container_hash::is_tuple_like<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- using Seq = mp11::make_index_sequence<std::tuple_size<T>::value>;
- detail::hash_append_tuple( h, f, v, Seq() );
- }
- // described classes
- #if defined(BOOST_DESCRIBE_CXX14)
- #if defined(_MSC_VER) && _MSC_VER == 1900
- # pragma warning(push)
- # pragma warning(disable: 4100) // unreferenced formal parameter
- #endif
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< container_hash::is_described_class<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- static_assert( !std::is_union<T>::value, "Described unions are not supported" );
- std::size_t r = 0;
- using Bd = describe::describe_bases<T, describe::mod_any_access>;
- mp11::mp_for_each<Bd>([&](auto D){
- using B = typename decltype(D)::type;
- hash2::hash_append( h, f, (B const&)v );
- ++r;
- });
- using Md = describe::describe_members<T, describe::mod_any_access>;
- mp11::mp_for_each<Md>([&](auto D){
- hash2::hash_append( h, f, v.*D.pointer );
- ++r;
- });
- // A hash_append call must always result in a call to Hash::update
- if( r == 0 )
- {
- hash2::hash_append( h, f, '\x00' );
- }
- }
- #if defined(_MSC_VER) && _MSC_VER == 1900
- # pragma warning(pop)
- #endif
- #endif // defined(BOOST_DESCRIBE_CXX14)
- // classes with tag_invoke
- } // namespace detail
- struct hash_append_tag
- {
- };
- struct hash_append_provider
- {
- template<class Hash, class Flavor, class T>
- static BOOST_CXX14_CONSTEXPR void hash_append( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append( h, f, v );
- }
- template<class Hash, class Flavor, class It>
- static BOOST_CXX14_CONSTEXPR void hash_append_range( Hash& h, Flavor const& f, It first, It last )
- {
- hash2::hash_append_range( h, f, first, last );
- }
- template<class Hash, class Flavor, class T>
- static BOOST_CXX14_CONSTEXPR void hash_append_size( Hash& h, Flavor const& f, T const& v )
- {
- hash2::hash_append_size( h, f, v );
- }
- template<class Hash, class Flavor, class It>
- static BOOST_CXX14_CONSTEXPR void hash_append_range_and_size( Hash& h, Flavor const& f, It first, It last )
- {
- hash2::hash_append_range_and_size( h, f, first, last );
- }
- template<class Hash, class Flavor, class It>
- static BOOST_CXX14_CONSTEXPR void hash_append_unordered_range( Hash& h, Flavor const& f, It first, It last )
- {
- hash2::hash_append_unordered_range( h, f, first, last );
- }
- };
- namespace detail
- {
- template<class Hash, class Flavor, class T>
- BOOST_CXX14_CONSTEXPR
- typename std::enable_if< detail::has_tag_invoke<T>::value, void >::type
- do_hash_append( Hash& h, Flavor const& f, T const& v )
- {
- tag_invoke( hash_append_tag(), hash_append_provider(), h, f, &v );
- }
- } // namespace detail
- // hash_append
- template<class Hash, class Flavor = default_flavor, class T>
- BOOST_CXX14_CONSTEXPR void hash_append( Hash& h, Flavor const& f, T const& v )
- {
- if( !detail::is_constant_evaluated() && is_contiguously_hashable<T, Flavor::byte_order>::value )
- {
- h.update( &v, sizeof(T) );
- }
- else
- {
- detail::do_hash_append( h, f, v );
- }
- }
- } // namespace hash2
- } // namespace boost
- #endif // #ifndef BOOST_HASH2_HASH_APPEND_HPP_INCLUDED
|