From 0a000167b7ae6c93ce30d870a7ff584975ee34b6 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Wed, 28 May 2025 11:05:10 +0200 Subject: [PATCH] Feature/hash_is_avalanching (#40) * added hash_is_avalanching * launched CI after enabling GHA * moved 1.89 entry from Change Log to Recent Changes * segregated some tests into hash_is_avalanching_test3.cpp and gotten rid of Unordered dependency * removed unneeded include * stopped using external std::hash for testing * typo * removed left over include * typo * moved hash_is_avalanching from boost::container_hash to boost * fixed specializations of boost::hash_is_avalanching --- doc/hash/recent.adoc | 4 ++ doc/hash/reference.adoc | 54 +++++++++++++++++- include/boost/container_hash/hash.hpp | 13 ++--- include/boost/container_hash/hash_fwd.hpp | 4 +- .../container_hash/hash_is_avalanching.hpp | 57 +++++++++++++++++++ test/CMakeLists.txt | 4 +- test/Jamfile.v2 | 9 ++- test/hash_is_avalanching_test.cpp | 6 +- test/hash_is_avalanching_test2.cpp | 6 +- test/hash_is_avalanching_test3.cpp | 38 +++++++++++++ 10 files changed, 172 insertions(+), 23 deletions(-) create mode 100644 include/boost/container_hash/hash_is_avalanching.hpp create mode 100644 test/hash_is_avalanching_test3.cpp diff --git a/doc/hash/recent.adoc b/doc/hash/recent.adoc index ef3a2e3..7c18492 100644 --- a/doc/hash/recent.adoc +++ b/doc/hash/recent.adoc @@ -8,6 +8,10 @@ https://www.boost.org/LICENSE_1_0.txt = Recent Changes :idprefix: recent_ +== Boost 1.89.0 + +* Added the `hash_is_avalanching` trait class. + == Boost 1.84.0 * {cpp}03 is no longer supported. diff --git a/doc/hash/reference.adoc b/doc/hash/reference.adoc index a8d2889..b5afe0d 100644 --- a/doc/hash/reference.adoc +++ b/doc/hash/reference.adoc @@ -1,7 +1,7 @@ //// Copyright 2005-2008 Daniel James Copyright 2022 Christian Mazakas -Copyright 2022 Peter Dimov +Copyright 2022, 2025 Peter Dimov Distributed under the Boost Software License, Version 1.0. https://www.boost.org/LICENSE_1_0.txt //// @@ -44,6 +44,8 @@ template std::size_t hash_range( It first, It last ); template void hash_unordered_range( std::size_t& seed, It first, It last ); template std::size_t hash_unordered_range( It first, It last ); +template struct hash_is_avalanching; + } // namespace boost ---- @@ -572,6 +574,56 @@ where `x` is the currently contained value in `v`. Throws: :: `std::bad_variant_access` when `v.valueless_by_exception()` is `true`. +== + +Defines the trait `boost::hash_is_avalanching`. + +[source] +---- +namespace boost +{ + +template struct hash_is_avalanching; + +} // namespace boost +---- + +=== hash_is_avalanching + +[source] +---- +template struct hash_is_avalanching +{ + static constexpr bool value = /* see below */; +}; +---- + +`hash_is_avalanching::value` is: + +* `false` if `Hash::is_avalanching` is not present, +* `Hash::is_avalanching::value` if this is present and convertible at compile time to a `bool`, +* `true` if `Hash::is_avalanching` is `void` (this usage is deprecated), +* ill-formed otherwise. + +A hash function is said to have the _avalanching property_ if small changes +in the input translate to large changes in the returned hash code +—ideally, flipping one bit in the representation of the input value results +in each bit of the hash code flipping with probability 50%. Libraries +such as link:../../../unordered/index.html[Boost.Unordered] consult this trait +to determine if the supplied hash function is of high quality. +`boost::hash` for `std::basic_string` and `std::basic_string_view` +has this trait set to `true` when `Ch` is an integral type (this includes +`std::string` and `std::string_view`, among others). +Users can set this trait for a particular `Hash` type by: + +* Inserting the nested `is_avalanching` typedef in the class definition +if they have access to its source code. +* Writing a specialization of `boost::hash_is_avalanching` +for `Hash`. + +Note that usage of this trait is not restricted to hash functions produced +with Boost.ContainerHash. + == Defines the trait `boost::container_hash::is_range`. diff --git a/include/boost/container_hash/hash.hpp b/include/boost/container_hash/hash.hpp index 8305a22..1fe3f2f 100644 --- a/include/boost/container_hash/hash.hpp +++ b/include/boost/container_hash/hash.hpp @@ -1,5 +1,5 @@ // Copyright 2005-2014 Daniel James. -// Copyright 2021, 2022 Peter Dimov. +// Copyright 2021, 2022, 2025 Peter Dimov. // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -11,6 +11,7 @@ #define BOOST_FUNCTIONAL_HASH_HASH_HPP #include +#include #include #include #include @@ -557,19 +558,15 @@ namespace boost #endif - // boost::unordered::hash_is_avalanching + // hash_is_avalanching - namespace unordered - { - template struct hash_is_avalanching; - template struct hash_is_avalanching< boost::hash< std::basic_string > >: std::is_integral {}; + template struct hash_is_avalanching< boost::hash< std::basic_string > >: std::is_integral {}; #if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) - template struct hash_is_avalanching< boost::hash< std::basic_string_view > >: std::is_integral {}; + template struct hash_is_avalanching< boost::hash< std::basic_string_view > >: std::is_integral {}; #endif - } // namespace unordered } // namespace boost diff --git a/include/boost/container_hash/hash_fwd.hpp b/include/boost/container_hash/hash_fwd.hpp index 32388ac..1f2a37a 100644 --- a/include/boost/container_hash/hash_fwd.hpp +++ b/include/boost/container_hash/hash_fwd.hpp @@ -1,5 +1,5 @@ // Copyright 2005-2009 Daniel James. -// Copyright 2021, 2022 Peter Dimov. +// Copyright 2021, 2022, 2025 Peter Dimov. // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -32,6 +32,8 @@ template std::size_t hash_range( It, It ); template void hash_unordered_range( std::size_t&, It, It ); template std::size_t hash_unordered_range( It, It ); +template struct hash_is_avalanching; + } // namespace boost #endif // #ifndef BOOST_FUNCTIONAL_HASH_FWD_HPP diff --git a/include/boost/container_hash/hash_is_avalanching.hpp b/include/boost/container_hash/hash_is_avalanching.hpp new file mode 100644 index 0000000..65c7059 --- /dev/null +++ b/include/boost/container_hash/hash_is_avalanching.hpp @@ -0,0 +1,57 @@ +// Copyright 2025 Joaquin M Lopez Munoz. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_HASH_HASH_IS_AVALANCHING_HPP_INCLUDED +#define BOOST_HASH_HASH_IS_AVALANCHING_HPP_INCLUDED + +#include + +namespace boost +{ +namespace hash_detail +{ + +template struct make_void +{ + using type = void; +}; + +template using void_t = typename make_void::type; + +template struct avalanching_value +{ + static constexpr bool value = IsAvalanching::value; +}; + +// may be explicitly marked as BOOST_DEPRECATED in the future +template<> struct avalanching_value +{ + static constexpr bool value = true; +}; + +template struct hash_is_avalanching_impl: std::false_type +{ +}; + +template struct hash_is_avalanching_impl >: + std::integral_constant::value> +{ +}; + +template +struct hash_is_avalanching_impl::type> +{ + // Hash::is_avalanching is not a type: we don't define value to produce + // a compile error downstream +}; + +} // namespace hash_detail + +template struct hash_is_avalanching: hash_detail::hash_is_avalanching_impl::type +{ +}; + +} // namespace boost + +#endif // #ifndef BOOST_HASH_HASH_IS_AVALANCHING_HPP_INCLUDED diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 30b3ec7..8f2b978 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2018, 2019, 2021, 2022 Peter Dimov +# Copyright 2018, 2019, 2021, 2022, 2025 Peter Dimov # Distributed under the Boost Software License, Version 1.0. # See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt @@ -7,6 +7,6 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST) if(HAVE_BOOST_TEST) boost_test_jamfile(FILE Jamfile.v2 - LINK_LIBRARIES Boost::container_hash Boost::core Boost::utility Boost::unordered) + LINK_LIBRARIES Boost::container_hash Boost::core Boost::utility) endif() diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index c415e9d..596c478 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1,5 +1,5 @@ # Copyright 2005-2012 Daniel James. -# Copyright 2022 Peter Dimov +# Copyright 2022, 2025 Peter Dimov # Distributed under the Boost Software License, Version 1.0. # https://www.boost.org/LICENSE_1_0.txt @@ -119,10 +119,9 @@ run is_described_class_test3.cpp run described_class_test.cpp : : : extra ; -run hash_is_avalanching_test.cpp - /boost/unordered//boost_unordered ; -run hash_is_avalanching_test2.cpp - /boost/unordered//boost_unordered ; +run hash_is_avalanching_test.cpp ; +run hash_is_avalanching_test2.cpp ; +run hash_is_avalanching_test3.cpp ; run hash_integral_test2.cpp ; diff --git a/test/hash_is_avalanching_test.cpp b/test/hash_is_avalanching_test.cpp index c31c56b..d3024ac 100644 --- a/test/hash_is_avalanching_test.cpp +++ b/test/hash_is_avalanching_test.cpp @@ -1,10 +1,10 @@ -// Copyright 2022 Peter Dimov. +// Copyright 2022, 2025 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 @@ -12,7 +12,7 @@ enum my_char { min = 0, max = 255 }; int main() { - using boost::unordered::hash_is_avalanching; + using boost::hash_is_avalanching; BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash > )); BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash > )); diff --git a/test/hash_is_avalanching_test2.cpp b/test/hash_is_avalanching_test2.cpp index 8d47834..66230d0 100644 --- a/test/hash_is_avalanching_test2.cpp +++ b/test/hash_is_avalanching_test2.cpp @@ -1,8 +1,9 @@ -// Copyright 2022 Peter Dimov. +// Copyright 2022, 2025 Peter Dimov. // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #include +#include #include #include #include @@ -14,14 +15,13 @@ int main() {} #else -#include #include enum my_char { min = 0, max = 255 }; int main() { - using boost::unordered::hash_is_avalanching; + using boost::hash_is_avalanching; BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash > )); BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< boost::hash > )); diff --git a/test/hash_is_avalanching_test3.cpp b/test/hash_is_avalanching_test3.cpp new file mode 100644 index 0000000..f0dda80 --- /dev/null +++ b/test/hash_is_avalanching_test3.cpp @@ -0,0 +1,38 @@ +// Copyright 2025 Joaquin M Lopez Munoz. +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +struct X +{ + using is_avalanching = void; +}; + +struct Y +{ + using is_avalanching = std::true_type; +}; + +struct Z +{ + using is_avalanching = std::false_type; +}; + +struct W +{ +}; + +int main() +{ + using boost::hash_is_avalanching; + + BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< X > )); + BOOST_TEST_TRAIT_TRUE(( hash_is_avalanching< Y > )); + BOOST_TEST_TRAIT_FALSE(( hash_is_avalanching< Z > )); + BOOST_TEST_TRAIT_FALSE(( hash_is_avalanching< W > )); + + return boost::report_errors(); +}