From c459a35dff3ee1b4597e36a8b0f541f11f81176c Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Mon, 4 Nov 2024 17:28:42 +0200 Subject: [PATCH] Add has_constant_size; use it in hash_append --- include/boost/hash2/digest.hpp | 6 +++ include/boost/hash2/has_constant_size.hpp | 63 ++++++++++++++++++++++ include/boost/hash2/hash_append.hpp | 34 ++++++------ test/Jamfile | 1 + test/digest.cpp | 6 +++ test/has_constant_size.cpp | 66 +++++++++++++++++++++++ 6 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 include/boost/hash2/has_constant_size.hpp create mode 100644 test/has_constant_size.cpp diff --git a/include/boost/hash2/digest.hpp b/include/boost/hash2/digest.hpp index da993f2..93e94c1 100644 --- a/include/boost/hash2/digest.hpp +++ b/include/boost/hash2/digest.hpp @@ -63,6 +63,12 @@ template class digest BOOST_CXX14_CONSTEXPR reference operator[]( std::size_t i ) { BOOST_ASSERT( i < N ); return data_[ i ]; } BOOST_CXX14_CONSTEXPR const_reference operator[]( std::size_t i ) const { BOOST_ASSERT( i < N ); return data_[ i ]; } + + BOOST_CXX14_CONSTEXPR reference front() noexcept { return data_[ 0 ]; } + constexpr const_reference front() const noexcept { return data_[ 0 ]; } + + BOOST_CXX14_CONSTEXPR reference back() noexcept { return data_[ N-1 ]; } + constexpr const_reference back() const noexcept { return data_[ N-1 ]; } }; // comparisons diff --git a/include/boost/hash2/has_constant_size.hpp b/include/boost/hash2/has_constant_size.hpp new file mode 100644 index 0000000..74c8987 --- /dev/null +++ b/include/boost/hash2/has_constant_size.hpp @@ -0,0 +1,63 @@ +#ifndef BOOST_HASH2_HAS_CONSTANT_SIZE_HPP_INCLUDED +#define BOOST_HASH2_HAS_CONSTANT_SIZE_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 + +namespace boost +{ + +// forward declaration +template class array; + +namespace hash2 +{ + +// forward declaration +template class digest; + +// detail::has_tuple_size + +namespace detail +{ + +template struct has_tuple_size: std::false_type +{ +}; + +template struct has_tuple_size::value == std::tuple_size::value + >::type +>: std::true_type +{ +}; + +} // namespace detail + +// has_constant_size + +template struct has_constant_size: detail::has_tuple_size +{ +}; + +template struct has_constant_size< boost::array >: std::true_type +{ +}; + +template struct has_constant_size< digest >: std::true_type +{ +}; + +template struct has_constant_size: has_constant_size +{ +}; + +} // namespace hash2 +} // namespace boost + +#endif // #ifndef BOOST_HASH2_HAS_CONSTANT_SIZE_HPP_INCLUDED diff --git a/include/boost/hash2/hash_append.hpp b/include/boost/hash2/hash_append.hpp index 9a76227..8af3ede 100644 --- a/include/boost/hash2/hash_append.hpp +++ b/include/boost/hash2/hash_append.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -223,7 +224,7 @@ template BOOST_CXX14_CONSTEXPR template BOOST_CXX14_CONSTEXPR - typename std::enable_if< container_hash::is_contiguous_range::value && !container_hash::is_tuple_like::value, void >::type + typename std::enable_if< container_hash::is_contiguous_range::value && !has_constant_size::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() ); @@ -234,17 +235,22 @@ template template BOOST_CXX14_CONSTEXPR - typename std::enable_if< container_hash::is_range::value && !container_hash::is_tuple_like::value && !container_hash::is_contiguous_range::value && !container_hash::is_unordered_range::value, void >::type + typename std::enable_if< container_hash::is_range::value && !has_constant_size::value && !container_hash::is_contiguous_range::value && !container_hash::is_unordered_range::value, void >::type do_hash_append( Hash& h, Flavor const& f, T const& v ) { hash2::hash_append_sized_range( h, f, v.begin(), v.end() ); } -// std::array (both contiguous range and tuple-like) +#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 BOOST_CXX14_CONSTEXPR - typename std::enable_if< container_hash::is_contiguous_range::value && container_hash::is_tuple_like::value, void >::type + typename std::enable_if< container_hash::is_contiguous_range::value && has_constant_size::value, void >::type do_hash_append( Hash& h, Flavor const& f, T const& v ) { if( v.size() == 0 ) @@ -254,28 +260,26 @@ template } else { - // std::array<>::data() is only constexpr in C++17 - hash2::hash_append_range( h, f, &v[ 0 ], &v[ 0 ] + v.size() ); + // 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() ); } } -// boost::array (constant size, but not tuple-like) - -#if defined(BOOST_MSVC) -# pragma warning(push) -# pragma warning(disable: 4702) // unreachable code -#endif +// constant size non-contiguous containers and ranges -template BOOST_CXX14_CONSTEXPR void do_hash_append( Hash& h, Flavor const& f, boost::array const& v ) +template + BOOST_CXX14_CONSTEXPR + typename std::enable_if< container_hash::is_range::value && has_constant_size::value && !container_hash::is_contiguous_range::value, void >::type + do_hash_append( Hash& h, Flavor const& f, T const& v ) { - if( v.size() == 0 ) + 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.front(), &v.front() + v.size() ); + hash2::hash_append_range( h, f, v.begin(), v.end() ); } } diff --git a/test/Jamfile b/test/Jamfile index 887f2b6..ca1d527 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -28,6 +28,7 @@ run flavor.cpp ; run is_contiguously_hashable.cpp ; run is_trivially_equality_comparable.cpp ; run is_endian_independent.cpp ; +run has_constant_size.cpp ; # helpers diff --git a/test/digest.cpp b/test/digest.cpp index aab2eea..4bbef96 100644 --- a/test/digest.cpp +++ b/test/digest.cpp @@ -160,6 +160,9 @@ static void test_element_access() { BOOST_TEST_EQ( &d[ i ], d.data() + i ); } + + BOOST_TEST_EQ( &d.front(), d.data() ); + BOOST_TEST_EQ( &d.back(), d.data() + d.size() - 1 ); } { @@ -169,6 +172,9 @@ static void test_element_access() { BOOST_TEST_EQ( &d[ i ], d.data() + i ); } + + BOOST_TEST_EQ( &d.front(), d.data() ); + BOOST_TEST_EQ( &d.back(), d.data() + d.size() - 1 ); } } diff --git a/test/has_constant_size.cpp b/test/has_constant_size.cpp new file mode 100644 index 0000000..18562be --- /dev/null +++ b/test/has_constant_size.cpp @@ -0,0 +1,66 @@ +// Copyright 2017, 2023, 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 +#include + +template void test( bool exp ) +{ + using boost::hash2::has_constant_size; + + if( exp ) + { + BOOST_TEST_TRAIT_TRUE((has_constant_size)); + BOOST_TEST_TRAIT_TRUE((has_constant_size)); + } + else + { + BOOST_TEST_TRAIT_FALSE((has_constant_size)); + BOOST_TEST_TRAIT_FALSE((has_constant_size)); + } +} + +struct X +{ +}; + +int main() +{ + test< void >( false ); + test< X >( false ); + + test< std::vector >( false ); + test< std::vector >( false ); + test< std::string >( false ); + test< std::set >( false ); + + test< std::array >( true ); + test< std::array >( true ); + + test< std::array >( true ); + test< std::array >( true ); + + test< boost::array >( true ); + test< boost::array >( true ); + + test< boost::array >( true ); + test< boost::array >( true ); + + test< std::array >( true ); + test< std::array >( true ); + + using boost::hash2::digest; + + test< digest<1> >( true ); + test< digest<16> >( true ); + test< digest<20> >( true ); + + return boost::report_errors(); +}