Added copyright and license
[libcds.git] / cds / container / michael_map_nogc.h
1 /*
2     This file is a part of libcds - Concurrent Data Structures library
3
4     (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
5
6     Source code repo: http://github.com/khizmax/libcds/
7     Download: http://sourceforge.net/projects/libcds/files/
8     
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions are met:
11
12     * Redistributions of source code must retain the above copyright notice, this
13       list of conditions and the following disclaimer.
14
15     * Redistributions in binary form must reproduce the above copyright notice,
16       this list of conditions and the following disclaimer in the documentation
17       and/or other materials provided with the distribution.
18
19     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.     
29 */
30
31 #ifndef CDSLIB_CONTAINER_MICHAEL_MAP_NOGC_H
32 #define CDSLIB_CONTAINER_MICHAEL_MAP_NOGC_H
33
34 #include <cds/container/details/michael_map_base.h>
35 #include <cds/gc/nogc.h>
36 #include <cds/details/allocator.h>
37
38 namespace cds { namespace container {
39
40     /// Michael's hash map (template specialization for \p cds::gc::nogc)
41     /** @ingroup cds_nonintrusive_map
42         \anchor cds_nonintrusive_MichaelHashMap_nogc
43
44         This specialization is so-called append-only when no item
45         reclamation may be performed. The class does not support deleting of map item.
46
47         See @ref cds_nonintrusive_MichaelHashMap_hp "MichaelHashMap" for description of template parameters.
48     */
49     template <
50         class OrderedList,
51 #ifdef CDS_DOXYGEN_INVOKED
52         class Traits = michael_map::traits
53 #else
54         class Traits
55 #endif
56     >
57     class MichaelHashMap<cds::gc::nogc, OrderedList, Traits>
58     {
59     public:
60         typedef cds::gc::nogc gc;        ///< No garbage collector
61         typedef OrderedList bucket_type; ///< type of ordered list used as a bucket implementation
62         typedef Traits      traits;      ///< Map traits
63
64         typedef typename bucket_type::key_type    key_type;    ///< key type
65         typedef typename bucket_type::mapped_type mapped_type; ///< type of value to be stored in the map
66         typedef typename bucket_type::value_type  value_type;  ///< Pair used as the some functor's argument
67
68         typedef typename bucket_type::key_comparator key_comparator;   ///< key comparing functor
69
70         /// Hash functor for \ref key_type and all its derivatives that you use
71         typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash;
72         typedef typename traits::item_counter item_counter;   ///< Item counter type
73
74         /// Bucket table allocator
75         typedef cds::details::Allocator< bucket_type, typename traits::allocator >  bucket_table_allocator;
76
77     protected:
78         //@cond
79         typedef typename bucket_type::iterator       bucket_iterator;
80         typedef typename bucket_type::const_iterator bucket_const_iterator;
81         //@endcond
82
83     protected:
84         item_counter    m_ItemCounter; ///< Item counter
85         hash            m_HashFunctor; ///< Hash functor
86         bucket_type *   m_Buckets;     ///< bucket table
87
88     private:
89         //@cond
90         const size_t    m_nHashBitmask;
91         //@endcond
92
93     protected:
94         //@cond
95         /// Calculates hash value of \p key
96         size_t hash_value( key_type const & key ) const
97         {
98             return m_HashFunctor( key ) & m_nHashBitmask;
99         }
100
101         /// Returns the bucket (ordered list) for \p key
102         bucket_type&    bucket( key_type const& key )
103         {
104             return m_Buckets[ hash_value( key ) ];
105         }
106         //@endcond
107
108     protected:
109         /// Forward iterator
110         /**
111             \p IsConst - constness boolean flag
112
113             The forward iterator for Michael's map is based on \p OrderedList forward iterator and has some features:
114             - it has no post-increment operator, only pre-increment
115             - it iterates items in unordered fashion
116         */
117         template <bool IsConst>
118         class iterator_type: private cds::intrusive::michael_set::details::iterator< bucket_type, IsConst >
119         {
120             //@cond
121             typedef cds::intrusive::michael_set::details::iterator< bucket_type, IsConst >  base_class;
122             friend class MichaelHashMap;
123             //@endcond
124
125         protected:
126             //@cond
127             //typedef typename base_class::bucket_type    bucket_type;
128             typedef typename base_class::bucket_ptr     bucket_ptr;
129             typedef typename base_class::list_iterator  list_iterator;
130
131             //typedef typename bucket_type::key_type      key_type;
132             //@endcond
133
134         public:
135             /// Value pointer type (const for const_iterator)
136             typedef typename cds::details::make_const_type<typename MichaelHashMap::mapped_type, IsConst>::pointer   value_ptr;
137             /// Value reference type (const for const_iterator)
138             typedef typename cds::details::make_const_type<typename MichaelHashMap::mapped_type, IsConst>::reference value_ref;
139
140             /// Key-value pair pointer type (const for const_iterator)
141             typedef typename cds::details::make_const_type<typename MichaelHashMap::value_type, IsConst>::pointer   pair_ptr;
142             /// Key-value pair reference type (const for const_iterator)
143             typedef typename cds::details::make_const_type<typename MichaelHashMap::value_type, IsConst>::reference pair_ref;
144
145         protected:
146             //@cond
147             iterator_type( list_iterator const& it, bucket_ptr pFirst, bucket_ptr pLast )
148                 : base_class( it, pFirst, pLast )
149             {}
150             //@endcond
151
152         public:
153             /// Default ctor
154             iterator_type()
155                 : base_class()
156             {}
157
158             /// Copy ctor
159             iterator_type( const iterator_type& src )
160                 : base_class( src )
161             {}
162
163             /// Dereference operator
164             pair_ptr operator ->() const
165             {
166                 assert( base_class::m_pCurBucket != nullptr );
167                 return base_class::m_itList.operator ->();
168             }
169
170             /// Dereference operator
171             pair_ref operator *() const
172             {
173                 assert( base_class::m_pCurBucket != nullptr );
174                 return base_class::m_itList.operator *();
175             }
176
177             /// Pre-increment
178             iterator_type& operator ++()
179             {
180                 base_class::operator++();
181                 return *this;
182             }
183
184             /// Assignment operator
185             iterator_type& operator = (const iterator_type& src)
186             {
187                 base_class::operator =(src);
188                 return *this;
189             }
190
191             /// Returns current bucket (debug function)
192             bucket_ptr bucket() const
193             {
194                 return base_class::bucket();
195             }
196
197             /// Equality operator
198             template <bool C>
199             bool operator ==(iterator_type<C> const& i ) const
200             {
201                 return base_class::operator ==( i );
202             }
203             /// Equality operator
204             template <bool C>
205             bool operator !=(iterator_type<C> const& i ) const
206             {
207                 return !( *this == i );
208             }
209         };
210
211
212     public:
213         /// Forward iterator
214         typedef iterator_type< false >    iterator;
215
216         /// Const forward iterator
217         typedef iterator_type< true >     const_iterator;
218
219         /// Returns a forward iterator addressing the first element in a set
220         /**
221             For empty set \code begin() == end() \endcode
222         */
223         iterator begin()
224         {
225             return iterator( m_Buckets[0].begin(), m_Buckets, m_Buckets + bucket_count() );
226         }
227
228         /// Returns an iterator that addresses the location succeeding the last element in a set
229         /**
230             Do not use the value returned by <tt>end</tt> function to access any item.
231             The returned value can be used only to control reaching the end of the set.
232             For empty set \code begin() == end() \endcode
233         */
234         iterator end()
235         {
236             return iterator( m_Buckets[bucket_count() - 1].end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
237         }
238
239         /// Returns a forward const iterator addressing the first element in a set
240         //@{
241         const_iterator begin() const
242         {
243             return get_const_begin();
244         }
245         const_iterator cbegin() const
246         {
247             return get_const_begin();
248         }
249         //@}
250
251         /// Returns an const iterator that addresses the location succeeding the last element in a set
252         //@{
253         const_iterator end() const
254         {
255             return get_const_end();
256         }
257         const_iterator cend() const
258         {
259             return get_const_end();
260         }
261         //@}
262
263     private:
264         //@cond
265         const_iterator get_const_begin() const
266         {
267             return const_iterator( const_cast<bucket_type const&>(m_Buckets[0]).begin(), m_Buckets, m_Buckets + bucket_count() );
268         }
269         const_iterator get_const_end() const
270         {
271             return const_iterator( const_cast<bucket_type const&>(m_Buckets[bucket_count() - 1]).end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
272         }
273         //@endcond
274
275     public:
276         /// Initialize the map
277         /**
278             The Michael's hash map is non-expandable container. You should point the average count of items \p nMaxItemCount
279             when you create an object.
280             \p nLoadFactor parameter defines average count of items per bucket and it should be small number between 1 and 10.
281             Remember, since the bucket implementation is an ordered list, searching in the bucket is linear [<tt>O(nLoadFactor)</tt>].
282             Note, that many popular STL hash map implementation uses load factor 1.
283
284             The ctor defines hash table size as rounding <tt>nMacItemCount / nLoadFactor</tt> up to nearest power of two.
285         */
286         MichaelHashMap(
287             size_t nMaxItemCount,   ///< estimation of max item count in the hash set
288             size_t nLoadFactor      ///< load factor: estimation of max number of items in the bucket
289         ) : m_nHashBitmask( michael_map::details::init_hash_bitmask( nMaxItemCount, nLoadFactor ))
290         {
291             // GC and OrderedList::gc must be the same
292             static_assert( std::is_same<gc, typename bucket_type::gc>::value, "GC and OrderedList::gc must be the same");
293
294             // atomicity::empty_item_counter is not allowed as a item counter
295             static_assert( !std::is_same<item_counter, atomicity::empty_item_counter>::value,
296                            "cds::atomicity::empty_item_counter is not allowed as a item counter");
297
298             m_Buckets = bucket_table_allocator().NewArray( bucket_count() );
299         }
300
301         /// Clears hash set and destroys it
302         ~MichaelHashMap()
303         {
304             clear();
305             bucket_table_allocator().Delete( m_Buckets, bucket_count() );
306         }
307
308         /// Inserts new node with key and default value
309         /**
310             The function creates a node with \p key and default value, and then inserts the node created into the map.
311
312             Preconditions:
313             - The \ref key_type should be constructible from value of type \p K.
314                 In trivial case, \p K is equal to \ref key_type.
315             - The \ref mapped_type should be default-constructible.
316
317             Returns an iterator pointed to inserted value, or \p end() if inserting is failed
318         */
319         template <typename K>
320         iterator insert( const K& key )
321         {
322             bucket_type& refBucket = bucket( key );
323             bucket_iterator it = refBucket.insert( key );
324
325             if ( it != refBucket.end() ) {
326                 ++m_ItemCounter;
327                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
328             }
329
330             return end();
331         }
332
333         /// Inserts new node
334         /**
335             The function creates a node with copy of \p val value
336             and then inserts the node created into the map.
337
338             Preconditions:
339             - The \ref key_type should be constructible from \p key of type \p K.
340             - The \ref mapped_type should be constructible from \p val of type \p V.
341
342             Returns an iterator pointed to inserted value, or \p end() if inserting is failed
343         */
344         template <typename K, typename V>
345         iterator insert( K const& key, V const& val )
346         {
347             bucket_type& refBucket = bucket( key );
348             bucket_iterator it = refBucket.insert( key, val );
349
350             if ( it != refBucket.end() ) {
351                 ++m_ItemCounter;
352                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
353             }
354
355             return end();
356         }
357
358         /// Inserts new node and initialize it by a functor
359         /**
360             This function inserts new node with key \p key and if inserting is successful then it calls
361             \p func functor with signature
362             \code
363             struct functor {
364                 void operator()( value_type& item );
365             };
366             \endcode
367
368             The argument \p item of user-defined functor \p func is the reference
369             to the map's item inserted. <tt>item.second</tt> is a reference to item's value that may be changed.
370
371             The user-defined functor it is called only if the inserting is successful.
372             The \p key_type should be constructible from value of type \p K.
373
374             The function allows to split creating of new item into two part:
375             - create item from \p key;
376             - insert new item into the map;
377             - if inserting is successful, initialize the value of item by calling \p f functor
378
379             This can be useful if complete initialization of object of \p mapped_type is heavyweight and
380             it is preferable that the initialization should be completed only if inserting is successful.
381
382             Returns an iterator pointed to inserted value, or \p end() if inserting is failed
383
384             @warning For \ref cds_nonintrusive_MichaelKVList_nogc "MichaelKVList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
385             \ref cds_nonintrusive_LazyKVList_nogc "LazyKVList" provides exclusive access to inserted item and does not require any node-level
386             synchronization.
387         */
388         template <typename K, typename Func>
389         iterator insert_with( const K& key, Func func )
390         {
391             bucket_type& refBucket = bucket( key );
392             bucket_iterator it = refBucket.insert_with( key, func );
393
394             if ( it != refBucket.end() ) {
395                 ++m_ItemCounter;
396                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
397             }
398
399             return end();
400         }
401
402         /// For key \p key inserts data of type \p mapped_type created from \p args
403         /**
404             \p key_type should be constructible from type \p K
405
406             Returns an iterator pointed to inserted value, or \p end() if inserting is failed
407         */
408         template <typename K, typename... Args>
409         iterator emplace( K&& key, Args&&... args )
410         {
411             bucket_type& refBucket = bucket( key );
412             bucket_iterator it = refBucket.emplace( std::forward<K>(key), std::forward<Args>(args)... );
413
414             if ( it != refBucket.end() ) {
415                 ++m_ItemCounter;
416                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
417             }
418
419             return end();
420         }
421
422         /// Updates the item
423         /**
424             If \p key is not in the map and \p bAllowInsert is \p true, the function inserts a new item.
425             Otherwise, the function returns an iterator pointing to the item found.
426
427             Returns <tt> std::pair<iterator, bool> </tt> where \p first is an iterator pointing to
428             item found or inserted (if inserting is not allowed and \p key is not found, the iterator will be \p end()),
429
430             \p second is true if new item has been added or \p false if the item
431             already is in the map.
432
433             @warning For \ref cds_nonintrusive_MichaelKVList_nogc "MichaelKVList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
434             \ref cds_nonintrusive_LazyKVList_nogc "LazyKVList" provides exclusive access to inserted item and does not require any node-level
435             synchronization.
436         */
437         template <typename K>
438         std::pair<iterator, bool> update( const K& key, bool bAllowInsert = true )
439         {
440             bucket_type& refBucket = bucket( key );
441             std::pair<bucket_iterator, bool> ret = refBucket.update( key, bAllowInsert );
442
443             if ( ret.second  )
444                 ++m_ItemCounter;
445             else if ( ret.first == refBucket.end() )
446                 return std::make_pair( end(), false );
447             return std::make_pair( iterator( ret.first, &refBucket, m_Buckets + bucket_count() ), ret.second );
448         }
449         //@cond
450         template <typename K>
451         CDS_DEPRECATED("ensure() is deprecated, use update()")
452         std::pair<iterator, bool> ensure( K const& key )
453         {
454             return update( key, true );
455         }
456         //@endcond
457
458         /// Checks whether the map contains \p key
459         /**
460             The function searches the item with key equal to \p key
461             and returns an iterator pointed to item found and \ref end() otherwise
462         */
463         template <typename K>
464         iterator contains( K const& key )
465         {
466             bucket_type& refBucket = bucket( key );
467             bucket_iterator it = refBucket.contains( key );
468
469             if ( it != refBucket.end() )
470                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
471
472             return end();
473         }
474         //@cond
475         template <typename K>
476         CDS_DEPRECATED("deprecated, use contains()")
477         iterator find( K const& key )
478         {
479             return contains( key );
480         }
481         //@endcond
482
483         /// Checks whether the map contains \p key using \p pred predicate for searching
484         /**
485             The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
486             \p Less functor has the interface like \p std::less.
487             \p pred must imply the same element order as the comparator used for building the map.
488         */
489         template <typename K, typename Less>
490         iterator contains( K const& key, Less pred )
491         {
492             bucket_type& refBucket = bucket( key );
493             bucket_iterator it = refBucket.contains( key, pred );
494
495             if ( it != refBucket.end() )
496                 return iterator( it, &refBucket, m_Buckets + bucket_count() );
497
498             return end();
499         }
500         //@cond
501         template <typename K, typename Less>
502         CDS_DEPRECATED("deprecated, use contains()")
503         iterator find_with( K const& key, Less pred )
504         {
505             return contains( key, pred );
506         }
507         //@endcond
508
509         /// Clears the map (not atomic)
510         void clear()
511         {
512             for ( size_t i = 0; i < bucket_count(); ++i )
513                 m_Buckets[i].clear();
514             m_ItemCounter.reset();
515         }
516
517         /// Checks whether the map is empty
518         /**
519             Emptiness is checked by item counting: if item count is zero then the map is empty.
520             Thus, the correct item counting feature is an important part of Michael's map implementation.
521         */
522         bool empty() const
523         {
524             return size() == 0;
525         }
526
527         /// Returns item count in the map
528         size_t size() const
529         {
530             return m_ItemCounter;
531         }
532
533         /// Returns the size of hash table
534         /**
535             Since \p %MichaelHashMap cannot dynamically extend the hash table size,
536             the value returned is an constant depending on object initialization parameters;
537             see \p MichaelHashMap::MichaelHashMap for explanation.
538         */
539         size_t bucket_count() const
540         {
541             return m_nHashBitmask + 1;
542         }
543     };
544 }} // namespace cds::container
545
546 #endif // ifndef CDSLIB_CONTAINER_MICHAEL_MAP_NOGC_H