GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/context/cdlist.h Lines: 138 144 95.8 %
Date: 2021-05-22 Branches: 921 2486 37.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Morgan Deters, Tim King, Clark Barrett
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 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
 * Context-dependent list class (only supports append).
13
 *
14
 * This list only supports appending to the list; on backtrack, the list is
15
 * simply shortened.
16
 */
17
18
#include "cvc5_private.h"
19
20
#ifndef CVC5__CONTEXT__CDLIST_H
21
#define CVC5__CONTEXT__CDLIST_H
22
23
#include <cstring>
24
#include <iterator>
25
#include <memory>
26
#include <string>
27
28
#include "base/check.h"
29
#include "context/cdlist_forward.h"
30
#include "context/context.h"
31
#include "context/context_mm.h"
32
33
namespace cvc5 {
34
namespace context {
35
36
/**
37
 * Generic context-dependent dynamic array.  Note that for efficiency,
38
 * this implementation makes the following assumptions:
39
 *
40
 * 1. Over time, objects are only added to the list.  Objects are only
41
 *    removed when a pop restores the list to a previous state.
42
 *
43
 * 2. T objects can safely be copied using their copy constructor,
44
 *    operator=, and memcpy.
45
 */
46
template <class T, class CleanUpT, class AllocatorT>
47
class CDList : public ContextObj {
48
public:
49
50
  /** The value type with which this CDList<> was instantiated. */
51
  typedef T value_type;
52
53
  /** The cleanup type with which this CDList<> was instantiated. */
54
  typedef CleanUpT CleanUp;
55
56
  /** The allocator type with which this CDList<> was instantiated. */
57
  typedef AllocatorT Allocator;
58
59
protected:
60
61
  /**
62
   * d_list is a dynamic array of objects of type T.
63
   */
64
  T* d_list;
65
66
  /**
67
   * Number of objects in d_list
68
   */
69
  size_t d_size;
70
71
private:
72
73
  static const size_t INITIAL_SIZE = 10;
74
  static const size_t GROWTH_FACTOR = 2;
75
76
  /**
77
   * Whether to call the destructor when items are popped from the
78
   * list.  True by default, but can be set to false by setting the
79
   * second argument in the constructor to false.
80
   */
81
  bool d_callDestructor;
82
83
  /**
84
   * Allocated size of d_list.
85
   */
86
  size_t d_sizeAlloc;
87
88
  /**
89
   * The CleanUp functor.
90
   */
91
  CleanUp d_cleanUp;
92
93
  /**
94
   * Our allocator.
95
   */
96
  Allocator d_allocator;
97
98
protected:
99
  /**
100
   * Private copy constructor used only by save().  d_list and
101
   * d_sizeAlloc are not copied: only the base class information and
102
   * d_size are needed in restore.
103
   */
104
11502433
  CDList(const CDList& l) :
105
    ContextObj(l),
106
    d_list(NULL),
107
11502433
    d_size(l.d_size),
108
    d_callDestructor(false),
109
    d_sizeAlloc(0),
110
    d_cleanUp(l.d_cleanUp),
111
23004866
    d_allocator(l.d_allocator) {
112
34507299
    Debug("cdlist") << "copy ctor: " << this
113
23004866
                    << " from " << &l
114
11502433
                    << " size " << d_size << std::endl;
115
11502433
  }
116
  CDList& operator=(const CDList& l) = delete;
117
118
 private:
119
  /**
120
   * Reallocate the array with more space. Throws bad_alloc if memory
121
   * allocation fails. Does not perform any action if there is still unused
122
   * allocated space.
123
   */
124
77418314
  void grow() {
125
154836628
    Debug("cdlist") << "grow " << this << " " << getContext()->getLevel()
126
77418314
                    << ": grow? " << d_size << " " << d_sizeAlloc << std::endl;
127
77418314
    if (d_size != d_sizeAlloc)
128
    {
129
      // If there is still unused allocated space
130
76904844
      return;
131
    }
132
1026940
    Debug("cdlist") << "grow " << this << " " << getContext()->getLevel()
133
513470
                    << ": grow!" << std::endl;
134
135
513470
    if(d_list == NULL) {
136
      // Allocate an initial list if one does not yet exist
137
330208
      d_sizeAlloc = INITIAL_SIZE;
138
990624
      Debug("cdlist") << "initial grow of cdlist " << this
139
660416
                      << " level " << getContext()->getLevel()
140
330208
                      << " to " << d_sizeAlloc << std::endl;
141
330208
      if(d_sizeAlloc > d_allocator.max_size()) {
142
        d_sizeAlloc = d_allocator.max_size();
143
      }
144
330208
      d_list = d_allocator.allocate(d_sizeAlloc);
145
330208
      if(d_list == NULL) {
146
        throw std::bad_alloc();
147
      }
148
    } else {
149
      // Allocate a new array with double the size
150
183262
      size_t newSize = GROWTH_FACTOR * d_sizeAlloc;
151
183262
      if(newSize > d_allocator.max_size()) {
152
        newSize = d_allocator.max_size();
153
        Assert(newSize > d_sizeAlloc)
154
            << "cannot request larger list due to allocator limits";
155
      }
156
183262
      T* newList = d_allocator.allocate(newSize);
157
549780
      Debug("cdlist") << "2x grow of cdlist " << this
158
366520
                      << " level " << getContext()->getLevel()
159
183260
                      << " to " << newSize
160
183260
                      << " (from " << d_list
161
183260
                      << " to " << newList << ")" << std::endl;
162
183260
      if(newList == NULL) {
163
        throw std::bad_alloc();
164
      }
165
183260
      std::memcpy(newList, d_list, sizeof(T) * d_sizeAlloc);
166
183260
      d_allocator.deallocate(d_list, d_sizeAlloc);
167
183260
      d_list = newList;
168
183260
      d_sizeAlloc = newSize;
169
    }
170
  }
171
172
  /**
173
   * Implementation of mandatory ContextObj method save: simply copies
174
   * the current size to a copy using the copy constructor (the
175
   * pointer and the allocated size are *not* copied as they are not
176
   * restored on a pop).  The saved information is allocated using the
177
   * ContextMemoryManager.
178
   */
179
8732494
  ContextObj* save(ContextMemoryManager* pCMM) override
180
  {
181
8732494
    ContextObj* data = new(pCMM) CDList<T, CleanUp, Allocator>(*this);
182
26197482
    Debug("cdlist") << "save " << this
183
17464988
                    << " at level " << this->getContext()->getLevel()
184
8732494
                    << " size at " << this->d_size
185
8732494
                    << " sizeAlloc at " << this->d_sizeAlloc
186
8732494
                    << " d_list is " << this->d_list
187
8732494
                    << " data:" << data << std::endl;
188
8732494
    return data;
189
  }
190
191
protected:
192
  /**
193
   * Implementation of mandatory ContextObj method restore: simply
194
   * restores the previous size.  Note that the list pointer and the
195
   * allocated size are not changed.
196
   */
197
11502433
 void restore(ContextObj* data) override
198
 {
199
34507299
   Debug("cdlist") << "restore " << this << " level "
200
23004866
                   << this->getContext()->getLevel() << " data == " << data
201
11502433
                   << " call dtor == " << this->d_callDestructor
202
11502433
                   << " d_list == " << this->d_list << std::endl;
203
11502433
   truncateList(((CDList<T, CleanUp, Allocator>*)data)->d_size);
204
34507299
   Debug("cdlist") << "restore " << this << " level "
205
23004866
                   << this->getContext()->getLevel() << " size back to "
206
11502433
                   << this->d_size << " sizeAlloc at " << this->d_sizeAlloc
207
                   << std::endl;
208
11502433
  }
209
210
  /**
211
   * Given a size parameter smaller than d_size, truncateList()
212
   * removes the elements from the end of the list until d_size equals size.
213
   *
214
   * WARNING! You should only use this function when you know what you are doing.
215
   * This is a primitive operation with strange context dependent behavior!
216
   * It is up to the user of the function to ensure that the saved d_size values
217
   * at lower context levels are less than or equal to size.
218
   */
219
15423279
  void truncateList(const size_t size){
220
15423279
    Assert(size <= d_size);
221
15423279
    if(d_callDestructor) {
222
153693717
      while(d_size != size) {
223
69436060
        --d_size;
224
69436060
        d_cleanUp(&d_list[d_size]);
225
69436060
        d_allocator.destroy(&d_list[d_size]);
226
      }
227
    } else {
228
601682
      d_size = size;
229
    }
230
15423279
  }
231
232
 public:
233
  /**
234
   * Main constructor: d_list starts as NULL, size is 0
235
   */
236
1135833
  CDList(Context* context,
237
         bool callDestructor = true,
238
         const CleanUp& cleanup = CleanUp(),
239
         const Allocator& alloc = Allocator()) :
240
    ContextObj(context),
241
    d_list(NULL),
242
    d_size(0),
243
    d_callDestructor(callDestructor),
244
    d_sizeAlloc(0),
245
    d_cleanUp(cleanup),
246
1135833
    d_allocator(alloc) {
247
1135833
  }
248
249
  /**
250
   * Destructor: delete the list
251
   */
252
1135833
  ~CDList() {
253
1135833
    this->destroy();
254
255
1135833
    if(this->d_callDestructor) {
256
1116901
      truncateList(0);
257
    }
258
259
1135833
    this->d_allocator.deallocate(this->d_list, this->d_sizeAlloc);
260
2271666
  }
261
262
  /**
263
   * Return the current size of (i.e. valid number of objects in) the
264
   * list.
265
   */
266
102153429
  size_t size() const {
267
102153429
    return d_size;
268
  }
269
270
  /**
271
   * Return true iff there are no valid objects in the list.
272
   */
273
14152855
  bool empty() const {
274
14152855
    return d_size == 0;
275
  }
276
277
  /**
278
   * Add an item to the end of the list. This method uses the copy constructor
279
   * of T, so the type has to support it. As a result, this method cannot be
280
   * used with types that do not have a copy constructor such as
281
   * std::unique_ptr. Use CDList::emplace_back() instead of CDList::push_back()
282
   * to avoid this issue.
283
   */
284
77418274
  void push_back(const T& data) {
285
232254822
    Debug("cdlist") << "push_back " << this
286
154836548
                    << " " << getContext()->getLevel()
287
77418274
                    << ": make-current, "
288
77418274
                    << "d_list == " << d_list << std::endl;
289
77418274
    makeCurrent();
290
77418274
    grow();
291
77418272
    Assert(d_size < d_sizeAlloc);
292
293
232254816
    Debug("cdlist") << "push_back " << this
294
154836544
                    << " " << getContext()->getLevel()
295
77418272
                    << ": construct! at " << d_list
296
154836544
                    << "[" << d_size << "] == " << &d_list[d_size]
297
                    << std::endl;
298
77418272
    d_allocator.construct(&d_list[d_size], data);
299
232254816
    Debug("cdlist") << "push_back " << this
300
154836544
                    << " " << getContext()->getLevel()
301
77418272
                    << ": done..." << std::endl;
302
77418272
    ++d_size;
303
232254816
    Debug("cdlist") << "push_back " << this
304
154836544
                    << " " << getContext()->getLevel()
305
77418272
                    << ": size now " << d_size << std::endl;
306
77418272
  }
307
308
  /**
309
   * Construct an item to the end of the list. This method constructs the item
310
   * in-place (similar to std::vector::emplace_back()), so it can be used for
311
   * types that do not have a copy constructor such as std::unique_ptr.
312
   */
313
  template <typename... Args>
314
40
  void emplace_back(Args&&... args)
315
  {
316
120
    Debug("cdlist") << "emplace_back " << this << " "
317
80
                    << getContext()->getLevel() << ": make-current, "
318
40
                    << "d_list == " << d_list << std::endl;
319
40
    makeCurrent();
320
40
    grow();
321
40
    Assert(d_size < d_sizeAlloc);
322
323
120
    Debug("cdlist") << "emplace_back " << this << " "
324
80
                    << getContext()->getLevel() << ": construct! at " << d_list
325
80
                    << "[" << d_size << "] == " << &d_list[d_size] << std::endl;
326
80
    std::allocator_traits<AllocatorT>::construct(
327
40
        d_allocator, &d_list[d_size], std::forward<Args>(args)...);
328
120
    Debug("cdlist") << "emplace_back " << this << " "
329
80
                    << getContext()->getLevel() << ": done..." << std::endl;
330
40
    ++d_size;
331
120
    Debug("cdlist") << "emplace_back " << this << " "
332
80
                    << getContext()->getLevel() << ": size now " << d_size
333
                    << std::endl;
334
40
  }
335
336
  /**
337
   * Access to the ith item in the list.
338
   */
339
93112693
  const T& operator[](size_t i) const {
340
93112693
    Assert(i < d_size) << "index out of bounds in CDList::operator[]";
341
93112693
    return d_list[i];
342
  }
343
344
  /**
345
   * Returns the most recent item added to the list.
346
   */
347
4642
  const T& back() const {
348
4642
    Assert(d_size > 0) << "CDList::back() called on empty list";
349
4642
    return d_list[d_size - 1];
350
  }
351
352
  /**
353
   * Iterator for CDList class.  It has to be const because we don't
354
   * allow items in the list to be changed.  It's a straightforward
355
   * wrapper around a pointer.  Note that for efficiency, we implement
356
   * only prefix increment and decrement.  Also note that it's OK to
357
   * create an iterator from an empty, uninitialized list, as begin()
358
   * and end() will have the same value (NULL).
359
   */
360
  class const_iterator {
361
    T const* d_it;
362
363
2506791
    const_iterator(T const* it) : d_it(it) {}
364
365
    friend class CDList<T, CleanUp, Allocator>;
366
367
  public:
368
369
    // FIXME we support operator--, but do we satisfy all the
370
    // requirements of a bidirectional iterator ?
371
    typedef std::input_iterator_tag iterator_category;
372
    typedef T value_type;
373
    typedef std::ptrdiff_t difference_type;
374
    typedef const T* pointer;
375
    typedef const T& reference;
376
377
41528
    const_iterator() : d_it(NULL) {}
378
379
2
    inline bool operator==(const const_iterator& i) const {
380
2
      return d_it == i.d_it;
381
    }
382
383
17094064
    inline bool operator!=(const const_iterator& i) const {
384
17094064
      return d_it != i.d_it;
385
    }
386
387
16511553
    inline const T& operator*() const {
388
16511553
      return *d_it;
389
    }
390
391
    inline const T* operator->() const { return d_it; }
392
393
    /** Prefix increment */
394
16390703
    const_iterator& operator++() {
395
16390703
      ++d_it;
396
16390703
      return *this;
397
    }
398
399
    /** Prefix decrement */
400
    const_iterator& operator--() { --d_it; return *this; }
401
402
    /** operator+ */
403
65888
    const_iterator operator+(long signed int off) const {
404
65888
      return const_iterator(d_it + off);
405
    }
406
407
    // Postfix operations on iterators: requires a Proxy object to
408
    // hold the intermediate value for dereferencing
409
    class Proxy {
410
      const T* d_t;
411
412
    public:
413
414
      Proxy(const T& p): d_t(&p) {}
415
416
      T& operator*() {
417
        return *d_t;
418
      }
419
    };/* class CDList<>::const_iterator::Proxy */
420
421
    /** Postfix increment: returns Proxy with the old value. */
422
    Proxy operator++(int) {
423
      Proxy e(*(*this));
424
      ++(*this);
425
      return e;
426
    }
427
428
    /** Postfix decrement: returns Proxy with the old value. */
429
    Proxy operator--(int) {
430
      Proxy e(*(*this));
431
      --(*this);
432
      return e;
433
    }
434
435
  };/* class CDList<>::const_iterator */
436
  typedef const_iterator iterator;
437
438
  /**
439
   * Returns an iterator pointing to the first item in the list.
440
   */
441
678120
  const_iterator begin() const {
442
678120
    return const_iterator(static_cast<T const*>(d_list));
443
  }
444
445
  /**
446
   * Returns an iterator pointing one past the last item in the list.
447
   */
448
1762783
  const_iterator end() const {
449
1762783
    return const_iterator(static_cast<T const*>(d_list) + d_size);
450
  }
451
};/* class CDList<> */
452
453
template <class T, class CleanUp>
454
class CDList<T, CleanUp, ContextMemoryAllocator<T> > : public ContextObj {
455
  /* CDList is incompatible for use with a ContextMemoryAllocator.
456
   *
457
   * Explanation:
458
   * If ContextMemoryAllocator is used and d_list grows at a deeper context
459
   * level the reallocated will be reallocated in a context memory region that
460
   * can be destroyed on pop. To support this, a full copy of d_list would have
461
   * to be made. As this is unacceptable for performance in other situations, we
462
   * do not do this.
463
   */
464
465
  static_assert(sizeof(T) == 0,
466
                "Cannot create a CDList with a ContextMemoryAllocator.");
467
};
468
469
}  // namespace context
470
}  // namespace cvc5
471
472
#endif /* CVC5__CONTEXT__CDLIST_H */