49677655b960bca1bee32b051733859333536543
[libcds.git] / cds / container / michael_set_nogc.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_CONTAINER_MICHAEL_SET_NOGC_H
4 #define __CDS_CONTAINER_MICHAEL_SET_NOGC_H
5
6 #include <cds/container/details/michael_set_base.h>
7 #include <cds/gc/nogc.h>
8 #include <cds/details/allocator.h>
9
10 namespace cds { namespace container {
11
12     /// Michael's hash set (template specialization for gc::nogc)
13     /** @ingroup cds_nonintrusive_set
14         \anchor cds_nonintrusive_MichaelHashSet_nogc
15
16         This specialization is so-called append-only when no item
17         reclamation may be performed. The class does not support deleting of list item.
18
19         See \ref cds_nonintrusive_MichaelHashSet_hp "MichaelHashSet" for description of template parameters.
20         The template parameter \p OrderedList should be any \p gc::nogc -derived ordered list, for example,
21         \ref cds_nonintrusive_MichaelList_nogc "append-only MichaelList".
22     */
23     template <
24         class OrderedList,
25 #ifdef CDS_DOXYGEN_INVOKED
26         class Traits = michael_set::traits
27 #else
28         class Traits
29 #endif
30     >
31     class MichaelHashSet< cds::gc::nogc, OrderedList, Traits >
32     {
33     public:
34         typedef cds::gc::nogc gc;        ///< Garbage collector
35         typedef OrderedList bucket_type; ///< type of ordered list to be used as a bucket implementation
36         typedef Traits      traits;      ///< Set traits
37
38         typedef typename bucket_type::value_type        value_type;     ///< type of value stored in the list
39         typedef typename bucket_type::key_comparator    key_comparator; ///< key comparison functor
40
41         /// Hash functor for \ref value_type and all its derivatives that you use
42         typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash;
43         typedef typename traits::item_counter  item_counter; ///< Item counter type
44
45         /// Bucket table allocator
46         typedef cds::details::Allocator< bucket_type, typename traits::allocator >  bucket_table_allocator;
47
48     protected:
49         //@cond
50         typedef typename bucket_type::iterator        bucket_iterator;
51         typedef typename bucket_type::const_iterator  bucket_const_iterator;
52         //@endcond
53
54     protected:
55         item_counter    m_ItemCounter;   ///< Item counter
56         hash            m_HashFunctor;   ///< Hash functor
57         bucket_type *   m_Buckets;       ///< bucket table
58
59     private:
60         //@cond
61         const size_t    m_nHashBitmask;
62         //@endcond
63
64     protected:
65         //@cond
66         /// Calculates hash value of \p key
67         template <typename Q>
68         size_t hash_value( const Q& key ) const
69         {
70             return m_HashFunctor( key ) & m_nHashBitmask;
71         }
72
73         /// Returns the bucket (ordered list) for \p key
74         template <typename Q>
75         bucket_type&    bucket( const Q& key )
76         {
77             return m_Buckets[ hash_value( key ) ];
78         }
79         //@endcond
80
81     public:
82         /// Forward iterator
83         /**
84             The forward iterator for Michael's set is based on \p OrderedList forward iterator and has some features:
85             - it has no post-increment operator
86             - it iterates items in unordered fashion
87         */
88         typedef michael_set::details::iterator< bucket_type, false >    iterator;
89
90         /// Const forward iterator
91         /**
92             For iterator's features and requirements see \ref iterator
93         */
94         typedef michael_set::details::iterator< bucket_type, true >     const_iterator;
95
96         /// Returns a forward iterator addressing the first element in a set
97         /**
98             For empty set \code begin() == end() \endcode
99         */
100         iterator begin()
101         {
102             return iterator( m_Buckets[0].begin(), m_Buckets, m_Buckets + bucket_count() );
103         }
104
105         /// Returns an iterator that addresses the location succeeding the last element in a set
106         /**
107             Do not use the value returned by <tt>end</tt> function to access any item.
108             The returned value can be used only to control reaching the end of the set.
109             For empty set \code begin() == end() \endcode
110         */
111         iterator end()
112         {
113             return iterator( m_Buckets[bucket_count() - 1].end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
114         }
115
116         /// Returns a forward const iterator addressing the first element in a set
117         //@{
118         const_iterator begin() const
119         {
120             return get_const_begin();
121         }
122         const_iterator cbegin() const
123         {
124             return get_const_begin();
125         }
126         //@}
127
128         /// Returns an const iterator that addresses the location succeeding the last element in a set
129         //@{
130         const_iterator end() const
131         {
132             return get_const_end();
133         }
134         const_iterator cend() const
135         {
136             return get_const_end();
137         }
138         //@}
139
140     private:
141         //@cond
142         const_iterator get_const_begin() const
143         {
144             return const_iterator( const_cast<bucket_type const&>(m_Buckets[0]).begin(), m_Buckets, m_Buckets + bucket_count() );
145         }
146         const_iterator get_const_end() const
147         {
148             return const_iterator( const_cast<bucket_type const&>(m_Buckets[bucket_count() - 1]).end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
149         }
150         //@endcond
151
152     public:
153         /// Initialize hash set
154         /** @copydetails cds_nonintrusive_MichaelHashSet_hp_ctor
155         */
156         MichaelHashSet(
157             size_t nMaxItemCount,   ///< estimation of max item count in the hash set
158             size_t nLoadFactor      ///< load factor: estimation of max number of items in the bucket
159         ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor ))
160         {
161             // GC and OrderedList::gc must be the same
162             static_assert( std::is_same<gc, typename bucket_type::gc>::value, "GC and OrderedList::gc must be the same");
163
164             // atomicity::empty_item_counter is not allowed as a item counter
165             static_assert( !std::is_same<item_counter, atomicity::empty_item_counter>::value,
166                            "cds::atomicity::empty_item_counter is not allowed as a item counter");
167
168             m_Buckets = bucket_table_allocator().NewArray( bucket_count() );
169         }
170
171         /// Clears hash set and destroys it
172         ~MichaelHashSet()
173         {
174             clear();
175             bucket_table_allocator().Delete( m_Buckets, bucket_count() );
176         }
177
178         /// Inserts new node
179         /**
180             The function inserts \p val in the set if it does not contain
181             an item with key equal to \p val.
182
183             Return an iterator pointing to inserted item if success, otherwise \ref end()
184         */
185         template <typename Q>
186         iterator insert( const Q& val )
187         {
188             bucket_type& refBucket = bucket( val );
189             bucket_iterator it = refBucket.insert( val );
190
191             if ( it != refBucket.end() ) {
192                 ++m_ItemCounter;
193                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
194             }
195
196             return end();
197         }
198
199         /// Inserts data of type \ref value_type constructed with <tt>std::forward<Args>(args)...</tt>
200         /**
201             Return an iterator pointing to inserted item if success \ref end() otherwise
202         */
203         template <typename... Args>
204         iterator emplace( Args&&... args )
205         {
206             bucket_type& refBucket = bucket( value_type(std::forward<Args>(args)...));
207             bucket_iterator it = refBucket.emplace( std::forward<Args>(args)... );
208
209             if ( it != refBucket.end() ) {
210                 ++m_ItemCounter;
211                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
212             }
213
214             return end();
215         }
216
217         /// Ensures that the item \p val exists in the set
218         /**
219             The operation inserts new item if the key \p val is not found in the set.
220             Otherwise, the function returns an iterator that points to item found.
221
222             Returns <tt> std::pair<iterator, bool> </tt> where \p first is an iterator pointing to
223             item found or inserted, \p second is true if new item has been added or \p false if the item
224             already is in the set.
225
226             @warning For \ref cds_nonintrusive_MichaelList_nogc "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
227             \ref cds_nonintrusive_LazyList_nogc "LazyList" provides exclusive access to inserted item and does not require any node-level
228             synchronization.
229         */
230         template <typename Q>
231         std::pair<iterator, bool> ensure( const Q& val )
232         {
233             bucket_type& refBucket = bucket( val );
234             std::pair<bucket_iterator, bool> ret = refBucket.ensure( val );
235
236             if ( ret.first != refBucket.end() ) {
237                 if ( ret.second )
238                     ++m_ItemCounter;
239                 return std::make_pair( iterator( ret.first, &refBucket, m_Buckets + bucket_count() ), ret.second );
240             }
241
242             return std::make_pair( end(), ret.second );
243         }
244
245         /// Find the key \p key
246         /** \anchor cds_nonintrusive_MichealSet_nogc_find
247             The function searches the item with key equal to \p key
248             and returns an iterator pointed to item found if the key is found,
249             and \ref end() otherwise
250         */
251         template <typename Q>
252         iterator find( Q const& key )
253         {
254             bucket_type& refBucket = bucket( key );
255             bucket_iterator it = refBucket.find( key );
256             if ( it != refBucket.end() )
257                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
258
259             return end();
260         }
261
262         /// Finds the key \p val using \p pred predicate for searching
263         /**
264             The function is an analog of \ref cds_nonintrusive_MichealSet_nogc_find "find(Q const&)"
265             but \p pred is used for key comparing.
266             \p Less functor has the interface like \p std::less.
267             \p Less must imply the same element order as the comparator used for building the set.
268         */
269         template <typename Q, typename Less>
270         iterator find_with( Q const& key, Less pred )
271         {
272             bucket_type& refBucket = bucket( key );
273             bucket_iterator it = refBucket.find_with( key, pred );
274             if ( it != refBucket.end() )
275                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
276
277             return end();
278         }
279
280         /// Clears the set (not atomic)
281         void clear()
282         {
283             for ( size_t i = 0; i < bucket_count(); ++i )
284                 m_Buckets[i].clear();
285             m_ItemCounter.reset();
286         }
287
288         /// Checks if the set is empty
289         /**
290             The emptiness is checked by the item counting: if item count is zero then the set is empty.
291             Thus, the correct item counting feature is an important part of Michael's set implementation.
292         */
293         bool empty() const
294         {
295             return size() == 0;
296         }
297
298         /// Returns item count in the set
299         size_t size() const
300         {
301             return m_ItemCounter;
302         }
303
304         /// Returns the size of hash table
305         /**
306             Since \p %MichaelHashSet cannot dynamically extend the hash table size,
307             the value returned is an constant depending on object initialization parameters;
308             see MichaelHashSet::MichaelHashSet for explanation.
309         */
310         size_t bucket_count() const
311         {
312             return m_nHashBitmask + 1;
313         }
314     };
315
316 }} // cds::container
317
318 #endif // ifndef __CDS_CONTAINER_MICHAEL_SET_NOGC_H