Skip to content

Commit

Permalink
Add has_constant_size<T>; use it in hash_append
Browse files Browse the repository at this point in the history
  • Loading branch information
pdimov committed Nov 4, 2024
1 parent 889ef41 commit c459a35
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 15 deletions.
6 changes: 6 additions & 0 deletions include/boost/hash2/digest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ template<std::size_t N> 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
Expand Down
63 changes: 63 additions & 0 deletions include/boost/hash2/has_constant_size.hpp
Original file line number Diff line number Diff line change
@@ -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 <type_traits>
#include <utility>

namespace boost
{

// forward declaration
template<class T, std::size_t N> class array;

namespace hash2
{

// forward declaration
template<std::size_t N> class digest;

// detail::has_tuple_size

namespace detail
{

template<class T, class En = void> struct has_tuple_size: std::false_type
{
};

template<class T> struct has_tuple_size<T,
typename std::enable_if<
std::tuple_size<T>::value == std::tuple_size<T>::value
>::type
>: std::true_type
{
};

} // namespace detail

// has_constant_size

template<class T> struct has_constant_size: detail::has_tuple_size<T>
{
};

template<class T, std::size_t N> struct has_constant_size< boost::array<T, N> >: std::true_type
{
};

template<std::size_t N> struct has_constant_size< digest<N> >: std::true_type
{
};

template<class T> struct has_constant_size<T const>: has_constant_size<T>
{
};

} // namespace hash2
} // namespace boost

#endif // #ifndef BOOST_HASH2_HAS_CONSTANT_SIZE_HPP_INCLUDED
34 changes: 19 additions & 15 deletions include/boost/hash2/hash_append.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#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>
Expand Down Expand Up @@ -223,7 +224,7 @@ template<class Hash, class Flavor, class T, std::size_t N> BOOST_CXX14_CONSTEXPR

template<class Hash, class Flavor, class T>
BOOST_CXX14_CONSTEXPR
typename std::enable_if< container_hash::is_contiguous_range<T>::value && !container_hash::is_tuple_like<T>::value, void >::type
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() );
Expand All @@ -234,17 +235,22 @@ template<class Hash, class Flavor, class T>

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 && !container_hash::is_contiguous_range<T>::value && !container_hash::is_unordered_range<T>::value, void >::type
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_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<class Hash, class Flavor, class T>
BOOST_CXX14_CONSTEXPR
typename std::enable_if< container_hash::is_contiguous_range<T>::value && container_hash::is_tuple_like<T>::value, void >::type
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 )
Expand All @@ -254,28 +260,26 @@ template<class Hash, class Flavor, class T>
}
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<class Hash, class Flavor, class T, std::size_t N> BOOST_CXX14_CONSTEXPR void do_hash_append( Hash& h, Flavor const& f, boost::array<T, N> const& v )
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.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() );
}
}

Expand Down
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions test/digest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
}

{
Expand All @@ -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 );
}
}

Expand Down
66 changes: 66 additions & 0 deletions test/has_constant_size.cpp
Original file line number Diff line number Diff line change
@@ -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 <boost/hash2/has_constant_size.hpp>
#include <boost/hash2/digest.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/array.hpp>
#include <array>
#include <string>
#include <vector>
#include <set>

template<class T> void test( bool exp )
{
using boost::hash2::has_constant_size;

if( exp )
{
BOOST_TEST_TRAIT_TRUE((has_constant_size<T>));
BOOST_TEST_TRAIT_TRUE((has_constant_size<T const>));
}
else
{
BOOST_TEST_TRAIT_FALSE((has_constant_size<T>));
BOOST_TEST_TRAIT_FALSE((has_constant_size<T const>));
}
}

struct X
{
};

int main()
{
test< void >( false );
test< X >( false );

test< std::vector<int> >( false );
test< std::vector<X> >( false );
test< std::string >( false );
test< std::set<int> >( false );

test< std::array<int, 3> >( true );
test< std::array<int, 0> >( true );

test< std::array<X, 3> >( true );
test< std::array<X, 0> >( true );

test< boost::array<int, 5> >( true );
test< boost::array<int, 0> >( true );

test< boost::array<X, 7> >( true );
test< boost::array<X, 0> >( true );

test< std::array<int, 9> >( true );
test< std::array<int, 0> >( true );

using boost::hash2::digest;

test< digest<1> >( true );
test< digest<16> >( true );
test< digest<20> >( true );

return boost::report_errors();
}

0 comments on commit c459a35

Please sign in to comment.