LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/context - cdhashmap.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 100 104 96.2 %
Date: 2024-10-06 11:37:27 Functions: 1009 1265 79.8 %
Branches: 24 34 70.6 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * Top contributors (to current version):
       3                 :            :  *   Morgan Deters, Mikolas Janota, Tim King
       4                 :            :  *
       5                 :            :  * This file is part of the cvc5 project.
       6                 :            :  *
       7                 :            :  * Copyright (c) 2009-2024 by the authors listed in the file AUTHORS
       8                 :            :  * in the top-level source directory and their institutional affiliations.
       9                 :            :  * All rights reserved.  See the file COPYING in the top-level source
      10                 :            :  * directory for licensing information.
      11                 :            :  * ****************************************************************************
      12                 :            :  *
      13                 :            :  * Context-dependent map class.
      14                 :            :  *
      15                 :            :  * Generic templated class for a map which must be saved and restored as
      16                 :            :  * contexts are pushed and popped.  Requires that operator= be defined for the
      17                 :            :  * data class, and operator== for the key class.  For key types that don't have
      18                 :            :  * a std::hash<>, you should provide an explicit HashFcn.
      19                 :            :  *
      20                 :            :  * See also:
      21                 :            :  *  CDInsertHashMap : An "insert-once" CD hash map.
      22                 :            :  *  CDTrailHashMap : A lightweight CD hash map with poor iteration
      23                 :            :  *    characteristics and some quirks in usage.
      24                 :            :  *
      25                 :            :  * Internal documentation:
      26                 :            :  *
      27                 :            :  * CDHashMap<> is something of a work in progress at present (26 May
      28                 :            :  * 2010), due to some recent discoveries of problems with its
      29                 :            :  * internal state.  Here are some notes on the internal use of
      30                 :            :  * CDOhash_maps that may be useful in figuring out this mess:
      31                 :            :  *
      32                 :            :  *     So you have a CDHashMap<>.
      33                 :            :  *
      34                 :            :  *     You insert some (key,value) pairs.  Each allocates a CDOhash_map<>
      35                 :            :  *     and goes on a doubly-linked list headed by map.d_first and
      36                 :            :  *     threaded via CDOhash_map.{d_prev,d_next}.  CDOhash_maps are constructed
      37                 :            :  *     with a NULL d_map pointer, but then immediately call
      38                 :            :  *     makeCurrent() and set the d_map pointer back to the map.  At
      39                 :            :  *     context level 0, this doesn't lead to anything special.  In
      40                 :            :  *     higher context levels, this stores away a CDOhash_map with a NULL
      41                 :            :  *     map pointer at level 0, and a non-NULL map pointer in the
      42                 :            :  *     current context level.  (Remember that for later.)
      43                 :            :  *
      44                 :            :  *     When a key is associated to a new value in a CDHashMap, its
      45                 :            :  *     associated CDOhash_map calls makeCurrent(), then sets the new
      46                 :            :  *     value.  The save object is also a CDOhash_map (allocated in context
      47                 :            :  *     memory).
      48                 :            :  *
      49                 :            :  *     Now, CDOhash_maps disappear in a variety of ways.
      50                 :            :  *
      51                 :            :  *     First, you might pop beyond a "modification of the value"
      52                 :            :  *     scope level, requiring a re-association of the key to an old
      53                 :            :  *     value.  This is easy.  CDOhash_map::restore() does the work, and
      54                 :            :  *     the context memory of the save object is reclaimed as usual.
      55                 :            :  *
      56                 :            :  *     Second, you might pop beyond a "insert the key" scope level,
      57                 :            :  *     requiring that the key be completely removed from the map and
      58                 :            :  *     its CDOhash_map object memory freed.  Here, the CDOhash_map is restored
      59                 :            :  *     to a "NULL-map" state (see above), signaling it to remove
      60                 :            :  *     itself from the map completely and put itself on a "trash
      61                 :            :  *     list" for its scope.
      62                 :            :  *
      63                 :            :  *     Third, you might delete the cdhashmap(calling CDHashMap::~CDHashMap()).
      64                 :            :  *     This first calls destroy(), as per ContextObj contract, but
      65                 :            :  *     cdhashmapdoesn't save/restore itself, so that does nothing at the
      66                 :            :  *     CDHashMap-level. Then, for each element in the map, it marks it as being
      67                 :            :  *     "part of a complete map destruction", which essentially short-circuits
      68                 :            :  *     CDOhash_map::restore() (see CDOhash_map::restore()), then deallocates
      69                 :            :  *     it.
      70                 :            :  *
      71                 :            :  *     Fourth, you might clear() the CDHashMap.  This does exactly the
      72                 :            :  *     same as CDHashMap::~CDHashMap(), except that it doesn't call destroy()
      73                 :            :  *     on itself.
      74                 :            :  *
      75                 :            :  *     ContextObj::deleteSelf() calls the CDOhash_map destructor, then
      76                 :            :  *     frees the memory associated to the CDOhash_map.
      77                 :            :  *     CDOhash_map::~CDOhash_map() calls destroy(), which restores as much as
      78                 :            :  *     possible.
      79                 :            :  */
      80                 :            : 
      81                 :            : #include "cvc5parser_public.h"
      82                 :            : 
      83                 :            : #ifndef CVC5__CONTEXT__CDHASHMAP_H
      84                 :            : #define CVC5__CONTEXT__CDHASHMAP_H
      85                 :            : 
      86                 :            : #include <functional>
      87                 :            : #include <iterator>
      88                 :            : #include <unordered_map>
      89                 :            : 
      90                 :            : #include "base/check.h"
      91                 :            : #include "context/cdhashmap_forward.h"
      92                 :            : #include "context/context.h"
      93                 :            : 
      94                 :            : namespace cvc5::context {
      95                 :            : 
      96                 :            : // Auxiliary class: almost the same as CDO (see cdo.h)
      97                 :            : 
      98                 :            : template <class Key, class Data, class HashFcn = std::hash<Key> >
      99                 :            : class CDOhash_map : public ContextObj
     100                 :            : {
     101                 :            :   friend class CDHashMap<Key, Data, HashFcn>;
     102                 :            : 
     103                 :            :  public:
     104                 :            :   // The type of the <Key, Data> pair mapped by this class.
     105                 :            :   //
     106                 :            :   // Implementation:
     107                 :            :   // The data and key visible to users of CDHashMap are only visible through
     108                 :            :   // const references. Thus the type of dereferencing a
     109                 :            :   // CDHashMap<Key, Data>::iterator.second is intended to always be a
     110                 :            :   // `const Data&`. (Otherwise, to get a Data& safely, access operations
     111                 :            :   // would need to makeCurrent() to get the Data&, which is an unacceptable
     112                 :            :   // performance hit.) To allow for the desired updating in other scenarios, we
     113                 :            :   // store a std::pair<const Key, const Data> and break the const encapsulation
     114                 :            :   // when necessary.
     115                 :            :   using value_type = std::pair<const Key, const Data>;
     116                 :            : 
     117                 :            :  private:
     118                 :            :   value_type d_value;
     119                 :            : 
     120                 :            :   // See documentation of value_type for why this is needed.
     121                 :  123862373 :   Key& mutable_key() { return const_cast<Key&>(d_value.first); }
     122                 :            :   // See documentation of value_type for why this is needed.
     123                 :  493914890 :   Data& mutable_data() { return const_cast<Data&>(d_value.second); }
     124                 :            : 
     125                 :            :   CDHashMap<Key, Data, HashFcn>* d_map;
     126                 :            : 
     127                 :            :   // Doubly-linked list for keeping track of elements in order of insertion
     128                 :            :   CDOhash_map* d_prev;
     129                 :            :   CDOhash_map* d_next;
     130                 :            : 
     131                 :  123866282 :   ContextObj* save(ContextMemoryManager* pCMM) override
     132                 :            :   {
     133                 :  123866282 :     return new (pCMM) CDOhash_map(*this);
     134                 :            :   }
     135                 :            : 
     136                 :  123862373 :   void restore(ContextObj* data) override
     137                 :            :   {
     138                 :  123862373 :     CDOhash_map* p = static_cast<CDOhash_map*>(data);
     139         [ +  + ]:  123862373 :     if (d_map != NULL)
     140                 :            :     {
     141         [ +  + ]:  122757657 :       if (p->d_map == NULL)
     142                 :            :       {
     143 [ +  - ][ +  - ]:  241429144 :         Assert(d_map->d_map.find(getKey()) != d_map->d_map.end()
         [ -  + ][ -  - ]
     144                 :            :                && (*d_map->d_map.find(getKey())).second == this);
     145                 :            :         // no longer in map (popped beyond first level in which it was)
     146                 :  120714595 :         d_map->d_map.erase(getKey());
     147                 :            :         // If we call deleteSelf() here, it re-enters restore().  So,
     148                 :            :         // put it on a "trash heap" instead, for later deletion.
     149                 :            :         //
     150                 :            :         // FIXME multithreading
     151         [ +  + ]:  120714595 :         if (d_map->d_first == this)
     152                 :            :         {
     153         [ +  - ]:    3222343 :           if (d_next == this)
     154                 :            :           {
     155 [ -  + ][ -  + ]:    3222343 :             Assert(d_prev == this);
                 [ -  - ]
     156                 :    3222343 :             d_map->d_first = NULL;
     157                 :            :           }
     158                 :            :           else
     159                 :            :           {
     160                 :          0 :             d_map->d_first = d_next;
     161                 :            :           }
     162                 :            :         }
     163                 :  120714595 :         d_next->d_prev = d_prev;
     164                 :  120714595 :         d_prev->d_next = d_next;
     165                 :            : 
     166                 :            :         // this->deleteSelf();
     167                 :  120714595 :         enqueueToGarbageCollect();
     168                 :            :       }
     169                 :            :       else
     170                 :            :       {
     171                 :    2043026 :         mutable_data() = p->get();
     172                 :            :       }
     173                 :            :     }
     174                 :            :     // Explicitly call destructors for the key and the data as they will not
     175                 :            :     // otherwise get called.
     176                 :  123862373 :     p->mutable_key().~Key();
     177                 :  123862373 :     p->mutable_data().~Data();
     178                 :  123862373 :   }
     179                 :            : 
     180                 :            :   /** ensure copy ctor is only called by us */
     181                 :  123866282 :   CDOhash_map(const CDOhash_map& other)
     182                 :            :       : ContextObj(other),
     183                 :            :         // don't need to save the key---and if we do we can get
     184                 :            :         // refcounts for Node keys messed up and leak memory
     185                 :  123866282 :         d_value(Key(), other.d_value.second),
     186                 :  123866282 :         d_map(other.d_map),
     187                 :            :         d_prev(NULL),
     188                 :  123866282 :         d_next(NULL)
     189                 :            :   {
     190                 :  123866282 :   }
     191                 :            :   CDOhash_map& operator=(const CDOhash_map&) = delete;
     192                 :            : 
     193                 :            :  public:
     194                 :  191599669 :   CDOhash_map(Context* context,
     195                 :            :               CDHashMap<Key, Data, HashFcn>* map,
     196                 :            :               const Key& key,
     197                 :            :               const Data& data)
     198                 :  191599669 :       : ContextObj(context), d_value(key, data), d_map(NULL)
     199                 :            :   {
     200                 :            :     // Normal map insertion: first makeCurrent(), then set the data
     201                 :            :     // and then, later, the map.  Order is important; we can't
     202                 :            :     // initialize d_map in the constructor init list above, because
     203                 :            :     // we want the restore of d_map to NULL to signal us to remove
     204                 :            :     // the element from the map.
     205                 :            : 
     206                 :  191599669 :     set(data);
     207                 :  191599669 :     d_map = map;
     208                 :            : 
     209                 :  191599669 :     CDOhash_map*& first = d_map->d_first;
     210         [ +  + ]:  191599669 :     if (first == NULL)
     211                 :            :     {
     212                 :    9193553 :       first = d_next = d_prev = this;
     213                 :            :     }
     214                 :            :     else
     215                 :            :     {
     216                 :  182405913 :       d_prev = first->d_prev;
     217                 :  182405913 :       d_next = first;
     218                 :  182405913 :       d_prev->d_next = this;
     219                 :  182405913 :       first->d_prev = this;
     220                 :            :     }
     221                 :  191599669 :   }
     222                 :            : 
     223                 :  191594851 :   ~CDOhash_map() { destroy(); }
     224                 :            : 
     225                 :  368009766 :   void set(const Data& data)
     226                 :            :   {
     227                 :  368009766 :     makeCurrent();
     228                 :  368009766 :     mutable_data() = data;
     229                 :  368009766 :   }
     230                 :            : 
     231                 :  362143232 :   const Key& getKey() const { return d_value.first; }
     232                 :            : 
     233                 :   56733823 :   const Data& get() const { return d_value.second; }
     234                 :            : 
     235                 :  386536208 :   const value_type& getValue() const { return d_value; }
     236                 :            : 
     237                 :   54674583 :   operator Data() { return get(); }
     238                 :            : 
     239                 :  176278927 :   const Data& operator=(const Data& data)
     240                 :            :   {
     241                 :  176278927 :     set(data);
     242                 :  176278927 :     return data;
     243                 :            :   }
     244                 :            : 
     245                 :    2644707 :   CDOhash_map* next() const
     246                 :            :   {
     247         [ +  + ]:    2644707 :     if (d_next == d_map->d_first)
     248                 :            :     {
     249                 :     267786 :       return NULL;
     250                 :            :     }
     251                 :            :     else
     252                 :            :     {
     253                 :    2376921 :       return d_next;
     254                 :            :     }
     255                 :            :   }
     256                 :            : }; /* class CDOhash_map<> */
     257                 :            : 
     258                 :            : /**
     259                 :            :  * Generic templated class for a map which must be saved and restored
     260                 :            :  * as contexts are pushed and popped.  Requires that operator= be
     261                 :            :  * defined for the data class, and operator== for the key class.
     262                 :            :  */
     263                 :            : template <class Key, class Data, class HashFcn>
     264                 :            : class CDHashMap : public ContextObj
     265                 :            : {
     266                 :            :   typedef CDOhash_map<Key, Data, HashFcn> Element;
     267                 :            :   typedef std::unordered_map<Key, Element*, HashFcn> table_type;
     268                 :            : 
     269                 :            :   friend class CDOhash_map<Key, Data, HashFcn>;
     270                 :            : 
     271                 :            :   table_type d_map;
     272                 :            : 
     273                 :            :   Element* d_first;
     274                 :            :   Context* d_context;
     275                 :            : 
     276                 :            :   // Nothing to save; the elements take care of themselves
     277                 :          0 :   ContextObj* save(ContextMemoryManager* pCMM) override
     278                 :            :   {
     279                 :          0 :     Unreachable();
     280                 :            :     SuppressWrongNoReturnWarning;
     281                 :            :   }
     282                 :            : 
     283                 :            :   // Similarly, nothing to restore
     284                 :          0 :   void restore(ContextObj* data) override { Unreachable(); }
     285                 :            : 
     286                 :            :   // no copy or assignment
     287                 :            :   CDHashMap(const CDHashMap&) = delete;
     288                 :            :   CDHashMap& operator=(const CDHashMap&) = delete;
     289                 :            : 
     290                 :            :  public:
     291                 :   24137435 :   CDHashMap(Context* context)
     292                 :   24137435 :       : ContextObj(context), d_map(), d_first(NULL), d_context(context)
     293                 :            :   {
     294                 :   24137435 :   }
     295                 :            : 
     296                 :   24103359 :   ~CDHashMap()
     297                 :            :   {
     298                 :   24103359 :     destroy();
     299                 :   24103359 :     clear();
     300                 :   48206648 :   }
     301                 :            : 
     302                 :   24103359 :   void clear()
     303                 :            :   {
     304         [ +  + ]:   94983438 :     for (auto& key_element_pair : d_map)
     305                 :            :     {
     306                 :            :       // mark it as being a destruction (short-circuit restore())
     307                 :   70880096 :       Element* element = key_element_pair.second;
     308                 :   70880096 :       element->d_map = nullptr;
     309                 :   70880096 :       element->deleteSelf();
     310                 :            :     }
     311                 :   24103359 :     d_map.clear();
     312                 :   24103359 :     d_first = nullptr;
     313                 :   24103359 :   }
     314                 :            : 
     315                 :            :   // The usual operators of map
     316                 :            : 
     317                 :    2285243 :   size_t size() const { return d_map.size(); }
     318                 :            : 
     319                 :   23387361 :   bool empty() const { return d_map.empty(); }
     320                 :            : 
     321                 :        656 :   size_t count(const Key& k) const { return d_map.count(k); }
     322                 :            : 
     323                 :            :   // If a key is not present, a new object is created and inserted
     324                 :  230969880 :   Element& operator[](const Key& k)
     325                 :            :   {
     326                 :  230969880 :     const auto res = d_map.insert({k, nullptr});
     327         [ +  + ]:  230969880 :     if (res.second)
     328                 :            :     {  // create new object
     329                 :  125632667 :       res.first->second = new (true) Element(d_context, this, k, Data());
     330                 :            :     }
     331                 :  230969880 :     return *(res.first->second);
     332                 :            :   }
     333                 :            : 
     334                 :   66098011 :   bool insert(const Key& k, const Data& d)
     335                 :            :   {
     336                 :   66098011 :     const auto res = d_map.insert({k, nullptr});
     337         [ +  + ]:   66098011 :     if (res.second)
     338                 :            :     {  // create new object
     339                 :   65966951 :       res.first->second = new (true) Element(d_context, this, k, d);
     340                 :            :     }
     341                 :            :     else
     342                 :            :     {
     343                 :     131096 :       res.first->second->set(d);
     344                 :            :     }
     345                 :   66098011 :     return res.second;
     346                 :            :   }
     347                 :            : 
     348                 :            :   // FIXME: no erase(), too much hassle to implement efficiently...
     349                 :            : 
     350                 :            :   using value_type = typename CDOhash_map<Key, Data, HashFcn>::value_type;
     351                 :            : 
     352                 :            :   class iterator
     353                 :            :   {
     354                 :            :     const Element* d_it;
     355                 :            : 
     356                 :            :    public:
     357                 :            :     using iterator_category = std::forward_iterator_tag;
     358                 :            :     using value_type = typename CDOhash_map<Key, Data, HashFcn>::value_type;
     359                 :            :     using difference_type = ptrdiff_t;
     360                 :            :     using pointer = typename CDOhash_map<Key, Data, HashFcn>::value_type*;
     361                 :            :     using reference = typename CDOhash_map<Key, Data, HashFcn>::value_type&;
     362                 :            : 
     363                 : 1622495668 :     iterator(const Element* p) : d_it(p) {}
     364                 :         52 :     iterator(const iterator& i) : d_it(i.d_it) {}
     365                 :            : 
     366                 :            :     // Default constructor
     367                 :   62447452 :     iterator() : d_it(nullptr) {}
     368                 :            : 
     369                 :            :     // (Dis)equality
     370                 :  303894994 :     bool operator==(const iterator& i) const { return d_it == i.d_it; }
     371                 :  493692417 :     bool operator!=(const iterator& i) const { return d_it != i.d_it; }
     372                 :            : 
     373                 :            :     // Dereference operators.
     374                 :  358390616 :     const value_type& operator*() const { return d_it->getValue(); }
     375                 :   28145616 :     const value_type* operator->() const { return &d_it->getValue(); }
     376                 :            : 
     377                 :            :     // Prefix increment
     378                 :    2644707 :     iterator& operator++()
     379                 :            :     {
     380                 :    2644707 :       d_it = d_it->next();
     381                 :    2644707 :       return *this;
     382                 :            :     }
     383                 :            : 
     384                 :            :     // Postfix increment is not yet supported.
     385                 :            :   }; /* class CDHashMap<>::iterator */
     386                 :            : 
     387                 :            :   typedef iterator const_iterator;
     388                 :            : 
     389                 :     830386 :   iterator begin() const { return iterator(d_first); }
     390                 :            : 
     391                 : 1160922367 :   iterator end() const { return iterator(NULL); }
     392                 :            : 
     393                 :  824365780 :   iterator find(const Key& k) const
     394                 :            :   {
     395                 :  824365780 :     typename table_type::const_iterator i = d_map.find(k);
     396                 :            : 
     397         [ +  + ]:  824365780 :     if (i == d_map.end())
     398                 :            :     {
     399                 :  363623376 :       return end();
     400                 :            :     }
     401                 :            :     else
     402                 :            :     {
     403                 :  460742537 :       return iterator((*i).second);
     404                 :            :     }
     405                 :            :   }
     406                 :            : 
     407                 :            : }; /* class CDHashMap<> */
     408                 :            : 
     409                 :            : }  // namespace cvc5::context
     410                 :            : 
     411                 :            : #endif /* CVC5__CONTEXT__CDHASHMAP_H */

Generated by: LCOV version 1.14