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: 2026-04-07 10:40:57 Functions: 1031 1292 79.8 %
Branches: 25 36 69.4 %

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

Generated by: LCOV version 1.14