LCOV - code coverage report
Current view: top level - utilities/include/mcrl2/utilities - indexed_set.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 20 20 100.0 %
Date: 2024-04-19 03:43:27 Functions: 10 17 58.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Maurice Laveaux
       2             : // Copyright: see the accompanying file COPYING or copy at
       3             : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
       4             : //
       5             : // Distributed under the Boost Software License, Version 1.0.
       6             : // (See accompanying file LICENSE_1_0.txt or copy at
       7             : // http://www.boost.org/LICENSE_1_0.txt)
       8             : 
       9             : #ifndef MCRL2_UTILITIES_INDEXED_SET_H
      10             : #define MCRL2_UTILITIES_INDEXED_SET_H
      11             : 
      12             : #include <deque>
      13             : #include <mutex>
      14             : 
      15             : #include "mcrl2/utilities/unordered_map.h"
      16             : #include "mcrl2/utilities/unused.h"
      17             : #include "mcrl2/utilities/detail/atomic_wrapper.h"
      18             : #include "mcrl2/utilities/shared_mutex.h"
      19             : 
      20             : namespace mcrl2
      21             : {
      22             : namespace utilities
      23             : {
      24             : 
      25             : /// \brief A set that assigns each element an unique index.
      26             : template<typename Key,
      27             :          bool ThreadSafe = false,
      28             :          typename Hash = std::hash<Key>,
      29             :          typename Equals = std::equal_to<Key>,
      30             :          typename Allocator = std::allocator<Key>,
      31             :          typename KeyTable = std::deque< Key, Allocator > > 
      32             : class indexed_set
      33             : {
      34             : private:
      35             :   std::vector<detail::atomic_wrapper<std::size_t>> m_hashtable;
      36             :   KeyTable m_keys;
      37             : 
      38             :   /// \brief Mutex for the m_hashtable and m_keys data structures.
      39             :   mutable std::shared_ptr<std::mutex> m_mutex;
      40             :   mutable std::vector<shared_mutex> m_shared_mutexes;
      41             : 
      42             :   /// m_next_index indicates the next index that 
      43             :   //  has not yet been used. This allows to increase m_keys in 
      44             :   //  large steps, avoiding exclusive access too often.  
      45             :   detail::atomic_wrapper<size_t> m_next_index;
      46             : 
      47             :   Hash m_hasher;
      48             :   Equals m_equals;
      49             : 
      50             :   /// \brief Reserve indices that can be used. Doing this 
      51             :   ///        infrequently prevents obtaining an exclusive lock for the
      52             :   ///        indexed set too often. This operation requires a
      53             :   ///        resize of m_keys. 
      54             :   void reserve_indices(std::size_t thread_index);
      55             : 
      56             :   /// \brief Inserts the given (key, n) pair into the indexed set.
      57             :   std::size_t put_in_hashtable(const Key& key, std::size_t value, std::size_t& new_position);
      58             : 
      59             :   /// \brief Resizes the hash table to twice its current size.
      60             :   inline void resize_hashtable();
      61             : 
      62             : public:
      63             :   typedef Key key_type;
      64             :   typedef std::size_t size_type;
      65             :   typedef std::pair<const key_type, size_type> value_type;
      66             :   typedef Equals key_equal;
      67             :   typedef Hash hasher;
      68             : 
      69             :   typedef value_type& reference;
      70             :   typedef const value_type& const_reference;
      71             :   typedef value_type* pointer;
      72             :   typedef const value_type* const_pointer;
      73             : 
      74             :   typedef typename KeyTable::iterator iterator;
      75             :   typedef typename KeyTable::const_iterator const_iterator;
      76             : 
      77             :   typedef typename KeyTable::reverse_iterator reverse_iterator;
      78             :   typedef typename KeyTable::const_reverse_iterator const_reverse_iterator;
      79             : 
      80             :   typedef std::ptrdiff_t difference_type;
      81             :   
      82             :   /// \brief Value returned when an element does not exist in the set.
      83             :   /// \return Value indicating non existing element, equal to std::numeric_limits<std::size_t>::max(). 
      84             :   static constexpr size_type npos = std::numeric_limits<std::size_t>::max();
      85             : 
      86             :   /// \brief Constructor of an empty indexed set. Starts with a hashtable of size 128 and assumes one single thread.
      87             :   indexed_set();
      88             : 
      89             :   /// \brief Constructor of an empty indexed set. 
      90             :   /// \details With a single thread it delivers contiguous values for states.
      91             :   ///          With multiple threads some indices may be skipped. Each thread
      92             :   ///          reserves numbers, which it hands out. If a thread does not have
      93             :   ///          the opportunity to hand out all numbers, holes in the contiguous
      94             :   ///          numbering can occur. The holes are always of limited size. 
      95             :   /// \param number_of_threads The number of threads that use this index set. If the number is 1, it is treated
      96             :   ///        as a sequential set. If this number is larger than 1, the threads must be numbered
      97             :   ///        from 1 up and including number_of_threads. The number 0 cannot be used in that case. 
      98             :   indexed_set(std::size_t number_of_threads);
      99             : 
     100             :   /// \brief Constructor of an empty index set. Starts with a hashtable of the indicated size. 
     101             :   /// \details With one thread the numbering is contiguous. With multiple threads limited size holes
     102             :   ///          can occur in the numbering. 
     103             :   /// \param number_of_threads The number of threads that use this index set. This number is either 1, and then the implementation 
     104             :   ///        assumes that the thread has number 0, or it is larger than 1, and it is assumed that threads are numbered from 1 upwards. 
     105             :   /// \param initial_hashtable_size The initial size of the hashtable.
     106             :   /// \param hash The hash function.
     107             :   /// \param equals The comparison function for its elements.
     108             :   indexed_set(
     109             :     std::size_t number_of_threads, 
     110             :     std::size_t initial_hashtable_size, 
     111             :     const hasher& hash = hasher(),
     112             :     const key_equal& equals = key_equal());
     113             : 
     114             :   /// \brief Returns a reference to the mapped value.
     115             :   /// \details Returns an invalid value, larger or equal than the size of the indexed set, if there is no element with the given key.
     116             :   size_type index(const key_type& key, std::size_t thread_index = 0) const;
     117             : 
     118             :   /// \brief Returns a reference to the mapped value.
     119             :   /// \details Returns an out_of_range exception if there is no element with the given key.
     120             :   /// \param index The position in the indexed set.
     121             :   /// \return The value at position index.
     122             :   const key_type& at(const size_type index) const;
     123             : 
     124             :   /// \brief Operator that provides a const reference at the position indicated by index.
     125             :   /// \param index The position in the indexed set.
     126             :   /// \return The value at position index.
     127             :   /// \threadsafe
     128             :   const key_type& operator[](const size_type index) const;
     129             : 
     130             :   /// \brief Forward iterator which runs through the elements from the lowest to the largest number.
     131             :   /// \details Complexity is constant per operation.
     132             :   iterator begin(std::size_t thread_index = 0) 
     133             :   { 
     134             :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     135             :     iterator i = m_keys.begin();
     136             :     return i;
     137             :   }
     138             : 
     139             :   /// \brief End of the forward iterator.
     140           4 :   iterator end(std::size_t thread_index = 0)
     141             :   { 
     142           4 :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     143           4 :     iterator i = m_keys.begin()+m_next_index;
     144           8 :     return i;
     145           4 :   }
     146             : 
     147             :   /// \brief Forward iterator which runs through the elements from the lowest to the largest number.
     148             :   /// \details Complexity is constant per operation.
     149        2231 :   const_iterator begin(std::size_t thread_index = 0) const
     150             :   {
     151        2231 :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     152        2231 :     const_iterator i = m_keys.begin();
     153        4462 :     return i;
     154        2231 :   }
     155             : 
     156             :   /// \brief End of the forward iterator.
     157        9239 :   const_iterator end(std::size_t thread_index = 0) const
     158             :   {
     159        9239 :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     160        9239 :     const_iterator i = m_keys.begin()+m_next_index;
     161       18478 :     return i;
     162        9239 :   }
     163             : 
     164             :   /// \brief const_iterator going through the elements in the set numbered from zero upwards. 
     165             :   const_iterator cbegin(std::size_t thread_index = 0) const
     166             :   { 
     167             :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     168             :     const_iterator i = m_keys.begin();
     169             :     return i;
     170             :   }
     171             : 
     172             :   /// \brief End of the forward const_iterator. 
     173             :   const_iterator cend(std::size_t thread_index = 0) const 
     174             :   { 
     175             :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     176             :     const_iterator i = m_keys.cbegin() + m_next_index;
     177             :     return i;
     178             :   }
     179             : 
     180             :   /// \brief Reverse iterator going through the elements in the set from the largest to the smallest index. 
     181             :   reverse_iterator rbegin(std::size_t thread_index = 0) 
     182             :   { 
     183             :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     184             :     reverse_iterator i = m_keys.rend() - m_next_index;
     185             :     return i;
     186             :   }
     187             : 
     188             :   /// \brief End of the reverse iterator. 
     189             :   reverse_iterator rend(std::size_t thread_index = 0)
     190             :   { 
     191             :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     192             :     reverse_iterator i = m_keys.rend();
     193             :     return i;
     194             :   }
     195             : 
     196             :   /// \brief Reverse const_iterator going through the elements from the highest to the lowest numbered element. 
     197             :   const_reverse_iterator crbegin(std::size_t thread_index = 0) const
     198             :   { 
     199             :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     200             :     const_reverse_iterator i=m_keys.crend() - m_next_index;
     201             :     return i;
     202             :   }
     203             : 
     204             :   /// \brief End of the reverse const_iterator. 
     205             :   const_reverse_iterator crend(std::size_t thread_index = 0) const 
     206             :   { 
     207             :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     208             :     reverse_iterator i = m_keys.crend();
     209             :     return i;
     210             :   }
     211             : 
     212             :   /// \brief Clears the indexed set by removing all its elements. It is not guaranteed that the memory is released too. 
     213             :   void clear(std::size_t thread_index=0);
     214             : 
     215             :   /// \brief Insert a key in the indexed set and return its index. 
     216             :   /// \details If the element was already in the set, the resulting bool is true, and the existing index is returned.
     217             :   ///         Otherwise, the key is inserted in the set, and the next available index is assigned to it. 
     218             :   /// \param  key The key to be inserted in the set.
     219             :   /// \return The index of the key and a boolean indicating whether the element was actually inserted.
     220             :   /// \details threadsafe
     221             :   std::pair<size_type, bool> insert(const key_type& key, std::size_t thread_index = 0);
     222             : 
     223             :   /// \brief Provides an iterator to the stored key in the indexed set.
     224             :   /// \param key The key that is sought.
     225             :   /// \return An iterator to the key, otherwise end().
     226             :   const_iterator find(const key_type& key, std::size_t thread_index = 0) const;
     227             : 
     228             :   /// \brief The number of elements in the indexed set.
     229             :   /// \return The number of elements in the indexed set. 
     230             :   /// \details threadsafe
     231       93379 :   size_type size(std::size_t thread_index = 0) const
     232             :   { 
     233       93379 :     shared_guard guard = m_shared_mutexes[thread_index].lock_shared();
     234       93379 :     size_type result=m_next_index;
     235       93379 :     return result;
     236       93379 :   }
     237             : };
     238             : 
     239             : } // end namespace utilities
     240             : } // end namespace mcrl2
     241             : 
     242             : #include "mcrl2/utilities/detail/indexed_set.h"
     243             : 
     244             : 
     245             : #endif // MCRL2_UTILITIES_INDEXED_SET_H

Generated by: LCOV version 1.14