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