@ -14,12 +14,14 @@
# include "absl/hash/hash.h"
# include <algorithm>
# include <array>
# include <bitset>
# include <cstring>
# include <deque>
# include <forward_list>
# include <functional>
# include <initializer_list>
# include <iterator>
# include <limits>
# include <list>
@ -46,6 +48,56 @@
namespace {
// Utility wrapper of T for the purposes of testing the `AbslHash` type erasure
// mechanism. `TypeErasedValue<T>` can be constructed with a `T`, and can
// be compared and hashed. However, all hashing goes through the hashing
// type-erasure framework.
template < typename T >
class TypeErasedValue {
public :
TypeErasedValue ( ) = default ;
TypeErasedValue ( const TypeErasedValue & ) = default ;
TypeErasedValue ( TypeErasedValue & & ) = default ;
explicit TypeErasedValue ( const T & n ) : n_ ( n ) { }
template < typename H >
friend H AbslHashValue ( H hash_state , const TypeErasedValue & v ) {
v . HashValue ( absl : : HashState : : Create ( & hash_state ) ) ;
return hash_state ;
}
void HashValue ( absl : : HashState state ) const {
absl : : HashState : : combine ( std : : move ( state ) , n_ ) ;
}
bool operator = = ( const TypeErasedValue & rhs ) const { return n_ = = rhs . n_ ; }
bool operator ! = ( const TypeErasedValue & rhs ) const { return ! ( * this = = rhs ) ; }
private :
T n_ ;
} ;
// A TypeErasedValue refinement, for containers. It exposes the wrapped
// `value_type` and is constructible from an initializer list.
template < typename T >
class TypeErasedContainer : public TypeErasedValue < T > {
public :
using value_type = typename T : : value_type ;
TypeErasedContainer ( ) = default ;
TypeErasedContainer ( const TypeErasedContainer & ) = default ;
TypeErasedContainer ( TypeErasedContainer & & ) = default ;
explicit TypeErasedContainer ( const T & n ) : TypeErasedValue < T > ( n ) { }
TypeErasedContainer ( std : : initializer_list < value_type > init_list )
: TypeErasedContainer ( T ( init_list . begin ( ) , init_list . end ( ) ) ) { }
// one-argument constructor of value type T, to appease older toolchains that
// get confused by one-element initializer lists in some contexts
explicit TypeErasedContainer ( const value_type & v )
: TypeErasedContainer ( T ( & v , & v + 1 ) ) { }
} ;
template < typename T >
using TypeErasedVector = TypeErasedContainer < std : : vector < T > > ;
using absl : : Hash ;
using absl : : hash_internal : : SpyHashState ;
@ -389,23 +441,60 @@ TYPED_TEST_SUITE_P(HashValueSequenceTest);
TYPED_TEST_P ( HashValueSequenceTest , BasicUsage ) {
EXPECT_TRUE ( ( is_hashable < TypeParam > : : value ) ) ;
using ValueType = typename TypeParam : : value_type ;
auto a = static_cast < ValueType > ( 0 ) ;
auto b = static_cast < ValueType > ( 23 ) ;
auto c = static_cast < ValueType > ( 42 ) ;
EXPECT_TRUE ( absl : : VerifyTypeImplementsAbslHashCorrectly (
std : : make_tuple ( TypeParam ( ) , TypeParam { } , TypeParam { a , b , c } ,
TypeParam { a , b } , TypeParam { b , c } ) ) ) ;
using IntType = typename TypeParam : : value_type ;
auto a = static_cast < IntType > ( 0 ) ;
auto b = static_cast < IntType > ( 23 ) ;
auto c = static_cast < IntType > ( 42 ) ;
std : : vector < TypeParam > exemplars = {
TypeParam ( ) , TypeParam ( ) , TypeParam { a , b , c } ,
TypeParam { a , c , b } , TypeParam { c , a , b } , TypeParam { a } ,
TypeParam { a , a } , TypeParam { a , a , a } , TypeParam { a , a , b } ,
TypeParam { a , b , a } , TypeParam { b , a , a } , TypeParam { a , b } ,
TypeParam { b , c } } ;
EXPECT_TRUE ( absl : : VerifyTypeImplementsAbslHashCorrectly ( exemplars ) ) ;
}
REGISTER_TYPED_TEST_CASE_P ( HashValueSequenceTest , BasicUsage ) ;
using IntSequenceTypes =
testing : : Types < std : : deque < int > , std : : forward_list < int > , std : : list < int > ,
std : : vector < int > , std : : vector < bool > , std : : set < int > ,
std : : multiset < int > > ;
std : : vector < int > , std : : vector < bool > , TypeErasedVector < int > ,
TypeErasedVector < bool > , std : : set < int > , std : : multiset < int > > ;
INSTANTIATE_TYPED_TEST_CASE_P ( My , HashValueSequenceTest , IntSequenceTypes ) ;
template < typename T >
class HashValueNestedSequenceTest : public testing : : Test { } ;
TYPED_TEST_SUITE_P ( HashValueNestedSequenceTest ) ;
TYPED_TEST_P ( HashValueNestedSequenceTest , BasicUsage ) {
using T = TypeParam ;
using V = typename T : : value_type ;
std : : vector < T > exemplars = {
// empty case
T { } ,
// sets of empty sets
T { V { } } , T { V { } , V { } } , T { V { } , V { } , V { } } ,
// multisets of different values
T { V { 1 } } , T { V { 1 , 1 } , V { 1 , 1 } } , T { V { 1 , 1 , 1 } , V { 1 , 1 , 1 } , V { 1 , 1 , 1 } } ,
// various orderings of same nested sets
T { V { } , V { 1 , 2 } } , T { V { } , V { 2 , 1 } } , T { V { 1 , 2 } , V { } } , T { V { 2 , 1 } , V { } } ,
// various orderings of various nested sets, case 2
T { V { 1 , 2 } , V { 3 , 4 } } , T { V { 1 , 2 } , V { 4 , 3 } } , T { V { 1 , 3 } , V { 2 , 4 } } ,
T { V { 1 , 3 } , V { 4 , 2 } } , T { V { 1 , 4 } , V { 2 , 3 } } , T { V { 1 , 4 } , V { 3 , 2 } } ,
T { V { 2 , 3 } , V { 1 , 4 } } , T { V { 2 , 3 } , V { 4 , 1 } } , T { V { 2 , 4 } , V { 1 , 3 } } ,
T { V { 2 , 4 } , V { 3 , 1 } } , T { V { 3 , 4 } , V { 1 , 2 } } , T { V { 3 , 4 } , V { 2 , 1 } } } ;
EXPECT_TRUE ( absl : : VerifyTypeImplementsAbslHashCorrectly ( exemplars ) ) ;
}
REGISTER_TYPED_TEST_CASE_P ( HashValueNestedSequenceTest , BasicUsage ) ;
using NestedIntSequenceTypes =
testing : : Types < std : : vector < std : : vector < int > > ,
std : : vector < TypeErasedVector < int > > ,
TypeErasedVector < std : : vector < int > > ,
TypeErasedVector < TypeErasedVector < int > > > ;
INSTANTIATE_TYPED_TEST_CASE_P ( My , HashValueNestedSequenceTest ,
NestedIntSequenceTypes ) ;
// Private type that only supports AbslHashValue to make sure our chosen hash
// implementation is recursive within absl::Hash.
// It uses std::abs() on the value to provide different bitwise representations
@ -564,23 +653,59 @@ TEST(HashValueTest, Variant) {
# endif
}
TEST ( HashValueTest , Maps ) {
EXPECT_TRUE ( ( is_hashable < std : : map < int , std : : string > > : : value ) ) ;
template < typename T >
class HashValueAssociativeMapTest : public testing : : Test { } ;
TYPED_TEST_SUITE_P ( HashValueAssociativeMapTest ) ;
TYPED_TEST_P ( HashValueAssociativeMapTest , BasicUsage ) {
using M = TypeParam ;
using V = typename M : : value_type ;
std : : vector < M > exemplars { M { } ,
M { V { 0 , " foo " } } ,
M { V { 1 , " foo " } } ,
M { V { 0 , " bar " } } ,
M { V { 1 , " bar " } } ,
M { V { 0 , " foo " } , V { 42 , " bar " } } ,
M { V { 42 , " bar " } , V { 0 , " foo " } } ,
M { V { 1 , " foo " } , V { 42 , " bar " } } ,
M { V { 1 , " foo " } , V { 43 , " bar " } } ,
M { V { 1 , " foo " } , V { 43 , " baz " } } } ;
EXPECT_TRUE ( absl : : VerifyTypeImplementsAbslHashCorrectly ( exemplars ) ) ;
}
using M = std : : map < int , std : : string > ;
EXPECT_TRUE ( absl : : VerifyTypeImplementsAbslHashCorrectly ( std : : make_tuple (
M { } , M { { 0 , " foo " } } , M { { 1 , " foo " } } , M { { 0 , " bar " } } , M { { 1 , " bar " } } ,
M { { 0 , " foo " } , { 42 , " bar " } } , M { { 1 , " foo " } , { 42 , " bar " } } ,
M { { 1 , " foo " } , { 43 , " bar " } } , M { { 1 , " foo " } , { 43 , " baz " } } ) ) ) ;
REGISTER_TYPED_TEST_CASE_P ( HashValueAssociativeMapTest , BasicUsage ) ;
using AssociativeMapTypes = testing : : Types < std : : map < int , std : : string > > ;
INSTANTIATE_TYPED_TEST_CASE_P ( My , HashValueAssociativeMapTest ,
AssociativeMapTypes ) ;
using MM = std : : multimap < int , std : : string > ;
EXPECT_TRUE ( absl : : VerifyTypeImplementsAbslHashCorrectly ( std : : make_tuple (
MM { } , MM { { 0 , " foo " } } , MM { { 1 , " foo " } } , MM { { 0 , " bar " } } , MM { { 1 , " bar " } } ,
MM { { 0 , " foo " } , { 0 , " bar " } } , MM { { 0 , " bar " } , { 0 , " foo " } } ,
MM { { 0 , " foo " } , { 42 , " bar " } } , MM { { 1 , " foo " } , { 42 , " bar " } } ,
MM { { 1 , " foo " } , { 1 , " foo " } , { 43 , " bar " } } , MM { { 1 , " foo " } , { 43 , " baz " } } ) ) ) ;
template < typename T >
class HashValueAssociativeMultimapTest : public testing : : Test { } ;
TYPED_TEST_SUITE_P ( HashValueAssociativeMultimapTest ) ;
TYPED_TEST_P ( HashValueAssociativeMultimapTest , BasicUsage ) {
using MM = TypeParam ;
using V = typename MM : : value_type ;
std : : vector < MM > exemplars { MM { } ,
MM { V { 0 , " foo " } } ,
MM { V { 1 , " foo " } } ,
MM { V { 0 , " bar " } } ,
MM { V { 1 , " bar " } } ,
MM { V { 0 , " foo " } , V { 0 , " bar " } } ,
MM { V { 0 , " bar " } , V { 0 , " foo " } } ,
MM { V { 0 , " foo " } , V { 42 , " bar " } } ,
MM { V { 1 , " foo " } , V { 42 , " bar " } } ,
MM { V { 1 , " foo " } , V { 1 , " foo " } , V { 43 , " bar " } } ,
MM { V { 1 , " foo " } , V { 43 , " bar " } , V { 1 , " foo " } } ,
MM { V { 1 , " foo " } , V { 43 , " baz " } } } ;
EXPECT_TRUE ( absl : : VerifyTypeImplementsAbslHashCorrectly ( exemplars ) ) ;
}
REGISTER_TYPED_TEST_CASE_P ( HashValueAssociativeMultimapTest , BasicUsage ) ;
using AssociativeMultimapTypes =
testing : : Types < std : : multimap < int , std : : string > > ;
INSTANTIATE_TYPED_TEST_CASE_P ( My , HashValueAssociativeMultimapTest ,
AssociativeMultimapTypes ) ;
TEST ( HashValueTest , ReferenceWrapper ) {
EXPECT_TRUE ( is_hashable < std : : reference_wrapper < Private > > : : value ) ;
@ -928,28 +1053,14 @@ TEST(HashTest, SmallValueOn64ByteBoundary) {
Hash < IntAndString > ( ) ( IntAndString { 0 , std : : string ( 63 , ' 0 ' ) } ) ;
}
struct TypeErased {
size_t n ;
template < typename H >
friend H AbslHashValue ( H hash_state , const TypeErased & v ) {
v . HashValue ( absl : : HashState : : Create ( & hash_state ) ) ;
return hash_state ;
}
void HashValue ( absl : : HashState state ) const {
absl : : HashState : : combine ( std : : move ( state ) , n ) ;
}
} ;
TEST ( HashTest , TypeErased ) {
EXPECT_TRUE ( ( is_hashable < TypeErased > : : value ) ) ;
EXPECT_TRUE ( ( is_hashable < std : : pair < TypeErased , int > > : : value ) ) ;
EXPECT_TRUE ( ( is_hashable < TypeErasedValue < size_t > > : : value ) ) ;
EXPECT_TRUE ( ( is_hashable < std : : pair < TypeErasedValue < size_t > , int > > : : value ) ) ;
EXPECT_EQ ( SpyHash ( TypeErased { 7 } ) , SpyHash ( size_t { 7 } ) ) ;
EXPECT_NE ( SpyHash ( TypeErased { 7 } ) , SpyHash ( size_t { 13 } ) ) ;
EXPECT_EQ ( SpyHash ( TypeErasedValue < size_t > ( 7 ) ) , SpyHash ( size_t { 7 } ) ) ;
EXPECT_NE ( SpyHash ( TypeErasedValue < size_t > ( 7 ) ) , SpyHash ( size_t { 13 } ) ) ;
EXPECT_EQ ( SpyHash ( std : : make_pair ( TypeErased { 7 } , 17 ) ) ,
EXPECT_EQ ( SpyHash ( std : : make_pair ( TypeErasedValue < size_t > ( 7 ) , 17 ) ) ,
SpyHash ( std : : make_pair ( size_t { 7 } , 17 ) ) ) ;
}