The splitter stores a const reference to bit-string, not a copy.
The maximum count of bits that can be cut in a single call is <tt> sizeof(UInt) * 8 </tt>
+
+ Template parameters:
+ - \p BitString - a fixed-sized type that interprets as bit string
+ - \p BitStringSize - the siZe of \p BitString in bytes, default is <tt>sizeof( BitString )</tt>
+ - \p UInt - an unsigned integer, return type of \p cut()
*/
- template <typename BitString, typename UInt = typename std::conditional< sizeof(BitString) % sizeof(size_t) == 0, size_t, unsigned >::type >
+ template <typename BitString, size_t BitStringSize = sizeof( BitString ), typename UInt = typename std::conditional< BitStringSize % sizeof(size_t) == 0, size_t, unsigned >::type >
class split_bitstring
{
public:
typedef BitString bitstring; ///< Bit-string type
typedef UInt uint_type; ///< Bit-string portion type
+ static CDS_CONSTEXPR size_t const c_bitstring_size = BitStringSize; ///< size of \p BitString in bytes
//@cond
- static CDS_CONSTEXPR size_t const c_nHashSize = (sizeof(bitstring) + sizeof(uint_type) - 1) / sizeof(uint_type);
+ static CDS_CONSTEXPR size_t const c_nHashSize = (c_bitstring_size + sizeof(uint_type) - 1) / sizeof(uint_type);
static CDS_CONSTEXPR size_t const c_nBitPerByte = 8;
- static CDS_CONSTEXPR size_t const c_nBitPerHash = sizeof(bitstring) * c_nBitPerByte;
- static CDS_CONSTEXPR size_t const c_nBitPerInt = sizeof(uint_type) * c_nBitPerByte;
+ static CDS_CONSTEXPR size_t const c_nBitPerHash = c_bitstring_size * c_nBitPerByte;
+ static CDS_CONSTEXPR size_t const c_nBitPerInt = sizeof( uint_type ) * c_nBitPerByte;
//@endcond
public:
template <typename Accessor>
using hash_accessor = cds::intrusive::feldman_hashset::hash_accessor< Accessor >;
+ /// Hash size option
+ /**
+ @copydetails cds::intrusive::feldman_hashset::traits::hash_size
+ */
+ template <size_t Size>
+ using hash_size = cds::intrusive::feldman_hashset::hash_size< Size >;
+
/// \p FeldmanHashSet internal statistics, see cds::intrusive::feldman_hashset::stat
template <typename EventCounter = cds::atomicity::event_counter>
using stat = cds::intrusive::feldman_hashset::stat< EventCounter >;
*/
typedef cds::opt::none hash_accessor;
+ /// The size of hash value in bytes
+ /**
+ @copydetails cds::intrusive::feldman_hashset::traits::hash_size
+ */
+ enum : size_t {
+ hash_size = 0
+ };
+
/// Hash comparing functor
/**
@copydetails cds::intrusive::feldman_hashset::traits::compare
Supported \p Options are:
- \p feldman_hashset::hash_accessor - mandatory option, hash accessor functor.
@copydetails traits::hash_accessor
+ - \p feldman_hashset::hash_size - the size of hash value in bytes.
+ @copydetails traits::hash_size
- \p opt::allocator - item allocator
@copydetails traits::allocator
- \p opt::node_allocator - array node allocator.
static CDS_CONSTEXPR const bool c_bExtractLockExternal = false; ///< Group of \p extract_xxx functions does not require external locking
typedef typename base_class::exempt_ptr exempt_ptr; ///< pointer to extracted node
+ /// The size of hash_type in bytes, see \p feldman_hashset::traits::hash_size for explanation
+ static CDS_CONSTEXPR size_t const c_hash_size = base_class::c_hash_size;
+
/// Level statistics
typedef feldman_hashset::level_statistics level_statistics;
/// Count of hazard pointers required
static CDS_CONSTEXPR size_t const c_nHazardPtrCount = base_class::c_nHazardPtrCount;
+ /// The size of hash_type in bytes, see \p feldman_hashset::traits::hash_size for explanation
+ static CDS_CONSTEXPR size_t const c_hash_size = base_class::c_hash_size;
+
/// Level statistics
typedef feldman_hashset::level_statistics level_statistics;
//@endcond
};
+ // Hash size option
+ /**
+ @copydetails traits::hash_size
+ */
+ template <size_t Size>
+ struct hash_size {
+ //@cond
+ template <typename Base> struct pack: public Base
+ {
+ enum: size_t {
+ hash_size = Size
+ };
+ };
+ //@endcond
+ };
+
/// \p FeldmanHashSet internal statistics
template <typename EventCounter = cds::atomicity::event_counter>
struct stat {
*/
typedef cds::opt::none hash_accessor;
+ /// The size of hash value in bytes
+ /**
+ By default, the size of hash value is <tt>sizeof( hash_type )</tt>.
+ Sometimes it is not correct, for example, for that 6-byte struct \p static_assert will be thrown:
+ \code
+ struct key_type {
+ uint32_t key1;
+ uint16_t subkey;
+ };
+
+ static_assert( sizeof( key_type ) == 6, "Key type size mismatch" );
+ \endcode
+ For that case you can specify \p hash_size explicitly.
+
+ Value \p 0 means <tt>sizeof( hash_type )</tt>.
+ */
+ enum : size_t {
+ hash_size = 0
+ };
+
/// Disposer for removing data nodes
typedef cds::intrusive::opt::v::empty_disposer disposer;
/// Array node allocator
/**
- Allocator for array nodes. That allocator is used for creating \p headNode and \p arrayNode when the set grows.
+ Allocator for array nodes. The allocator is used for creating \p headNode and \p arrayNode when the set grows.
Default is \ref CDS_DEFAULT_ALLOCATOR
*/
typedef CDS_DEFAULT_ALLOCATOR node_allocator;
Supported \p Options are:
- \p feldman_hashset::hash_accessor - mandatory option, hash accessor functor.
@copydetails traits::hash_accessor
+ - \p feldman_hashset::hash_size - the size of hash value in bytes.
+ @copydetails traits::hash_size
- \p opt::node_allocator - array node allocator.
@copydetails traits::node_allocator
- \p opt::compare - hash comparison functor. No default functor is provided.
//@cond
namespace details {
- template <typename HashType >
- using hash_splitter = cds::algo::split_bitstring< HashType >;
+ template <typename HashType, size_t HashSize >
+ using hash_splitter = cds::algo::split_bitstring< HashType, HashSize >;
struct metrics {
size_t head_node_size; // power-of-two
feldman_hashset::bitwise_compare< hash_type >
>::type hash_comparator;
- typedef feldman_hashset::details::hash_splitter< hash_type > hash_splitter;
+ /// The size of hash_type in bytes, see \p traits::hash_size for explanation
+ static CDS_CONSTEXPR size_t const c_hash_size = traits::hash_size == 0 ? sizeof( hash_type ) : traits::hash_size;
+
+ typedef feldman_hashset::details::hash_splitter< hash_type, c_hash_size > hash_splitter;
enum node_flags {
flag_array_converting = 1, ///< the cell is converting from data node to an array node
public:
multilevel_array(size_t head_bits, size_t array_bits )
- : m_Metrics(feldman_hashset::details::metrics::make(head_bits, array_bits, sizeof(hash_type)))
+ : m_Metrics(feldman_hashset::details::metrics::make( head_bits, array_bits, c_hash_size ))
, m_Head( alloc_head_node())
{}
using exempt_ptr = cds::urcu::exempt_ptr< gc, value_type, value_type, disposer, void >; ///< pointer to extracted node
+ /// The size of hash_type in bytes, see \p feldman_hashset::traits::hash_size for explanation
+ static CDS_CONSTEXPR size_t const c_hash_size = base_class::c_hash_size;
+
//@cond
typedef feldman_hashset::level_statistics level_statistics;
//@endcond
/// Count of hazard pointers required
static CDS_CONSTEXPR size_t const c_nHazardPtrCount = 2;
+ /// The size of hash_type in bytes, see \p feldman_hashset::traits::hash_size for explanation
+ static CDS_CONSTEXPR size_t const c_hash_size = base_class::c_hash_size;
+
/// Level statistics
typedef feldman_hashset::level_statistics level_statistics;
test( s );
}
+ TEST_F( IntrusiveFeldmanHashSet_DHP, explicit_hash_size )
+ {
+ struct traits: public ci::feldman_hashset::traits
+ {
+ typedef base_class::hash_accessor2 hash_accessor;
+ enum: size_t {
+ hash_size = sizeof( std::declval<key_val>().nKey )
+ };
+ typedef base_class::cmp2 compare;
+ typedef mock_disposer disposer;
+ typedef ci::feldman_hashset::stat<> stat;
+ };
+
+ typedef ci::FeldmanHashSet< gc_type, int_item2, traits > set_type;
+
+ set_type s( 8, 3 );
+ test( s );
+ }
+
} // namespace
test( s );
}
+ TEST_F( IntrusiveFeldmanHashSet_HP, explicit_hash_size )
+ {
+ struct traits: public ci::feldman_hashset::traits
+ {
+ typedef base_class::hash_accessor2 hash_accessor;
+ enum: size_t {
+ hash_size = sizeof( std::declval<key_val>().nKey )
+ };
+ typedef base_class::cmp2 compare;
+ typedef mock_disposer disposer;
+ typedef ci::feldman_hashset::stat<> stat;
+ };
+
+ typedef ci::FeldmanHashSet< gc_type, int_item2, traits > set_type;
+
+ set_type s( 8, 3 );
+ test( s );
+ }
+
} // namespace
public:
struct stat
{
- unsigned int nDisposeCount ; // count of disposer calling
- unsigned int nFindCount ; // count of find-functor calling
+ unsigned int nDisposeCount; // count of disposer calling
+ unsigned int nFindCount; // count of find-functor calling
unsigned int nInsertCount;
mutable unsigned int nEraseCount;
void clear_stat()
{
- memset( this, 0, sizeof( *this ));
+ memset( this, 0, sizeof( *this ) );
}
};
, nVal( key )
{}
- int_item(int key, int val)
+ int_item( int key, int val )
: nKey( key )
- , nVal(val)
+ , nVal( val )
{}
int_item( int_item const& v )
}
};
+ struct key_val {
+ int nKey;
+ int nVal;
+
+ key_val()
+ {}
+
+ key_val( int key )
+ : nKey( key )
+ , nVal( key )
+ {}
+
+ key_val( int key, int val )
+ : nKey( key )
+ , nVal( val )
+ {}
+
+ key_val( key_val const& v )
+ : nKey( v.nKey )
+ , nVal( v.nVal )
+ {}
+
+ int key() const
+ {
+ return nKey;
+ }
+ };
+
+ struct int_item2: public key_val, public stat
+ {
+ int_item2()
+ {}
+
+ explicit int_item2( int key )
+ : key_val( key )
+ {}
+
+ int_item2( int key, int val )
+ : key_val( key, val )
+ {}
+
+ int_item2( int_item2 const& v )
+ : key_val( v )
+ , stat()
+ {}
+ };
+
struct hash_accessor {
int operator()( int_item const& v ) const
{
}
};
+ struct hash_accessor2 {
+ key_val const& operator()( int_item2 const& v ) const
+ {
+ return v;
+ }
+ };
+
struct simple_item_counter {
size_t m_nCount;
}
};
+ struct cmp2 {
+ int operator ()( key_val const& lhs, key_val const& rhs ) const
+ {
+ if ( lhs.key() < rhs.key() )
+ return -1;
+ return lhs.key() > rhs.key() ? 1 : 0;
+ }
+ };
+
struct mock_disposer
{
template <typename T>
this->test( s );
}
+TYPED_TEST_P( IntrusiveFeldmanHashSet, explicit_hash_size )
+{
+ struct traits: public ci::feldman_hashset::traits
+ {
+ typedef typename TestFixture::hash_accessor2 hash_accessor;
+ enum: size_t {
+ hash_size = sizeof( std::declval<key_val>().nKey )
+ };
+ typedef typename TestFixture::cmp2 compare;
+ typedef typename TestFixture::mock_disposer disposer;
+ typedef ci::feldman_hashset::stat<> stat;
+ };
+
+ typedef ci::FeldmanHashSet< typename TestFixture::rcu_type, typename TestFixture::int_item2, traits > set_type;
+
+ set_type s( 8, 3 );
+ this->test( s );
+}
+
// GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as
// "No test named <test_name> can be found in this test case"
REGISTER_TYPED_TEST_CASE_P( IntrusiveFeldmanHashSet,
- compare, less, cmpmix, backoff, stat
+ compare, less, cmpmix, backoff, stat, explicit_hash_size
);
test( s );
}
+ TEST_F( FeldmanHashSet_DHP, explicit_hash_size )
+ {
+ struct set_traits: public cc::feldman_hashset::traits
+ {
+ typedef get_hash2 hash_accessor;
+ enum: size_t {
+ hash_size = sizeof( std::declval<key_val>().nKey )
+ };
+ typedef cmp2 compare;
+ typedef cc::feldman_hashset::stat<> stat;
+ };
+ typedef cc::FeldmanHashSet< gc_type, int_item2, set_traits > set_type;
+
+ set_type s( 1, 1 );
+ test( s );
+ }
+
} // namespace
test( s );
}
+ TEST_F( FeldmanHashSet_HP, explicit_hash_size )
+ {
+ struct set_traits: public cc::feldman_hashset::traits
+ {
+ typedef get_hash2 hash_accessor;
+ enum: size_t {
+ hash_size = sizeof( std::declval<key_val>(). nKey )
+ };
+ typedef cmp2 compare;
+ typedef cc::feldman_hashset::stat<> stat;
+ };
+ typedef cc::FeldmanHashSet< gc_type, int_item2, set_traits > set_type;
+
+ set_type s( 1, 1 );
+ test( s );
+ }
+
} // namespace
}
};
+ struct key_val {
+ int nKey;
+ int nVal;
+
+ key_val()
+ {}
+
+ key_val( int key )
+ : nKey( key )
+ , nVal( key )
+ {}
+
+ key_val( int key, int val )
+ : nKey( key )
+ , nVal( val )
+ {}
+
+ key_val( key_val const& v )
+ : nKey( v.nKey )
+ , nVal( v.nVal )
+ {}
+
+ int key() const
+ {
+ return nKey;
+ }
+ };
+
+ struct int_item2: public key_val, public stat
+ {
+ std::string strVal;
+
+ int_item2()
+ {}
+
+ explicit int_item2( int key )
+ : key_val( key )
+ {}
+
+ int_item2( int key, int val )
+ : key_val( key, val )
+ {}
+
+ int_item2( int_item2 const& v )
+ : key_val( v )
+ , stat()
+ , strVal( v.strVal )
+ {}
+
+ int_item2( int_item2&& v )
+ : key_val( v )
+ , stat()
+ , strVal( std::move( v.strVal ))
+ {}
+
+ int_item2( int k, std::string&& s )
+ : key_val( k, k * 2 )
+ , strVal( std::move( s ) )
+ {}
+
+ explicit int_item2( other_item const& s )
+ : key_val( s.key(), s.key() * 2 )
+ {}
+ };
+
+
struct get_hash {
int operator()( int_item const& i ) const
{
}
};
+ struct get_hash2 {
+ key_val const& operator()( int_item2 const& i ) const
+ {
+ return i;
+ }
+
+ key_val operator()( other_item const& i ) const
+ {
+ return key_val( i.key() );
+ }
+
+ key_val operator()( int i ) const
+ {
+ return key_val( i );
+ }
+ };
+
struct simple_item_counter {
size_t m_nCount;
}
};
+ struct cmp2 {
+ int operator ()( key_val const& v1, key_val const& v2 ) const
+ {
+ if ( v1.key() < v2.key() )
+ return -1;
+ return v1.key() > v2.key() ? 1 : 0;
+ }
+ };
+
struct other_less {
template <typename Q, typename T>
bool operator()( Q const& lhs, T const& rhs ) const
this->test( s );
}
+ TYPED_TEST_P( FeldmanHashSet, explicit_hash_size )
+ {
+ typedef typename TestFixture::rcu_type rcu_type;
+ typedef typename TestFixture::int_item2 int_item;
+ typedef typename TestFixture::get_hash2 get_hash2;
+
+ struct set_traits: public cc::feldman_hashset::traits
+ {
+ enum: size_t {
+ hash_size = sizeof( std::declval<int_item2>().nKey )
+ };
+ typedef get_hash2 hash_accessor;
+ typedef cc::feldman_hashset::stat<> stat;
+ };
+ typedef cc::FeldmanHashSet< rcu_type, int_item2, set_traits > set_type;
+
+ set_type s( 8, 4 );
+ this->test( s );
+ }
+
// GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as
// "No test named <test_name> can be found in this test case"
REGISTER_TYPED_TEST_CASE_P( FeldmanHashSet,
- defaulted, compare, less, cmpmix, item_counting, backoff, stat
+ defaulted, compare, less, cmpmix, item_counting, backoff, stat, explicit_hash_size
);
} // namespace