{
/// Hash functor, default is \p std::hash
/**
- \p MultiLevelHashMap may use any hash functor converting key to
+ \p MultiLevelHashMap may use any hash functor converting a key to
fixed-sized bit-string, for example, <a href="https://en.wikipedia.org/wiki/Secure_Hash_Algorithm">SHA1, SHA2</a>,\r
<a href="https://en.wikipedia.org/wiki/MurmurHash">MurmurHash</a>,\r
<a href="https://en.wikipedia.org/wiki/CityHash">CityHash</a>\r
template <typename Q>
node_type( hasher& h, Q const& key )
- : m_Value( std::make_pair( key, mapped_type()))
+ : m_Value( std::move( std::make_pair( key, mapped_type())))
, m_hash( h( m_Value.first ))
{}
template <typename Q, typename U >
node_type( hasher& h, Q const& key, U const& val )
- : m_Value( std::make_pair( key, val ))
+ : m_Value( std::move( std::make_pair( key, mapped_type(val))))
, m_hash( h( m_Value.first ))
{}
template <typename Q, typename... Args>
node_type( hasher& h, Q&& key, Args&&... args )
- : m_Value( std::forward<Q>(key), std::move( mapped_type( std::forward<Args>(args)... )))
+ : m_Value( std::move( std::make_pair( std::forward<Q>(key), std::move( mapped_type( std::forward<Args>(args)... )))))
, m_hash( h( m_Value.first ))
{}
};
struct intrusive_traits: public original_traits
{
- typedef get_node_hash hash_accesor;
+ typedef get_node_hash hash_accessor;
typedef node_disposer disposer;
};
There are several specializations of \p %MultiLevelHashMap for each \p GC. You should include:
- <tt><cds/container/multilevel_hashmap_hp.h></tt> for \p gc::HP garbage collector
- <tt><cds/container/multilevel_hashmap_dhp.h></tt> for \p gc::DHP garbage collector
- - <tt><cds/container/multilevel_hashmap_rcu.h></tt> for \ref cds_intrusive_MultiLevelHashSet_rcu "RCU type". RCU specialization
+ - <tt><cds/container/multilevel_hashmap_rcu.h></tt> for \ref cds_intrusive_MultiLevelHashMap_rcu "RCU type". RCU specialization
has a slightly different interface.
*/
template <
#ifdef CDS_DOXYGEN_INVOKED
: protected cds::intrusive::MultiLevelHashSet< GC, std::pair<Key const, T>, Traits >
#else
- : protected cds::container::details::make_multilevel_hashmap< GC, Key, T, Hasher, Traits >::type
+ : protected cds::container::details::make_multilevel_hashmap< GC, Key, T, Traits >::type
#endif
{
//@cond
- typedef cds::container::details::make_multilevel_hashmap< GC, Key, T, Hasher, Traits > maker;
+ typedef cds::container::details::make_multilevel_hashmap< GC, Key, T, Traits > maker;
typedef typename maker::type base_class;
//@endcond
public:
typedef typename maker::cxx_node_allocator cxx_node_allocator;
typedef std::unique_ptr< node_type, typename maker::node_disposer > scoped_node_ptr;
- template <class Iterator>
- class bidirectional_iterator : protected Iterator
+ template <bool IsConst>
+ class bidirectional_iterator: public base_class::iterator_base
{
friend class MultiLevelHashMap;
- typedef Iterator base_class;
+ friend typename base_class;
+ typedef typename base_class::iterator_base iterator_base;
+
+ protected:
+ static CDS_CONSTEXPR bool const c_bConstantIterator = IsConst;
public:
- typedef typename std::conditional< base_class::c_bConstantIterator, value_type const*, value_type*>::type value_ptr; ///< Value pointer
- typedef typename std::conditional< base_class::c_bConstantIterator, value_type const&, value_type&>::type value_ref; ///< Value reference
+ typedef typename std::conditional< IsConst, value_type const*, value_type*>::type value_ptr; ///< Value pointer
+ typedef typename std::conditional< IsConst, value_type const&, value_type&>::type value_ref; ///< Value reference
public:
bidirectional_iterator() CDS_NOEXCEPT
- : base_class()
{}
bidirectional_iterator( bidirectional_iterator const& rhs ) CDS_NOEXCEPT
- : base_class( rhs )
+ : iterator_base( rhs )
{}
bidirectional_iterator& operator=(bidirectional_iterator const& rhs) CDS_NOEXCEPT
{
- base_class::operator=( rhs );
+ iterator_base::operator=( rhs );
return *this;
}
bidirectional_iterator& operator++()
{
- base_class::operator++();
+ iterator_base::operator++();
return *this;
}
bidirectional_iterator& operator--()
{
- base_class::operator--();
+ iterator_base::operator--();
return *this;
}
value_ptr operator ->() const CDS_NOEXCEPT
{
- return pointer();
+ node_type * p = iterator_base::pointer();
+ return p ? &p->m_Value : nullptr;
}
value_ref operator *() const CDS_NOEXCEPT
{
- value_ptr p = pointer();
+ node_type * p = iterator_base::pointer();
assert( p );
- return *p;
+ return p->m_Value;
}
void release()
{
- base_class::release();
+ iterator_base::release();
}
- template <class It>
- bool operator ==(It const& rhs) const CDS_NOEXCEPT
+ template <bool IsConst2>
+ bool operator ==(bidirectional_iterator<IsConst2> const& rhs) const CDS_NOEXCEPT
{
- return base_class::operator==( rhs );
+ return iterator_base::operator==( rhs );
}
- template <class It>
- bool operator !=(It const& rhs) const CDS_NOEXCEPT
+ template <bool IsConst2>
+ bool operator !=(bidirectional_iterator<IsConst2> const& rhs) const CDS_NOEXCEPT
{
return !( *this == rhs );
}
protected:
- bidirectional_iterator( base_class&& it ) CDS_NOEXCEPT
- : base_class( it )
+ bidirectional_iterator( base_class const& set, typename base_class::array_node * pNode, size_t idx, bool )
+ : iterator_base( set, pNode, idx, false )
+ {}
+
+ bidirectional_iterator( base_class const& set, typename base_class::array_node * pNode, size_t idx )
+ : iterator_base( set, pNode, idx )
+ {}
+ };
+
+ /// Reverse bidirectional iterator
+ template <bool IsConst>
+ class reverse_bidirectional_iterator : public base_class::iterator_base
+ {
+ friend class MultiLevelHashMap;
+ friend typename base_class;
+ typedef typename base_class::iterator_base iterator_base;
+
+ public:
+ typedef typename std::conditional< IsConst, value_type const*, value_type*>::type value_ptr; ///< Value pointer
+ typedef typename std::conditional< IsConst, value_type const&, value_type&>::type value_ref; ///< Value reference
+
+ public:
+ reverse_bidirectional_iterator() CDS_NOEXCEPT
+ : iterator_base()
+ {}
+
+ reverse_bidirectional_iterator( reverse_bidirectional_iterator const& rhs ) CDS_NOEXCEPT
+ : iterator_base( rhs )
{}
- value_ptr pointer() const CDS_NOEXCEPT
+ reverse_bidirectional_iterator& operator=( reverse_bidirectional_iterator const& rhs) CDS_NOEXCEPT
{
- typename base_class::value_ptr p = base_class::pointer();
+ iterator_base::operator=( rhs );
+ return *this;
+ }
+
+ reverse_bidirectional_iterator& operator++()
+ {
+ iterator_base::operator--();
+ return *this;
+ }
+
+ reverse_bidirectional_iterator& operator--()
+ {
+ iterator_base::operator++();
+ return *this;
+ }
+
+ value_ptr operator ->() const CDS_NOEXCEPT
+ {
+ node_type * p = iterator_base::pointer();
return p ? &p->m_Value : nullptr;
}
-
- node_type * node_pointer() const CDS_NOEXCEPT
+
+ value_ref operator *() const CDS_NOEXCEPT
{
- return base_class::pointer();
+ node_type * p = iterator_base::pointer();
+ assert( p );
+ return p->m_Value;
+ }
+
+ void release()
+ {
+ iterator_base::release();
+ }
+
+ template <bool IsConst2>
+ bool operator ==(reverse_bidirectional_iterator<IsConst2> const& rhs) const
+ {
+ return iterator_base::operator==( rhs );
+ }
+
+ template <bool IsConst2>
+ bool operator !=(reverse_bidirectional_iterator<IsConst2> const& rhs)
+ {
+ return !( *this == rhs );
+ }
+
+ protected:
+ reverse_bidirectional_iterator( base_class const& set, typename base_class::array_node * pNode, size_t idx, bool )
+ : iterator_base( set, pNode, idx, false )
+ {}
+
+ reverse_bidirectional_iterator( base_class const& set, typename base_class::array_node * pNode, size_t idx )
+ : iterator_base( set, pNode, idx, false )
+ {
+ iterator_base::backward();
}
};
+
//@endcond
public:
/// Guarded pointer
typedef typename gc::template guarded_ptr< value_type > guarded_ptr;
#else
- typedef typename gc::template guarded_ptr< node_type, value_type, cds::details::guarded_ptr_cast_set<node_type, value_type> > guarded_ptr;
+ typedef typename gc::template guarded_ptr< node_type, value_type, cds::container::details::guarded_ptr_cast_set<node_type, value_type> > guarded_ptr;
#endif
#ifdef CDS_DOXYGEN_INVOKED
typedef implementation_defined reverse_iterator; ///< @ref cds_container_MultilevelHashMap_iterators "bidirectional reverse iterator" type
typedef implementation_defined const_reverse_iterator; ///< @ref cds_container_MultilevelHashMap_iterators "bidirectional reverse const iterator" type
#else
- typedef bidirectional_iterator<typename base_class::iterator> iterator;
- typedef bidirectional_iterator<typename base_class::const_iterator> const_iterator;
- typedef bidirectional_iterator<typename base_class::reverse_iterator> reverse_iterator;
- typedef bidirectional_iterator<typename base_class::const_reverse_iterator> const_reverse_iterator;
+ typedef bidirectional_iterator<false> iterator;
+ typedef bidirectional_iterator<true> const_iterator;
+ typedef reverse_bidirectional_iterator<false> reverse_iterator;
+ typedef reverse_bidirectional_iterator<true> const_reverse_iterator;
#endif
protected:
Returns \p true if inserting successful, \p false otherwise.
*/
template <typename K>
- bool insert( K const& key )
+ bool insert( K&& key )
{
- scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, key ));
+ scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key) ));
if ( base_class::insert( *sp )) {
sp.release();
return true;
Returns \p true if \p val is inserted into the map, \p false otherwise.
*/
template <typename K, typename V>
- bool insert( K const& key, V const& val )
+ bool insert( K&& key, V&& val )
{
- scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, key, val ));
+ scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key), std::forward<V>(val)));
if ( base_class::insert( *sp )) {
sp.release();
return true;
it is preferable that the initialization should be completed only if inserting is successful.
*/
template <typename K, typename Func>
- bool insert_with( K const& key, Func func )
+ bool insert_with( K&& key, Func func )
{
- scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, key ));
+ scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key)));
if ( base_class::insert( *sp, [&func]( node_type& item ) { func( item.m_Value ); } )) {
sp.release();
return true;
template <typename K, typename... Args>
bool emplace( K&& key, Args&&... args )
{
- scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, std::forward<K>(key), std::forward<Args>(args)... ));
+ scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key), std::forward<Args>(args)... ));
if ( base_class::insert( *sp )) {
sp.release();
return true;
/// Updates data by \p key
/**
- The operation performs inserting or changing data with lock-free manner.
+ The operation performs inserting or replacing the element with lock-free manner.
If the \p key not found in the map, then the new item created from \p key
will be inserted into the map iff \p bInsert is \p true
(note that in this case the \ref key_type should be constructible from type \p K).
- Otherwise, if \p key is found, the functor \p func is called with item found.
+ Otherwise, if \p key is found, it is replaced with a new item created from
+ \p key.
The functor \p Func signature:
\code
struct my_functor {
- void operator()( bool bNew, value_type& item );
+ void operator()( value_type& item, value_type * old );
};
\endcode
where:
- - \p bNew - \p true if the item has been inserted, \p false otherwise
- \p item - item of the map
+ - \p old - old item of the map, if \p nullptr - the new item was inserted
- The functor may change any fields of the \p item.second that is \ref value_type.
+ The functor may change any fields of the \p item.second.
Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
\p second is \p true if new item has been added or \p false if \p key already exists.
@warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
*/
template <typename K, typename Func>
- std::pair<bool, bool> update( K const& key, Func func, bool bInsert = true )
+ std::pair<bool, bool> update( K&& key, Func func, bool bInsert = true )
{
- scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, key ));
- std::pair<bool, bool> result = base_class::do_update( *sp, [&func]( bool bNew, node_type& node ) { func( bNew, node.m_Value );}, bInsert );
- if ( !result.first )
+ scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key)));
+ std::pair<bool, bool> result = base_class::do_update( *sp,
+ [&func]( node_type& node, node_type * old ) { func( node.m_Value, old ? &old->m_Value : nullptr );},
+ bInsert );
+ if ( result.first )
sp.release();
return result;
}
*/
bool erase_at( iterator const& iter )
{
- return base_class::erase_at( static_cast<typename iterator::base_class const&>( iter ));
+ return base_class::do_erase_at( iter );
}
//@cond
bool erase_at( reverse_iterator const& iter )
{
- return base_class::erase_at( static_cast<typenamereverse_iterator::base_class const&>( iter ));
+ return base_class::do_erase_at( iter );
}
//@endcond
return gp;
}
- /// Extracts the item pointed by the iterator \p iter
- /**
- The item extracted is freed automatically by garbage collector \p GC
- when returned \p guarded_ptr object will be destroyed or released.
-
- @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
-
- Due to concurrent nature of the map the returned guarded pointer may be empty.
- Check it before dereferencing.
- */
- guarded_ptr extract_at( iterator const& iter )
- {
- guarded_ptr gp;
- if ( base_class::erase_at( iter )) {
- // The element erased is guarded by iter so it is still alive
- gp.reset( iter.node_pointer());
- }
- return gp;
- }
-
/// Checks whether the map contains \p key
/**
The function searches the item by its hash that is equal to <tt>hash( key_type( key ))</tt>
/// Returns an iterator to the beginning of the map
iterator begin()
{
- return iterator( base_class::begin() );
+ return base_class::template init_begin<iterator>();
}
/// Returns an const iterator to the beginning of the map
const_iterator begin() const
{
- return const_iterator( base_class::begin());
+ return base_class::template init_begin<const_iterator>();
}
/// Returns an const iterator to the beginning of the map
const_iterator cbegin()
{
- return const_iterator( base_class::cbegin());
+ return base_class::template init_begin<const_iterator>();
}
/// Returns an iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior.
iterator end()
{
- return iterator( base_class::end());
+ return base_class::template init_end<iterator>();
}
/// Returns a const iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior.
const_iterator end() const
{
- return const_iterator( base_class::end());
+ return base_class::template init_end<const_iterator>();
}
/// Returns a const iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior.
const_iterator cend()
{
- return const_iterator( base_class::cend());
+ return base_class::template init_end<const_iterator>();
}
/// Returns a reverse iterator to the first element of the reversed map
reverse_iterator rbegin()
{
- return reverse_iterator( base_class::rbegin());
+ return base_class::template init_rbegin<reverse_iterator>();
}
/// Returns a const reverse iterator to the first element of the reversed map
const_reverse_iterator rbegin() const
{
- return const_reverse_iterator( base_class::rbegin());
+ return base_class::template init_rbegin<const_reverse_iterator>();
}
/// Returns a const reverse iterator to the first element of the reversed map
const_reverse_iterator crbegin()
{
- return const_reverse_iterator( base_class::crbegin());
+ return base_class::template init_rbegin<const_reverse_iterator>();
}
/// Returns a reverse iterator to the element following the last element of the reversed map
*/
reverse_iterator rend()
{
- return reverse_iterator( base_class::rend());
+ return base_class::template init_rend<reverse_iterator>();
}
/// Returns a const reverse iterator to the element following the last element of the reversed map
*/
const_reverse_iterator rend() const
{
- return const_reverse_iterator( base_class::rend());
+ return base_class::template init_rend<const_reverse_iterator>();
}
/// Returns a const reverse iterator to the element following the last element of the reversed map
*/
const_reverse_iterator crend()
{
- return const_reverse_iterator( base_class::crend());
+ return base_class::template init_rend<const_reverse_iterator>();
}
///@}
};
protected:
//@cond
- /// Bidirectional iterator class
- template <bool IsConst>
- class bidirectional_iterator
+ class iterator_base
{
friend class MultiLevelHashSet;
+ protected:
array_node * m_pNode; ///< current array node
size_t m_idx; ///< current position in m_pNode
typename gc::Guard m_guard; ///< HP guard
MultiLevelHashSet const* m_set; ///< Hash set
-
- protected:
- static CDS_CONSTEXPR bool const c_bConstantIterator = IsConst;
-
- public:
- typedef typename std::conditional< IsConst, value_type const*, value_type*>::type value_ptr; ///< Value pointer
- typedef typename std::conditional< IsConst, value_type const&, value_type&>::type value_ref; ///< Value reference
-
+
public:
- bidirectional_iterator() CDS_NOEXCEPT
+ iterator_base() CDS_NOEXCEPT
: m_pNode( nullptr )
, m_idx( 0 )
, m_set( nullptr )
{}
- bidirectional_iterator( bidirectional_iterator const& rhs ) CDS_NOEXCEPT
+ iterator_base( iterator_base const& rhs ) CDS_NOEXCEPT
: m_pNode( rhs.m_pNode )
, m_idx( rhs.m_idx )
, m_set( rhs.m_set )
m_guard.copy( rhs.m_guard );
}
- bidirectional_iterator& operator=(bidirectional_iterator const& rhs) CDS_NOEXCEPT
+ iterator_base& operator=(iterator_base const& rhs) CDS_NOEXCEPT
{
m_pNode = rhs.m_pNode;
m_idx = rhs.m_idx;
return *this;
}
- bidirectional_iterator& operator++()
+ iterator_base& operator++()
{
forward();
return *this;
}
- bidirectional_iterator& operator--()
+ iterator_base& operator--()
{
backward();
return *this;
}
- value_ptr operator ->() const CDS_NOEXCEPT
- {
- return pointer();
- }
-
- value_ref operator *() const CDS_NOEXCEPT
- {
- value_ptr p = pointer();
- assert( p );
- return *p;
- }
-
void release()
{
m_guard.clear();
}
- template <bool IsConst2>
- bool operator ==(bidirectional_iterator<IsConst2> const& rhs) const CDS_NOEXCEPT
+ bool operator ==(iterator_base const& rhs) const CDS_NOEXCEPT
{
return m_pNode == rhs.m_pNode && m_idx == rhs.m_idx && m_set == rhs.m_set;
}
- template <bool IsConst2>
- bool operator !=(bidirectional_iterator<IsConst2> const& rhs) const CDS_NOEXCEPT
+ bool operator !=(iterator_base const& rhs) const CDS_NOEXCEPT
{
return !( *this == rhs );
}
protected:
- bidirectional_iterator( MultiLevelHashSet& set, array_node * pNode, size_t idx, bool )
+ iterator_base( MultiLevelHashSet const& set, array_node * pNode, size_t idx, bool )
: m_pNode( pNode )
, m_idx( idx )
, m_set( &set )
{}
- bidirectional_iterator( MultiLevelHashSet& set, array_node * pNode, size_t idx )
+ iterator_base( MultiLevelHashSet const& set, array_node * pNode, size_t idx )
: m_pNode( pNode )
, m_idx( idx )
, m_set( &set )
forward();
}
- value_ptr pointer() const CDS_NOEXCEPT
+ value_type * pointer() const CDS_NOEXCEPT
{
return m_guard.template get<value_type>();
}
}
};
+ template <class Iterator>
+ Iterator init_begin() const
+ {
+ return Iterator( *this, m_Head, size_t(0) - 1 );
+ }
+
+ template <class Iterator>
+ Iterator init_end() const
+ {
+ return Iterator( *this, m_Head, head_size(), false );
+ }
+
+ template <class Iterator>
+ Iterator init_rbegin() const
+ {
+ return Iterator( *this, m_Head, head_size() );
+ }
+
+ template <class Iterator>
+ Iterator init_rend() const
+ {
+ return Iterator( *this, m_Head, size_t(0) - 1, false );
+ }
+
+ /// Bidirectional iterator class
+ template <bool IsConst>
+ class bidirectional_iterator: protected iterator_base
+ {
+ friend class MultiLevelHashSet;
+
+ protected:
+ static CDS_CONSTEXPR bool const c_bConstantIterator = IsConst;
+
+ public:
+ typedef typename std::conditional< IsConst, value_type const*, value_type*>::type value_ptr; ///< Value pointer
+ typedef typename std::conditional< IsConst, value_type const&, value_type&>::type value_ref; ///< Value reference
+
+ public:
+ bidirectional_iterator() CDS_NOEXCEPT
+ {}
+
+ bidirectional_iterator( bidirectional_iterator const& rhs ) CDS_NOEXCEPT
+ : iterator_base( rhs )
+ {}
+
+ bidirectional_iterator& operator=(bidirectional_iterator const& rhs) CDS_NOEXCEPT
+ {
+ iterator_base::operator=( rhs );
+ return *this;
+ }
+
+ bidirectional_iterator& operator++()
+ {
+ iterator_base::operator++();
+ return *this;
+ }
+
+ bidirectional_iterator& operator--()
+ {
+ iterator_base::operator--();
+ return *this;
+ }
+
+ value_ptr operator ->() const CDS_NOEXCEPT
+ {
+ return iterator_base::pointer();
+ }
+
+ value_ref operator *() const CDS_NOEXCEPT
+ {
+ value_ptr p = iterator_base::pointer();
+ assert( p );
+ return *p;
+ }
+
+ void release()
+ {
+ iterator_base::release();
+ }
+
+ template <bool IsConst2>
+ bool operator ==(bidirectional_iterator<IsConst2> const& rhs) const CDS_NOEXCEPT
+ {
+ return iterator_base::operator==( rhs );
+ }
+
+ template <bool IsConst2>
+ bool operator !=(bidirectional_iterator<IsConst2> const& rhs) const CDS_NOEXCEPT
+ {
+ return !( *this == rhs );
+ }
+
+ protected:
+ bidirectional_iterator( MultiLevelHashSet& set, array_node * pNode, size_t idx, bool )
+ : iterator_base( set, pNode, idx, false )
+ {}
+
+ bidirectional_iterator( MultiLevelHashSet& set, array_node * pNode, size_t idx )
+ : iterator_base( set, pNode, idx )
+ {}
+ };
+
/// Reverse bidirectional iterator
template <bool IsConst>
- class reverse_bidirectional_iterator : protected bidirectional_iterator<IsConst>
+ class reverse_bidirectional_iterator : public iterator_base
{
- typedef bidirectional_iterator<IsConst> base_class;
friend class MultiLevelHashSet;
public:
- typedef typename base_class::value_ptr value_ptr;
- typedef typename base_class::value_ref value_ref;
+ typedef typename std::conditional< IsConst, value_type const*, value_type*>::type value_ptr; ///< Value pointer
+ typedef typename std::conditional< IsConst, value_type const&, value_type&>::type value_ref; ///< Value reference
public:
reverse_bidirectional_iterator() CDS_NOEXCEPT
- : base_class()
+ : iterator_base()
{}
reverse_bidirectional_iterator( reverse_bidirectional_iterator const& rhs ) CDS_NOEXCEPT
- : base_class( rhs )
+ : iterator_base( rhs )
{}
reverse_bidirectional_iterator& operator=( reverse_bidirectional_iterator const& rhs) CDS_NOEXCEPT
{
- base_class::operator=( rhs );
+ iterator_base::operator=( rhs );
return *this;
}
reverse_bidirectional_iterator& operator++()
{
- base_class::backward();
+ iterator_base::operator--();
return *this;
}
reverse_bidirectional_iterator& operator--()
{
- base_class::forward();
+ iterator_base::operator++();
return *this;
}
value_ptr operator ->() const CDS_NOEXCEPT
{
- return base_class::operator->();
+ return iterator_base::pointer();
}
value_ref operator *() const CDS_NOEXCEPT
{
- return base_class::operator*();
+ value_ptr p = iterator_base::pointer();
+ assert( p );
+ return *p;
}
void release()
{
- base_class::release();
+ iterator_base::release();
}
template <bool IsConst2>
bool operator ==(reverse_bidirectional_iterator<IsConst2> const& rhs) const
{
- return base_class::operator==( rhs );
+ return iterator_base::operator==( rhs );
}
template <bool IsConst2>
}
private:
+ reverse_bidirectional_iterator( MultiLevelHashSet& set, array_node * pNode, size_t idx, bool )
+ : iterator_base( set, pNode, idx, false )
+ {}
+
reverse_bidirectional_iterator( MultiLevelHashSet& set, array_node * pNode, size_t idx )
- : base_class( set, pNode, idx, false )
+ : iterator_base( set, pNode, idx, false )
{
- base_class::backward();
+ iterator_base::backward();
}
};
//@endcond
*/
bool erase_at( iterator const& iter )
{
- if ( iter.m_set != this )
- return false;
- if ( iter.m_pNode == m_Head && iter.m_idx >= head_size())
- return false;
- if ( iter.m_idx >= array_node_size() )
- return false;
-
- for (;;) {
- node_ptr slot = iter.m_pNode->nodes[iter.m_idx].load( memory_model::memory_order_acquire );
- if ( slot.bits() == 0 && slot.ptr() == iter.pointer() ) {
- if ( iter.m_pNode->nodes[iter.m_idx].compare_exchange_strong(slot, node_ptr(nullptr), memory_model::memory_order_acquire, atomics::memory_order_relaxed) ) {
- // the item is guarded by iterator, so we may retire it safely
- gc::template retire<disposer>( slot.ptr() );
- --m_ItemCounter;
- m_Stat.onEraseSuccess();
- return true;
- }
- }
- else
- return false;
- }
+ return do_erase_at( iter );
}
//@cond
bool erase_at( reverse_iterator const& iter )
{
- return erase_at(static_cast<iterator const&>( iter ));
+ return do_erase_at( iter );
}
//@endcond
/// Returns an iterator to the element following the last element of the set. This element acts as a placeholder; attempting to access it results in undefined behavior.
iterator end()
{
- return iterator( *this, m_Head, head_size() - 1 );
+ return iterator( *this, m_Head, head_size(), false );
}
/// Returns a const iterator to the element following the last element of the set. This element acts as a placeholder; attempting to access it results in undefined behavior.
const_iterator end() const
{
- return const_iterator( *this, m_Head, head_size() - 1 );
+ return const_iterator( *this, m_Head, head_size(), false );
}
/// Returns a const iterator to the element following the last element of the set. This element acts as a placeholder; attempting to access it results in undefined behavior.
const_iterator cend()
{
- return const_iterator( *this, m_Head, head_size() - 1 );
+ return const_iterator( *this, m_Head, head_size(), false );
}
/// Returns a reverse iterator to the first element of the reversed set
*/
reverse_iterator rend()
{
- return reverse_iterator( *this, m_Head, 0 );
+ return reverse_iterator( *this, m_Head, size_t(0) - 1, false );
}
/// Returns a const reverse iterator to the element following the last element of the reversed set
*/
const_reverse_iterator rend() const
{
- return const_reverse_iterator( *this, m_Head, 0 );
+ return const_reverse_iterator( *this, m_Head, size_t(0) - 1, false );
}
/// Returns a const reverse iterator to the element following the last element of the reversed set
*/
const_reverse_iterator crend()
{
- return const_reverse_iterator( *this, m_Head, 0 );
+ return const_reverse_iterator( *this, m_Head, size_t(0) - 1, false );
}
///@}
} // while
}
+ bool do_erase_at( iterator_base const& iter )
+ {
+ if ( iter.m_set != this )
+ return false;
+ if ( iter.m_pNode == m_Head && iter.m_idx >= head_size())
+ return false;
+ if ( iter.m_idx >= array_node_size() )
+ return false;
+
+ for (;;) {
+ node_ptr slot = iter.m_pNode->nodes[iter.m_idx].load( memory_model::memory_order_acquire );
+ if ( slot.bits() == 0 && slot.ptr() == iter.pointer() ) {
+ if ( iter.m_pNode->nodes[iter.m_idx].compare_exchange_strong(slot, node_ptr(nullptr), memory_model::memory_order_acquire, atomics::memory_order_relaxed) ) {
+ // the item is guarded by iterator, so we may retire it safely
+ gc::template retire<disposer>( slot.ptr() );
+ --m_ItemCounter;
+ m_Stat.onEraseSuccess();
+ return true;
+ }
+ }
+ else
+ return false;
+ }
+ }
+
template <typename Func>
std::pair<bool, bool> do_update( value_type& val, Func f, bool bInsert = true )
{
<ClInclude Include="..\..\..\tests\test-hdr\map\hdr_skiplist_map.h" />\r
<ClInclude Include="..\..\..\tests\test-hdr\map\hdr_skiplist_map_rcu.h" />\r
<ClInclude Include="..\..\..\tests\test-hdr\map\hdr_striped_map.h" />\r
+ <ClInclude Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap.h" />\r
<ClInclude Include="..\..\..\tests\test-hdr\map\print_skiplist_stat.h" />\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="..\..\..\tests\test-hdr\map\hdr_michael_map_rcu_gpt.cpp" />\r
<ClCompile Include="..\..\..\tests\test-hdr\map\hdr_michael_map_rcu_shb.cpp" />\r
<ClCompile Include="..\..\..\tests\test-hdr\map\hdr_michael_map_rcu_sht.cpp" />\r
+ <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp" />\r
+ <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_hp.cpp" />\r
<ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_flat_map.cpp" />\r
<ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_list.cpp" />\r
<ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_map.cpp" />\r
<ClCompile Include="..\..\..\tests\test-hdr\map\hdr_skiplist_map_dhp.cpp">\r
<Filter>skip_list</Filter>\r
</ClCompile>\r
+ <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_hp.cpp">\r
+ <Filter>multilvel_hashmap</Filter>\r
+ </ClCompile>\r
+ <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp">\r
+ <Filter>multilvel_hashmap</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="..\..\..\tests\test-hdr\map\hdr_map.h" />\r
<ClInclude Include="..\..\..\tests\test-hdr\map\hdr_cuckoo_map.h">\r
<Filter>cuckoo</Filter>\r
</ClInclude>\r
+ <ClInclude Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap.h">\r
+ <Filter>multilvel_hashmap</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<Filter Include="cuckoo">\r
<Filter Include="split_list">\r
<UniqueIdentifier>{9318c3c0-92a3-4a5a-be2b-a47411a3e4c4}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="multilvel_hashmap">\r
+ <UniqueIdentifier>{cf81877d-3069-48a6-a143-2281963e9c4f}</UniqueIdentifier>\r
+ </Filter>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
tests/test-hdr/map/hdr_michael_map_lazy_rcu_shb.cpp \
tests/test-hdr/map/hdr_michael_map_lazy_rcu_sht.cpp \
tests/test-hdr/map/hdr_michael_map_lazy_nogc.cpp \
+ tests/test-hdr/map/hdr_multilevel_hashmap_hp.cpp \
+ tests/test-hdr/map/hdr_multilevel_hashmap_dhp.cpp \
tests/test-hdr/map/hdr_refinable_hashmap_hashmap_std.cpp \
tests/test-hdr/map/hdr_refinable_hashmap_boost_list.cpp \
tests/test-hdr/map/hdr_refinable_hashmap_list.cpp \
map/hdr_michael_map_lazy_rcu_shb.cpp\r
map/hdr_michael_map_lazy_rcu_sht.cpp\r
map/hdr_michael_map_lazy_nogc.cpp\r
+ map/hdr_multilevel_hashmap_hp.cpp\r
+ map/hdr_multilevel_hashmap_dhp.cpp\r
map/hdr_refinable_hashmap_hashmap_std.cpp\r
map/hdr_refinable_hashmap_boost_list.cpp\r
map/hdr_refinable_hashmap_list.cpp\r
--- /dev/null
+//$$CDS-header$$
+
+#ifndef CDSTEST_HDR_MULTILEVEL_HASHMAP_H
+#define CDSTEST_HDR_MULTILEVEL_HASHMAP_H
+
+#include "cppunit/cppunit_proxy.h"
+
+// forward declaration
+namespace cds {
+ namespace container {}
+ namespace opt {}
+}
+
+namespace map {
+ namespace cc = cds::container;
+ namespace co = cds::opt;
+
+ class MultiLevelHashMapHdrTest : public CppUnitMini::TestCase
+ {
+ struct Item
+ {
+ unsigned int nInsertCall;
+ unsigned int nFindCall;
+ unsigned int nEraseCall;
+ mutable unsigned int nIteratorCall;
+
+ Item()
+ : nInsertCall(0)
+ , nFindCall(0)
+ , nEraseCall(0)
+ , nIteratorCall(0)
+ {}
+
+ explicit Item( unsigned int n )
+ : nInsertCall(n)
+ , nFindCall(0)
+ , nEraseCall(0)
+ , nIteratorCall(0)
+ {}
+ };
+
+ struct hash128
+ {
+ size_t lo;
+ size_t hi;
+
+ hash128() {}
+ hash128(size_t l, size_t h) : lo(l), hi(h) {}
+ hash128( hash128 const& h) : lo(h.lo), hi(h.hi) {}
+
+ struct make {
+ hash128 operator()( size_t n ) const
+ {
+ return hash128( std::hash<size_t>()( n ), std::hash<size_t>()( ~n ));
+ }
+ hash128 operator()( hash128 const& n ) const
+ {
+ return hash128( std::hash<size_t>()( n.lo ), std::hash<size_t>()( ~n.hi ));
+ }
+ };
+
+ struct less {
+ bool operator()( hash128 const& lhs, hash128 const& rhs ) const
+ {
+ if ( lhs.hi != rhs.hi )
+ return lhs.hi < rhs.hi;
+ return lhs.lo < rhs.lo;
+ }
+ };
+
+ struct cmp {
+ int operator()( hash128 const& lhs, hash128 const& rhs ) const
+ {
+ if ( lhs.hi != rhs.hi )
+ return lhs.hi < rhs.hi ? -1 : 1;
+ return lhs.lo < rhs.lo ? -1 : lhs.lo == rhs.lo ? 0 : 1;
+ }
+ };
+
+ friend bool operator==( hash128 const& lhs, hash128 const& rhs )
+ {
+ return cmp()( lhs, rhs ) == 0;
+ }
+ friend bool operator!=(hash128 const& lhs, hash128 const& rhs)
+ {
+ return !( lhs == rhs );
+ }
+ };
+
+ template <typename Map>
+ void test_hp( size_t nHeadBits, size_t nArrayBits )
+ {
+ typedef typename Map::hash_type hash_type;
+ typedef typename Map::key_type key_type;
+ typedef typename Map::mapped_type mapped_type;
+ typedef typename Map::value_type value_type;
+ typedef typename Map::guarded_ptr guarded_ptr;
+
+ size_t const capacity = 1000;
+
+ Map m;
+ CPPUNIT_MSG("Array size: head=" << m.head_size() << ", array_node=" << m.array_node_size());
+ //CPPUNIT_ASSERT(m.head_size() >= (size_t(1) << nHeadBits));
+ //CPPUNIT_ASSERT(m.array_node_size() == (size_t(1) << nArrayBits));
+
+ CPPUNIT_ASSERT(m.empty());
+ CPPUNIT_ASSERT(m.size() == 0);
+
+ // insert( key )/update()/get()/find()
+ for ( size_t i = 0; i < capacity; ++i ) {
+ size_t key = i * 57;
+ CPPUNIT_ASSERT(!m.contains( key ))
+ CPPUNIT_ASSERT(m.insert( key ));
+ CPPUNIT_ASSERT(m.contains( key ));
+ CPPUNIT_ASSERT(m.size() == i + 1);
+
+ auto ret = m.update(key, [] ( value_type& v, value_type * old ) {
+ CPPUNIT_ASSERT_CURRENT( old != nullptr );
+ ++v.second.nInsertCall;
+ }, false );
+ CPPUNIT_ASSERT( ret.first );
+ CPPUNIT_ASSERT( !ret.second );
+
+ CPPUNIT_ASSERT(m.find(key, [](value_type& v) { ++v.second.nFindCall;} ));
+
+ guarded_ptr gp{ m.get( key ) };
+ CPPUNIT_ASSERT( gp );
+ CPPUNIT_ASSERT( gp->first == key );
+ CPPUNIT_ASSERT( gp->second.nInsertCall == 1 );
+ CPPUNIT_ASSERT( gp->second.nFindCall == 1 );
+ }
+ CPPUNIT_ASSERT(!m.empty());
+ CPPUNIT_ASSERT(m.size() == capacity);
+
+ // iterator test
+ size_t nCount = 0;
+ for ( auto it = m.begin(), itEnd = m.end(); it != itEnd; ++it ) {
+ CPPUNIT_ASSERT( it->second.nIteratorCall == 0 );
+ CPPUNIT_ASSERT( it->second.nInsertCall == 1 );
+ CPPUNIT_ASSERT( (*it).second.nFindCall == 1 );
+ it->second.nIteratorCall += 1;
+ ++nCount;
+ }
+ CPPUNIT_ASSERT( nCount == capacity );
+
+ nCount = 0;
+ for ( auto it = m.rbegin(), itEnd = m.rend(); it != itEnd; ++it ) {
+ CPPUNIT_ASSERT( it->second.nInsertCall == 1 );
+ CPPUNIT_ASSERT( (*it).second.nFindCall == 1 );
+ CPPUNIT_ASSERT( it->second.nIteratorCall == 1 );
+ (*it).second.nIteratorCall += 1;
+ ++nCount;
+ }
+ CPPUNIT_ASSERT( nCount == capacity );
+
+ nCount = 0;
+ for ( auto it = m.cbegin(), itEnd = m.cend(); it != itEnd; ++it ) {
+ CPPUNIT_ASSERT( it->second.nInsertCall == 1 );
+ CPPUNIT_ASSERT( (*it).second.nFindCall == 1 );
+ CPPUNIT_ASSERT( it->second.nIteratorCall == 2 );
+ (*it).second.nIteratorCall += 1;
+ ++nCount;
+ }
+ CPPUNIT_ASSERT( nCount == capacity );
+
+ nCount = 0;
+ for ( auto it = m.crbegin(), itEnd = m.crend(); it != itEnd; ++it ) {
+ CPPUNIT_ASSERT( it->second.nInsertCall == 1 );
+ CPPUNIT_ASSERT( (*it).second.nFindCall == 1 );
+ CPPUNIT_ASSERT( it->second.nIteratorCall == 3 );
+ (*it).second.nIteratorCall += 1;
+ ++nCount;
+ }
+ CPPUNIT_ASSERT( nCount == capacity );
+
+ // find
+ for ( size_t i = 0; i < capacity; i++ ) {
+ size_t key = i * 57;
+ CPPUNIT_ASSERT( m.find( key, [key]( value_type& v ) {
+ CPPUNIT_ASSERT_CURRENT( v.first == key );
+ CPPUNIT_ASSERT_CURRENT( v.second.nInsertCall == 1 );
+ CPPUNIT_ASSERT_CURRENT( v.second.nFindCall == 1 );
+ CPPUNIT_ASSERT_CURRENT( v.second.nIteratorCall == 4 );
+ }));
+ }
+
+ // erase
+ for ( size_t i = capacity; i > 0; --i ) {
+ size_t key = (i -1) * 57;
+ guarded_ptr gp = m.get( key );
+ CPPUNIT_ASSERT( gp );
+ CPPUNIT_ASSERT( gp->first == key );
+ CPPUNIT_ASSERT( gp->second.nInsertCall == 1 );
+ CPPUNIT_ASSERT( gp->second.nFindCall == 1 );
+ CPPUNIT_ASSERT( (*gp).second.nIteratorCall == 4 );
+
+ CPPUNIT_ASSERT(m.erase( key ));
+
+ gp = m.get( key );
+ CPPUNIT_ASSERT( !gp );
+ CPPUNIT_ASSERT(!m.contains( key ));
+ }
+ CPPUNIT_ASSERT( m.empty());
+ CPPUNIT_ASSERT(m.size() == 0);
+
+ // Iterators on empty map
+ CPPUNIT_ASSERT(m.begin() == m.end());
+ CPPUNIT_ASSERT(m.cbegin() == m.cend());
+ CPPUNIT_ASSERT(m.rbegin() == m.rend());
+ CPPUNIT_ASSERT(m.crbegin() == m.crend());
+
+ // insert( key, val )
+ for ( size_t i = 0; i < capacity; ++i ) {
+ CPPUNIT_ASSERT(!m.contains(i));
+ CPPUNIT_ASSERT(m.insert( i, (unsigned int) i * 100));
+ CPPUNIT_ASSERT( m.contains(i));
+ CPPUNIT_ASSERT( m.find( i, [i]( value_type& v ) {
+ CPPUNIT_ASSERT_CURRENT( v.first == i );
+ CPPUNIT_ASSERT_CURRENT( v.second.nInsertCall == i * 100 );
+ }));
+ }
+ CPPUNIT_ASSERT( !m.empty());
+ CPPUNIT_ASSERT(m.size() == capacity);
+
+ // erase( key, func )
+ for ( size_t i = 0; i < capacity; ++i ) {
+ CPPUNIT_ASSERT( m.contains(i));
+ CPPUNIT_ASSERT( m.erase( i, [i]( value_type& v ) {
+ CPPUNIT_ASSERT_CURRENT( v.first == i );
+ CPPUNIT_ASSERT_CURRENT( v.second.nInsertCall == i * 100 );
+ v.second.nInsertCall = 0;
+ }));
+ }
+ CPPUNIT_ASSERT( m.empty());
+ CPPUNIT_ASSERT(m.size() == 0 );
+
+ // insert_with
+ for ( size_t i = 0; i < capacity; ++i ) {
+ size_t key = i * 121;
+ CPPUNIT_ASSERT(!m.contains(key));
+ CPPUNIT_ASSERT( m.insert_with( key, [key]( value_type& v ) {
+ CPPUNIT_ASSERT_CURRENT( v.first == key );
+ CPPUNIT_ASSERT_CURRENT( v.second.nInsertCall == 0 );
+ v.second.nInsertCall = decltype(v.second.nInsertCall)( key );
+ }));
+ CPPUNIT_ASSERT(m.find(key, [key] (value_type& v ) {
+ CPPUNIT_ASSERT_CURRENT( v.first == key );
+ CPPUNIT_ASSERT_CURRENT( v.second.nInsertCall == key );
+ }));
+ CPPUNIT_ASSERT(m.size() == i + 1);
+ }
+ CPPUNIT_ASSERT( !m.empty());
+ CPPUNIT_ASSERT(m.size() == capacity);
+
+ nCount = 0;
+ for ( auto it = m.begin(), itEnd = m.end(); it != itEnd; ++it ) {
+ CPPUNIT_ASSERT( it->first == it->second.nInsertCall );
+ CPPUNIT_ASSERT( it->second.nIteratorCall == 0 );
+ it->second.nIteratorCall += 1;
+ ++nCount;
+ }
+ CPPUNIT_ASSERT( nCount == capacity );
+
+ nCount = 0;
+ for ( auto it = m.rbegin(), itEnd = m.rend(); it != itEnd; ++it ) {
+ CPPUNIT_ASSERT( it->first == it->second.nInsertCall );
+ CPPUNIT_ASSERT( it->second.nIteratorCall == 1 );
+ it->second.nIteratorCall += 1;
+ ++nCount;
+ }
+ CPPUNIT_ASSERT( nCount == capacity );
+
+ // erase_at( iterator )
+ nCount = 0;
+ for ( auto it = m.begin(), itEnd = m.end(); it != itEnd; ++it ) {
+ CPPUNIT_ASSERT( it->first == it->second.nInsertCall );
+ CPPUNIT_ASSERT( it->second.nIteratorCall == 2 );
+ CPPUNIT_ASSERT(m.erase_at( it ));
+ ++nCount;
+ CPPUNIT_ASSERT(!m.contains( it->first ));
+ }
+ CPPUNIT_ASSERT( nCount == capacity );
+ CPPUNIT_ASSERT( m.empty());
+ CPPUNIT_ASSERT(m.size() == 0 );
+
+ // emplace
+ for ( size_t i = 0; i < capacity; ++i ) {
+ size_t key = i * 1023;
+ CPPUNIT_ASSERT(!m.contains(key));
+ CPPUNIT_ASSERT( m.emplace( key, (unsigned int) i ));
+ CPPUNIT_ASSERT(m.find(key, [key] (value_type& v ) {
+ CPPUNIT_ASSERT_CURRENT( v.first == key );
+ CPPUNIT_ASSERT_CURRENT( v.second.nInsertCall * 1023 == key );
+ }));
+ CPPUNIT_ASSERT(m.size() == i + 1);
+ }
+ CPPUNIT_ASSERT( !m.empty());
+ CPPUNIT_ASSERT(m.size() == capacity);
+
+ // erase_at( reverse_iterator )
+ nCount = 0;
+ for ( auto it = m.rbegin(), itEnd = m.rend(); it != itEnd; ++it ) {
+ CPPUNIT_ASSERT( it->first == it->second.nInsertCall * 1023 );
+ CPPUNIT_ASSERT(m.erase_at( it ));
+ ++nCount;
+ CPPUNIT_ASSERT(!m.contains( it->first ));
+ }
+ CPPUNIT_ASSERT( nCount == capacity );
+ CPPUNIT_ASSERT( m.empty());
+ CPPUNIT_ASSERT(m.size() == 0 );
+
+
+ // extract
+ for ( size_t i = 0; i < capacity; ++i ) {
+ size_t key = i * 711;
+ CPPUNIT_ASSERT(!m.contains(key));
+ auto ret = m.update( key, [i]( value_type& v, value_type * old ) {
+ CPPUNIT_ASSERT_CURRENT( old == nullptr );
+ v.second.nInsertCall = (unsigned int) i;
+ });
+ CPPUNIT_ASSERT( ret.first );
+ CPPUNIT_ASSERT( ret.second );
+ CPPUNIT_ASSERT(m.find(key, [i, key] (value_type& v ) {
+ CPPUNIT_ASSERT_CURRENT( v.first == key );
+ CPPUNIT_ASSERT_CURRENT( v.second.nInsertCall == i );
+ }));
+ CPPUNIT_ASSERT(m.size() == i + 1);
+ }
+ CPPUNIT_ASSERT( !m.empty());
+ CPPUNIT_ASSERT(m.size() == capacity);
+
+ for ( size_t i = capacity; i > 0; --i ) {
+ size_t key = (i-1) * 711;
+ guarded_ptr gp{ m.extract(key) };
+ CPPUNIT_ASSERT( gp );
+ CPPUNIT_ASSERT( gp->first == key );
+ CPPUNIT_ASSERT((*gp).second.nInsertCall == i - 1 );
+ gp = m.extract(key);
+ CPPUNIT_ASSERT( !gp );
+ }
+ CPPUNIT_ASSERT( m.empty());
+ CPPUNIT_ASSERT(m.size() == 0 );
+
+ // clear
+ for ( size_t i = 0; i < capacity; ++i ) {
+ CPPUNIT_ASSERT(!m.contains( i ))
+ CPPUNIT_ASSERT(m.insert( i ));
+ CPPUNIT_ASSERT(m.contains( i ));
+ CPPUNIT_ASSERT(m.size() == i + 1);
+ }
+ CPPUNIT_ASSERT( !m.empty());
+ CPPUNIT_ASSERT(m.size() == capacity );
+
+ m.clear();
+ CPPUNIT_ASSERT( m.empty());
+ CPPUNIT_ASSERT(m.size() == 0 );
+
+
+ CPPUNIT_MSG( m.statistics() );
+ }
+
+ void hp_stdhash();
+ void hp_stdhash_stat();
+ void hp_stdhash_5_3();
+ void hp_stdhash_5_3_stat();
+ void hp_hash128();
+ void hp_hash128_stat();
+ void hp_hash128_4_3();
+ void hp_hash128_4_3_stat();
+
+ void dhp_stdhash();
+ void dhp_stdhash_stat();
+ void dhp_stdhash_5_3();
+ void dhp_stdhash_5_3_stat();
+ void dhp_hash128();
+ void dhp_hash128_stat();
+ void dhp_hash128_4_3();
+ void dhp_hash128_4_3_stat();
+
+ CPPUNIT_TEST_SUITE(MultiLevelHashMapHdrTest)
+ CPPUNIT_TEST(hp_stdhash)
+ CPPUNIT_TEST(hp_stdhash_stat)
+ CPPUNIT_TEST(hp_stdhash_5_3)
+ CPPUNIT_TEST(hp_stdhash_5_3_stat)
+ CPPUNIT_TEST(hp_hash128)
+ CPPUNIT_TEST(hp_hash128_stat)
+ CPPUNIT_TEST(hp_hash128_4_3)
+ CPPUNIT_TEST(hp_hash128_4_3_stat)
+
+ CPPUNIT_TEST(dhp_stdhash)
+ CPPUNIT_TEST(dhp_stdhash_stat)
+ CPPUNIT_TEST(dhp_stdhash_5_3)
+ CPPUNIT_TEST(dhp_stdhash_5_3_stat)
+ CPPUNIT_TEST(dhp_hash128)
+ CPPUNIT_TEST(dhp_hash128_stat)
+ CPPUNIT_TEST(dhp_hash128_4_3)
+ CPPUNIT_TEST(dhp_hash128_4_3_stat)
+ CPPUNIT_TEST_SUITE_END()
+
+ };
+
+} // namespace map
+
+#endif //#ifndef CDSTEST_HDR_MULTILEVEL_HASHMAP_H
--- /dev/null
+//$$CDS-header$$
+
+#include "map/hdr_multilevel_hashmap.h"
+#include <cds/container/multilevel_hashmap_dhp.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace map {
+ namespace {
+ typedef cds::gc::DHP gc_type;
+ } // namespace
+
+ void MultiLevelHashMapHdrTest::dhp_stdhash()
+ {
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item > map_type;
+
+ test_hp<map_type>(4, 2);
+ }
+
+ void MultiLevelHashMapHdrTest::dhp_hash128()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef hash128::make hash;
+ typedef hash128::less less;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 2);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::hash< hash128::make >
+ , co::less< hash128::less >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 2);
+ }
+
+ void MultiLevelHashMapHdrTest::dhp_stdhash_stat()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef cc::multilevel_hashmap::stat<> stat;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 2);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::stat< cc::multilevel_hashmap::stat<>>
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 2);
+ }
+
+ void MultiLevelHashMapHdrTest::dhp_hash128_stat()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef cc::multilevel_hashmap::stat<> stat;
+ typedef hash128::make hash;
+ typedef hash128::cmp compare;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 2);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::stat< cc::multilevel_hashmap::stat<>>
+ , co::hash< hash128::make >
+ , co::compare< hash128::cmp >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 2);
+ }
+
+ void MultiLevelHashMapHdrTest::dhp_stdhash_5_3()
+ {
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item > map_type;
+
+ test_hp<map_type>(5, 3);
+ }
+
+ void MultiLevelHashMapHdrTest::dhp_stdhash_5_3_stat()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef cc::multilevel_hashmap::stat<> stat;
+ typedef cds::backoff::empty back_off;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(5, 3);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::stat< cc::multilevel_hashmap::stat<>>
+ ,co::back_off< cds::backoff::empty >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(5, 3);
+ }
+
+ void MultiLevelHashMapHdrTest::dhp_hash128_4_3()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef hash128::make hash;
+ typedef hash128::less less;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 3);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::hash< hash128::make >
+ , co::less< hash128::less >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 3);
+ }
+
+ void MultiLevelHashMapHdrTest::dhp_hash128_4_3_stat()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef hash128::make hash;
+ typedef hash128::less less;
+ typedef cc::multilevel_hashmap::stat<> stat;
+ typedef co::v::sequential_consistent memory_model;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 3);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::hash< hash128::make >
+ , co::less< hash128::less >
+ , co::stat< cc::multilevel_hashmap::stat<>>
+ , co::memory_model< co::v::sequential_consistent >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 3);
+ }
+
+} // namespace map
+
+CPPUNIT_TEST_SUITE_REGISTRATION(map::MultiLevelHashMapHdrTest);
--- /dev/null
+//$$CDS-header$$
+
+#include "map/hdr_multilevel_hashmap.h"
+#include <cds/container/multilevel_hashmap_hp.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace map {
+ namespace {
+ typedef cds::gc::HP gc_type;
+ } // namespace
+
+ void MultiLevelHashMapHdrTest::hp_stdhash()
+ {
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item > map_type;
+
+ test_hp<map_type>(4, 2);
+ }
+
+ void MultiLevelHashMapHdrTest::hp_hash128()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef hash128::make hash;
+ typedef hash128::less less;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 2);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::hash< hash128::make >
+ , co::less< hash128::less >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 2);
+ }
+
+ void MultiLevelHashMapHdrTest::hp_stdhash_stat()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef cc::multilevel_hashmap::stat<> stat;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 2);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::stat< cc::multilevel_hashmap::stat<>>
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 2);
+ }
+
+ void MultiLevelHashMapHdrTest::hp_hash128_stat()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef cc::multilevel_hashmap::stat<> stat;
+ typedef hash128::make hash;
+ typedef hash128::cmp compare;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 2);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::stat< cc::multilevel_hashmap::stat<>>
+ , co::hash< hash128::make >
+ , co::compare< hash128::cmp >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 2);
+ }
+
+ void MultiLevelHashMapHdrTest::hp_stdhash_5_3()
+ {
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item > map_type;
+
+ test_hp<map_type>(5, 3);
+ }
+
+ void MultiLevelHashMapHdrTest::hp_stdhash_5_3_stat()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef cc::multilevel_hashmap::stat<> stat;
+ typedef cds::backoff::empty back_off;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(5, 3);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::stat< cc::multilevel_hashmap::stat<>>
+ ,co::back_off< cds::backoff::empty >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(5, 3);
+ }
+
+ void MultiLevelHashMapHdrTest::hp_hash128_4_3()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef hash128::make hash;
+ typedef hash128::less less;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 3);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::hash< hash128::make >
+ , co::less< hash128::less >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 3);
+ }
+
+ void MultiLevelHashMapHdrTest::hp_hash128_4_3_stat()
+ {
+ struct traits : public cc::multilevel_hashmap::traits {
+ typedef hash128::make hash;
+ typedef hash128::less less;
+ typedef cc::multilevel_hashmap::stat<> stat;
+ typedef co::v::sequential_consistent memory_model;
+ };
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item, traits > map_type;
+ test_hp<map_type>(4, 3);
+
+ typedef cc::MultiLevelHashMap< gc_type, size_t, Item,
+ typename cc::multilevel_hashmap::make_traits<
+ co::hash< hash128::make >
+ , co::less< hash128::less >
+ , co::stat< cc::multilevel_hashmap::stat<>>
+ , co::memory_model< co::v::sequential_consistent >
+ >::type
+ > map_type2;
+ test_hp<map_type2>(4, 3);
+ }
+
+} // namespace map
+
+CPPUNIT_TEST_SUITE_REGISTRATION(map::MultiLevelHashMapHdrTest);
}
};
- struct item_disposer {
- template <typename Hash>
- void operator()( Item<Hash> * p )
- {
- ++p->nDisposeCount;
- }
- };
-
struct hash128
{
size_t lo;