8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 17:23:04 +01:00
firebird-mirror/extern/libcds/cds/container/impl/feldman_hashmap.h
2022-10-08 20:46:39 +03:00

823 lines
35 KiB
C++

// Copyright (c) 2006-2018 Maxim Khizhinsky
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef CDSLIB_CONTAINER_IMPL_FELDMAN_HASHMAP_H
#define CDSLIB_CONTAINER_IMPL_FELDMAN_HASHMAP_H
#include <cds/intrusive/impl/feldman_hashset.h>
#include <cds/container/details/feldman_hashmap_base.h>
#include <cds/container/details/guarded_ptr_cast.h>
namespace cds { namespace container {
/// Hash map based on multi-level array
/** @ingroup cds_nonintrusive_map
@anchor cds_container_FeldmanHashMap_hp
Source:
- [2013] Steven Feldman, Pierre LaBorde, Damian Dechev "Concurrent Multi-level Arrays:
Wait-free Extensible Hash Maps"
[From the paper] The hardest problem encountered while developing a parallel hash map is how to perform
a global resize, the process of redistributing the elements in a hash map that occurs when adding new
buckets. The negative impact of blocking synchronization is multiplied during a global resize, because all
threads will be forced to wait on the thread that is performing the involved process of resizing the hash map
and redistributing the elements. \p %FeldmanHashSet implementation avoids global resizes through new array
allocation. By allowing concurrent expansion this structure is free from the overhead of an explicit resize,
which facilitates concurrent operations.
The presented design includes dynamic hashing, the use of sub-arrays within the hash map data structure;
which, in combination with <b>perfect hashing</b>, means that each element has a unique final, as well as current, position.
It is important to note that the perfect hash function required by our hash map is trivial to realize as
any hash function that permutes the bits of the key is suitable. This is possible because of our approach
to the hash function; we require that it produces hash values that are equal in size to that of the key.
We know that if we expand the hash map a fixed number of times there can be no collision as duplicate keys
are not provided for in the standard semantics of a hash map.
\p %FeldmanHashMap is a multi-level array which has an internal structure similar to a tree:
@image html feldman_hashset.png
The multi-level array differs from a tree in that each position on the tree could hold an array of nodes or a single node.
A position that holds a single node is a \p dataNode which holds the hash value of a key and the value that is associated
with that key; it is a simple struct holding two variables. A \p dataNode in the multi-level array could be marked.
A \p markedDataNode refers to a pointer to a \p dataNode that has been bitmarked at the least significant bit (LSB)
of the pointer to the node. This signifies that this \p dataNode is contended. An expansion must occur at this node;
any thread that sees this \p markedDataNode will try to replace it with an \p arrayNode; which is a position that holds
an array of nodes. The pointer to an \p arrayNode is differentiated from that of a pointer to a \p dataNode by a bitmark
on the second-least significant bit.
\p %FeldmanHashMap multi-level array is similar to a tree in that we keep a pointer to the root, which is a memory array
called \p head. The length of the \p head memory array is unique, whereas every other \p arrayNode has a uniform length;
a normal \p arrayNode has a fixed power-of-two length equal to the binary logarithm of a variable called \p arrayLength.
The maximum depth of the tree, \p maxDepth, is the maximum number of pointers that must be followed to reach any node.
We define \p currentDepth as the number of memory arrays that we need to traverse to reach the \p arrayNode on which
we need to operate; this is initially one, because of \p head.
That approach to the structure of the hash map uses an extensible hashing scheme; <b> the hash value is treated as a bit
string</b> and rehash incrementally.
@note Two important things you should keep in mind when you're using \p %FeldmanHashMap:
- all keys is converted to fixed-size bit-string by hash functor provided.
You can use variable-length keys, for example, \p std::string as a key for \p %FeldmanHashMap,
but real key in the map will be fixed-size hash values of your keys.
For the strings you may use well-known hashing algorithms like <a href="https://en.wikipedia.org/wiki/Secure_Hash_Algorithm">SHA1, SHA2</a>,
<a href="https://en.wikipedia.org/wiki/MurmurHash">MurmurHash</a>, <a href="https://en.wikipedia.org/wiki/CityHash">CityHash</a>
or its successor <a href="https://code.google.com/p/farmhash/">FarmHash</a> and so on, which
converts variable-length strings to fixed-length bit-strings, and such hash values will be the keys in \p %FeldmanHashMap.
If your key is fixed-sized the hash functor is optional, see \p feldman_hashmap::traits::hash for explanation and examples.
- \p %FeldmanHashMap uses a perfect hashing. It means that if two different keys, for example, of type \p std::string,
have identical hash then you cannot insert both that keys in the map. \p %FeldmanHashMap does not maintain the key,
it maintains its fixed-size hash value.
The map supports @ref cds_container_FeldmanHashMap_iterators "bidirectional thread-safe iterators".
Template parameters:
- \p GC - safe memory reclamation schema. Can be \p gc::HP, \p gc::DHP or one of \ref cds_urcu_type "RCU type"
- \p Key - a key type to be stored in the map
- \p T - a value type to be stored in the map
- \p Traits - type traits, the structure based on \p feldman_hashmap::traits or result of \p feldman_hashmap::make_traits metafunction.
There are several specializations of \p %FeldmanHashMap for each \p GC. You should include:
- <tt><cds/container/feldman_hashmap_hp.h></tt> for \p gc::HP garbage collector
- <tt><cds/container/feldman_hashmap_dhp.h></tt> for \p gc::DHP garbage collector
- <tt><cds/container/feldman_hashmap_rcu.h></tt> for \ref cds_container_FeldmanHashMap_rcu "RCU type". RCU specialization
has a slightly different interface.
*/
template <
class GC
,typename Key
,typename T
#ifdef CDS_DOXYGEN_INVOKED
,class Traits = feldman_hashmap::traits
#else
,class Traits
#endif
>
class FeldmanHashMap
#ifdef CDS_DOXYGEN_INVOKED
: protected cds::intrusive::FeldmanHashSet< GC, std::pair<Key const, T>, Traits >
#else
: protected cds::container::details::make_feldman_hashmap< GC, Key, T, Traits >::type
#endif
{
//@cond
typedef cds::container::details::make_feldman_hashmap< GC, Key, T, Traits > maker;
typedef typename maker::type base_class;
//@endcond
public:
typedef GC gc; ///< Garbage collector
typedef Key key_type; ///< Key type
typedef T mapped_type; ///< Mapped type
typedef std::pair< key_type const, mapped_type> value_type; ///< Key-value pair to be stored in the map
typedef Traits traits; ///< Map traits
#ifdef CDS_DOXYGEN_INVOKED
typedef typename traits::hash hasher; ///< Hash functor, see \p feldman_hashmap::traits::hash
#else
typedef typename maker::hasher hasher;
#endif
typedef typename maker::hash_type hash_type; ///< Hash type deduced from \p hasher return type
typedef typename base_class::hash_comparator hash_comparator; ///< hash compare functor based on \p Traits::compare and \p Traits::less
typedef typename traits::item_counter item_counter; ///< Item counter type
typedef typename traits::allocator allocator; ///< Element allocator
typedef typename traits::node_allocator node_allocator; ///< Array node allocator
typedef typename traits::memory_model memory_model; ///< Memory model
typedef typename traits::back_off back_off; ///< Backoff strategy
typedef typename traits::stat stat; ///< Internal statistics type
/// Count of hazard pointers required
static constexpr size_t const c_nHazardPtrCount = base_class::c_nHazardPtrCount;
/// The size of \p hash_type in bytes, see \p feldman_hashmap::traits::hash_size for explanation
static constexpr size_t const c_hash_size = base_class::c_hash_size;
/// Level statistics
typedef feldman_hashmap::level_statistics level_statistics;
protected:
//@cond
typedef typename maker::node_type node_type;
typedef typename maker::cxx_node_allocator cxx_node_allocator;
typedef std::unique_ptr< node_type, typename maker::node_disposer > scoped_node_ptr;
template <bool IsConst>
class bidirectional_iterator: public base_class::iterator_base
{
friend class FeldmanHashMap;
typedef typename base_class::iterator_base iterator_base;
protected:
static 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() noexcept
{}
bidirectional_iterator( bidirectional_iterator const& rhs ) noexcept
: iterator_base( rhs )
{}
bidirectional_iterator& operator=( bidirectional_iterator const& rhs ) 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 noexcept
{
node_type * p = iterator_base::pointer();
return p ? &p->m_Value : nullptr;
}
value_ref operator *() const noexcept
{
node_type * p = iterator_base::pointer();
assert( p );
return p->m_Value;
}
void release()
{
iterator_base::release();
}
template <bool IsConst2>
bool operator ==( bidirectional_iterator<IsConst2> const& rhs ) const noexcept
{
return iterator_base::operator==( rhs );
}
template <bool IsConst2>
bool operator !=( bidirectional_iterator<IsConst2> const& rhs ) const noexcept
{
return !( *this == rhs );
}
public: // for internal use only!
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 FeldmanHashMap;
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() noexcept
: iterator_base()
{}
reverse_bidirectional_iterator( reverse_bidirectional_iterator const& rhs ) noexcept
: iterator_base( rhs )
{}
reverse_bidirectional_iterator& operator=( reverse_bidirectional_iterator const& rhs) noexcept
{
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 noexcept
{
node_type * p = iterator_base::pointer();
return p ? &p->m_Value : nullptr;
}
value_ref operator *() const noexcept
{
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 );
}
public: // for internal use only!
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:
#ifdef CDS_DOXYGEN_INVOKED
/// Guarded pointer
typedef typename gc::template guarded_ptr< value_type > guarded_ptr;
#else
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 iterator; ///< @ref cds_container_FeldmanHashMap_iterators "bidirectional iterator" type
typedef implementation_defined const_iterator; ///< @ref cds_container_FeldmanHashMap_iterators "bidirectional const iterator" type
typedef implementation_defined reverse_iterator; ///< @ref cds_container_FeldmanHashMap_iterators "bidirectional reverse iterator" type
typedef implementation_defined const_reverse_iterator; ///< @ref cds_container_FeldmanHashMap_iterators "bidirectional reverse const iterator" type
#else
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:
//@cond
hasher m_Hasher;
//@endcond
public:
/// Creates empty map
/**
@param head_bits - 2<sup>head_bits</sup> specifies the size of head array, minimum is 4.
@param array_bits - 2<sup>array_bits</sup> specifies the size of array node, minimum is 2.
Equation for \p head_bits and \p array_bits:
\code
c_hash_size * 8 == head_bits + N * array_bits
\endcode
where \p N is multi-level array depth.
*/
FeldmanHashMap( size_t head_bits = 8, size_t array_bits = 4 )
: base_class( head_bits, array_bits )
{}
/// Destructs the map and frees all data
~FeldmanHashMap()
{}
/// Inserts new element with key and default value
/**
The function creates an element with \p key and default value, and then inserts the node created into the map.
Preconditions:
- The \p key_type should be constructible from a value of type \p K.
In trivial case, \p K is equal to \p key_type.
- The \p mapped_type should be default-constructible.
Returns \p true if inserting successful, \p false otherwise.
*/
template <typename K>
bool insert( K&& key )
{
scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>( key )));
if ( base_class::insert( *sp )) {
sp.release();
return true;
}
return false;
}
/// Inserts new element
/**
The function creates a node with copy of \p val value
and then inserts the node created into the map.
Preconditions:
- The \p key_type should be constructible from \p key of type \p K.
- The \p value_type should be constructible from \p val of type \p V.
Returns \p true if \p val is inserted into the map, \p false otherwise.
*/
template <typename K, typename V>
bool insert( K&& key, V&& 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;
}
return false;
}
/// Inserts new element and initialize it by a functor
/**
This function inserts new element with key \p key and if inserting is successful then it calls
\p func functor with signature
\code
struct functor {
void operator()( value_type& item );
};
\endcode
The argument \p item of user-defined functor \p func is the reference
to the map's item inserted:
- <tt>item.first</tt> is a const reference to item's key that cannot be changed.
- <tt>item.second</tt> is a reference to item's value that may be changed.
\p key_type should be constructible from value of type \p K.
The function allows to split creating of new item into two part:
- create item from \p key;
- insert new item into the map;
- if inserting is successful, initialize the value of item by calling \p func functor
This can be useful if complete initialization of object of \p value_type is heavyweight and
it is preferable that the initialization should be completed only if inserting is successful.
*/
template <typename K, typename Func>
bool insert_with( K&& key, Func func )
{
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;
}
return false;
}
/// For key \p key inserts data of type \p value_type created in-place from <tt>std::forward<Args>(args)...</tt>
/**
Returns \p true if inserting successful, \p false otherwise.
*/
template <typename K, typename... Args>
bool emplace( K&& key, 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;
}
return false;
}
/// Updates data by \p key
/**
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, it is replaced with a new item created from
\p key.
The functor \p Func signature:
\code
struct my_functor {
void operator()( value_type& item, value_type * old );
};
\endcode
where:
- \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.
Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successful,
\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&& key, Func func, bool bInsert = true )
{
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;
}
/// Delete \p key from the map
/**
\p key_type must be constructible from value of type \p K.
The function deletes the element with hash value equal to <tt>hash( key_type( key ))</tt>
Return \p true if \p key is found and deleted, \p false otherwise.
*/
template <typename K>
bool erase( K const& key )
{
return base_class::erase( m_Hasher( key_type( key )));
}
/// Delete \p key from the map
/**
The function searches an item with hash value equal to <tt>hash( key_type( key ))</tt>,
calls \p f functor and deletes the item. If \p key is not found, the functor is not called.
The functor \p Func interface:
\code
struct extractor {
void operator()( value_type& item ) { ... }
};
\endcode
where \p item is the element found.
\p key_type must be constructible from value of type \p K.
Return \p true if key is found and deleted, \p false otherwise
*/
template <typename K, typename Func>
bool erase( K const& key, Func f )
{
return base_class::erase( m_Hasher( key_type( key )), [&f]( node_type& node) { f( node.m_Value ); } );
}
/// Deletes the element pointed by iterator \p iter
/**
Returns \p true if the operation is successful, \p false otherwise.
The function does not invalidate the iterator, it remains valid and can be used for further traversing.
*/
bool erase_at( iterator const& iter )
{
return base_class::do_erase_at( iter );
}
//@cond
bool erase_at( reverse_iterator const& iter )
{
return base_class::do_erase_at( iter );
}
bool erase_at( const_iterator const& iter )
{
return base_class::do_erase_at( iter );
}
bool erase_at( const_reverse_iterator const& iter )
{
return base_class::do_erase_at( iter );
}
//@endcond
/// Extracts the item from the map with specified \p key
/**
The function searches an item with key equal to <tt>hash( key_type( key ))</tt> in the map,
unlinks it from the map, and returns a guarded pointer to the item found.
If \p key is not found the function returns an empty guarded pointer.
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.
Usage:
\code
typedef cds::container::FeldmanHashMap< cds::gc::HP, int, foo, my_traits > map_type;
map_type theMap;
// ...
{
map_type::guarded_ptr gp( theMap.extract( 5 ));
if ( gp ) {
// Deal with gp
// ...
}
// Destructor of gp releases internal HP guard and frees the pointer
}
\endcode
*/
template <typename K>
guarded_ptr extract( K const& key )
{
return base_class::extract( m_Hasher( key_type( key )));
}
/// 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>
and returns \p true if it is found, or \p false otherwise.
*/
template <typename K>
bool contains( K const& key )
{
return base_class::contains( m_Hasher( key_type( key )));
}
/// Find the key \p key
/**
The function searches the item by its hash that is equal to <tt>hash( key_type( key ))</tt>
and calls the functor \p f for item found.
The interface of \p Func functor is:
\code
struct functor {
void operator()( value_type& item );
};
\endcode
where \p item is the item found.
The functor may change \p item.second.
The function returns \p true if \p key is found, \p false otherwise.
*/
template <typename K, typename Func>
bool find( K const& key, Func f )
{
return base_class::find( m_Hasher( key_type( key )), [&f]( node_type& node ) { f( node.m_Value );});
}
/// Finds the key \p key and return the item found
/**
The function searches the item with a hash equal to <tt>hash( key_type( key ))</tt>
and returns a guarded pointer to the item found.
If \p key is not found the function returns an empty guarded pointer.
It is safe when a concurrent thread erases the item returned as \p guarded_ptr.
In this case the item will be freed later by garbage collector \p GC automatically
when \p guarded_ptr object will be destroyed or released.
@note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
Usage:
\code
typedef cds::container::FeldmanHashMap< cds::gc::HP, int, foo, my_traits > map_type;
map_type theMap;
// ...
{
map_type::guarded_ptr gp( theMap.get( 5 ));
if ( gp ) {
// Deal with gp
//...
}
// Destructor of guarded_ptr releases internal HP guard
}
\endcode
*/
template <typename K>
guarded_ptr get( K const& key )
{
return base_class::get( m_Hasher( key_type( key )));
}
/// Clears the map (non-atomic)
/**
The function unlink all data node from the map.
The function is not atomic but is thread-safe.
After \p %clear() the map may not be empty because another threads may insert items.
*/
void clear()
{
base_class::clear();
}
/// Checks if the map is empty
/**
Emptiness is checked by item counting: if item count is zero then the map is empty.
Thus, the correct item counting feature is an important part of the map implementation.
*/
bool empty() const
{
return base_class::empty();
}
/// Returns item count in the map
size_t size() const
{
return base_class::size();
}
/// Returns const reference to internal statistics
stat const& statistics() const
{
return base_class::statistics();
}
/// Returns the size of head node
size_t head_size() const
{
return base_class::head_size();
}
/// Returns the size of the array node
size_t array_node_size() const
{
return base_class::array_node_size();
}
/// Collects tree level statistics into \p stat
/**
The function traverses the set and collects statistics for each level of the tree
into \p feldman_hashset::level_statistics struct. The element of \p stat[i]
represents statistics for level \p i, level 0 is head array.
The function is thread-safe and may be called in multi-threaded environment.
Result can be useful for estimating efficiency of hash functor you use.
*/
void get_level_statistics( std::vector< feldman_hashmap::level_statistics>& stat) const
{
base_class::get_level_statistics( stat );
}
public:
///@name Thread-safe iterators
/** @anchor cds_container_FeldmanHashMap_iterators
The map supports thread-safe iterators: you may iterate over the map in multi-threaded environment.
It is guaranteed that the iterators will remain valid even if another thread deletes the node the iterator points to:
Hazard Pointer embedded into the iterator object protects the node from physical reclamation.
@note Since the iterator object contains hazard pointer that is a thread-local resource,
the iterator should not be passed to another thread.
Each iterator object supports the common interface:
- dereference operators:
@code
value_type [const] * operator ->() noexcept
value_type [const] & operator *() noexcept
@endcode
- pre-increment and pre-decrement. Post-operators is not supported
- equality operators <tt>==</tt> and <tt>!=</tt>.
Iterators are equal iff they point to the same cell of the same array node.
Note that for two iterators \p it1 and \p it2, the conditon <tt> it1 == it2 </tt>
does not entail <tt> &(*it1) == &(*it2) </tt>
- helper member function \p release() that clears internal hazard pointer.
After \p release() the iterator points to \p nullptr but it still remain valid: further iterating is possible.
During iteration you may safely erase any item from the set;
@ref erase_at() function call doesn't invalidate any iterator.
If some iterator points to the item to be erased, that item is not deleted immediately
but only after that iterator will be advanced forward or backward.
@note It is possible the item can be iterated more that once, for example, if an iterator points to the item
in array node that is being splitted.
*/
///@{
/// Returns an iterator to the beginning of the map
iterator begin()
{
return base_class::template init_begin<iterator>();
}
/// Returns an const iterator to the beginning of the map
const_iterator begin() const
{
return base_class::template init_begin<const_iterator>();
}
/// Returns an const iterator to the beginning of the map
const_iterator 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 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 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 base_class::template init_end<const_iterator>();
}
/// Returns a reverse iterator to the first element of the reversed map
reverse_iterator 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 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 base_class::template init_rbegin<const_reverse_iterator>();
}
/// Returns a reverse iterator to the element following the last element of the reversed map
/**
It corresponds to the element preceding the first element of the non-reversed container.
This element acts as a placeholder, attempting to access it results in undefined behavior.
*/
reverse_iterator 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
/**
It corresponds to the element preceding the first element of the non-reversed container.
This element acts as a placeholder, attempting to access it results in undefined behavior.
*/
const_reverse_iterator rend() const
{
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
/**
It corresponds to the element preceding the first element of the non-reversed container.
This element acts as a placeholder, attempting to access it results in undefined behavior.
*/
const_reverse_iterator crend()
{
return base_class::template init_rend<const_reverse_iterator>();
}
///@}
};
}} // namespace cds::container
#endif // #ifndef CDSLIB_CONTAINER_IMPL_FELDMAN_HASHMAP_H