CBMC
small_map.h
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Small map
4 
5 Author: Daniel Poetzl
6 
7 \*******************************************************************/
8 
11 
12 #ifndef CPROVER_UTIL_SMALL_MAP_H
13 #define CPROVER_UTIL_SMALL_MAP_H
14 
15 #include <bitset>
16 #include <cstdint>
17 #include <cstring>
18 #include <limits>
19 #include <memory>
20 #include <new>
21 #include <type_traits>
22 #include <utility>
23 
24 #include "invariant.h"
25 
26 //#define _SMALL_MAP_REALLOC_STATS
27 
67 template <typename T, typename Ind = uint32_t, std::size_t Num = 8>
69 {
70 public:
71  small_mapt() : ind(0), p(nullptr)
72  {
73  }
74 
75 private:
76  static_assert(std::is_unsigned<Ind>::value, "Ind should be an unsigned type");
77 
78  typedef Ind index_fieldt;
79 
80  friend void small_map_test();
81 
82  // Memory
83 
84  void copy(T *dest, const T *src, std::size_t n) const
85  {
86  for(std::size_t i = 0; i < n; i++)
87  {
88  new(dest + i) T(*(src + i));
89  }
90  }
91 
92  T *allocate(std::size_t n) const
93  {
94  if(n == 0)
95  return nullptr;
96 
97  T *mem = (T *)malloc(sizeof(T) * n);
98 
99  if(!mem)
100  throw std::bad_alloc();
101 
102  return mem;
103  }
104 
105  T *allocate(T *ptr, std::size_t n) const
106  {
107  if(n == 0)
108  return nullptr;
109 
110  // explicitly cast to char * as GCC 8 warns about not using new/delete for
111  // class sharing_node_innert<dstringt, std::basic_string<char>,
112  // std::equal_to<dstringt> >
113  T *mem = (T *)realloc((char *)ptr, sizeof(T) * n);
114 
115  if(!mem)
116  throw std::bad_alloc();
117 
118 #ifdef _SMALL_MAP_REALLOC_STATS
119  if(ptr == mem)
120  {
121  realloc_success++;
122  }
123  else if(ptr != nullptr)
124  {
125  realloc_failure++;
126  }
127 #endif
128 
129  return mem;
130  }
131 
132 public:
133  small_mapt(const small_mapt &m) : ind(m.ind), p(m.p)
134  {
135  if(m.p == nullptr)
136  {
137  return;
138  }
139 
140  const std::size_t n = m.size();
141  INVARIANT(n > 0, "size is > 0 if data pointer is non-null");
142 
143  p = allocate(n);
144 
145  copy(p, m.p, n);
146  }
147 
149  {
150  if(p)
151  {
152  std::size_t n = size();
153 
154  for(std::size_t i = 0; i < n; i++)
155  {
156  (p + i)->~T();
157  }
158 
159  free(p);
160  }
161  }
162 
163 private:
164  // Derived config
165 
166  static const std::size_t N_BITS = std::numeric_limits<index_fieldt>::digits;
167 
168  static const std::size_t NUM = Num;
169 
170  static_assert(NUM >= 2, "NUM should be at least 2");
171 
172  static constexpr std::size_t num_bits(const std::size_t n)
173  {
174  return n < 2 ? 1 : 1 + num_bits(n >> 1);
175  }
176 
177  static constexpr std::size_t S_BITS = NUM * num_bits(NUM - 1) + NUM;
178 
179  static const std::size_t BITS = num_bits(NUM - 1) + 1;
180 
181  static constexpr index_fieldt indicator_mask(const index_fieldt digit = 1)
182  {
183  return !digit ? 0 : digit | indicator_mask(digit << BITS);
184  }
185 
186  static const index_fieldt IND = indicator_mask();
187 
188  static const index_fieldt MASK = ((index_fieldt)1 << BITS) - 1;
189 
190  static_assert(S_BITS <= N_BITS, "S_BITS should be no larger than N_BITS");
191 
192  static_assert(
193  std::numeric_limits<std::size_t>::digits >= BITS,
194  "BITS must fit into an unsigned");
195 
196  // Internal
197 
198  std::size_t get_field(std::size_t field) const
199  {
200  PRECONDITION(field < NUM);
201 
202  std::size_t shift = field * BITS;
203  return (ind & (MASK << shift)) >> shift;
204  }
205 
206  void set_field(std::size_t field, std::size_t v)
207  {
208  PRECONDITION(field < NUM);
209  PRECONDITION((std::size_t)(v >> 1) < NUM);
210 
211  std::size_t shift = field * BITS;
212 
213  ind &= ~((index_fieldt)MASK << shift);
214  ind |= (index_fieldt)v << shift;
215  }
216 
217  void shift_indices(std::size_t ii)
218  {
219  for(std::size_t idx = 0; idx < S_BITS / BITS; idx++)
220  {
221  std::size_t v = get_field(idx);
222  if(v & 1)
223  {
224  v >>= 1;
225  if(v > ii)
226  {
227  v = ((v - 1) << 1) | 1;
228  set_field(idx, v);
229  }
230  }
231  }
232  }
233 
234 public:
235  // Standard const iterator
236 
237  typedef std::pair<const std::size_t, const T &> valuet;
238 
243  {
244  public:
245  explicit const_iterator(const small_mapt &m) : m(m), idx(0), ii(0)
246  {
247  find_next();
248  }
249 
251  const small_mapt &m,
252  const std::size_t idx,
253  const std::size_t ii)
254  : m(m), idx(idx), ii(ii)
255  {
256  }
257 
258  const valuet operator*() const
259  {
260  return valuet(idx, *(m.p + ii));
261  }
262 
263  const std::shared_ptr<valuet> operator->() const
264  {
265  return std::make_shared<valuet>(idx, *(m.p + ii));
266  }
267 
272  const T &get_value() const
273  {
274  return *(m.p + ii);
275  }
276 
278  {
279  idx++;
280  find_next();
281 
282  return *this;
283  }
284 
286  {
287  std::size_t old_idx = idx;
288  std::size_t old_ii = ii;
289 
290  idx++;
291  find_next();
292 
293  return const_iterator(m, old_idx, old_ii);
294  }
295 
296  bool operator==(const const_iterator &other) const
297  {
298  return idx == other.idx;
299  }
300 
301  bool operator!=(const const_iterator &other) const
302  {
303  return idx != other.idx;
304  }
305 
306  private:
307  void find_next()
308  {
309  while(idx < NUM)
310  {
311  std::size_t v = m.get_field(idx);
312  if(v & 1)
313  {
314  ii = v >> 1;
315  break;
316  }
317 
318  idx++;
319  }
320  }
321 
322  const small_mapt &m;
323  std::size_t idx;
324  std::size_t ii;
325  };
326 
328  {
329  return const_iterator(*this);
330  }
331 
333  {
334  return const_iterator(*this, NUM, 0);
335  }
336 
343  {
344  public:
345  const_value_iterator(const small_mapt &m, const int ii) : m(m), ii(ii)
346  {
347  }
348 
349  const T &operator*() const
350  {
351  return *(m.p + ii);
352  }
353 
354  const T *operator->() const
355  {
356  return m.p + ii;
357  }
358 
360  {
361  ii--;
362 
363  return *this;
364  }
365 
367  {
368  int old_ii = ii;
369 
370  ii--;
371 
372  return const_value_iterator(m, old_ii);
373  }
374 
375  bool operator==(const const_value_iterator &other) const
376  {
377  return ii == other.ii;
378  }
379 
380  bool operator!=(const const_value_iterator &other) const
381  {
382  return ii != other.ii;
383  }
384 
385  private:
386  const small_mapt &m;
387  int ii;
388  };
389 
391  {
392  return const_value_iterator(*this, size() - 1);
393  }
394 
396  {
397  return const_value_iterator(*this, -1);
398  }
399 
400  // Interface
401 
402  T &operator[](std::size_t idx)
403  {
404  PRECONDITION(idx < NUM);
405 
406  std::size_t v = get_field(idx);
407  if(v & 1)
408  {
409  std::size_t ii = v >> 1;
410  return *(p + ii);
411  }
412 
413  std::size_t ii = size();
414  p = allocate(p, ii + 1);
415  new(p + ii) T();
416 
417  v = (ii << 1) | 1;
418  set_field(idx, v);
419 
420  return *(p + ii);
421  }
422 
423  const_iterator find(std::size_t idx) const
424  {
425  PRECONDITION(idx < NUM);
426 
427  std::size_t v = get_field(idx);
428  if(v & 1)
429  {
430  std::size_t ii = v >> 1;
431  return const_iterator(*this, idx, ii);
432  }
433 
434  return end();
435  }
436 
437  std::size_t erase(std::size_t idx)
438  {
439  PRECONDITION(idx < NUM);
440 
441  std::size_t v = get_field(idx);
442 
443  if(v & 1)
444  {
445  std::size_t ii = v >> 1;
446 
447  (p + ii)->~T();
448  std::size_t n = size();
449  if(ii < n - 1)
450  {
451  // explicitly cast to char * as GCC 8 warns about not using new/delete
452  // for
453  // class sharing_node_innert<dstringt, std::basic_string<char>,
454  // std::equal_to<dstringt> >
455  memmove((char *)(p + ii), p + ii + 1, sizeof(T) * (n - ii - 1));
456  }
457 
458  p = allocate(p, n - 1);
459 
460  if(n == 1)
461  {
462  p = nullptr;
463  }
464 
465  set_field(idx, 0);
466  shift_indices(ii);
467 
468  return 1;
469  }
470 
471  return 0;
472  }
473 
474  small_mapt *copy_and_erase(std::size_t idx) const
475  {
476  PRECONDITION(idx < NUM);
477 
478  std::size_t v = get_field(idx);
479  INVARIANT(v & 1, "element must be in map");
480 
481  std::size_t ii = v >> 1;
482  std::size_t n = size();
483 
484  // new map
485 
486  small_mapt *m = new small_mapt();
487 
488  if(n == 1)
489  {
490  return m;
491  }
492 
493  m->p = allocate(n - 1);
494 
495  // copy part before erased element
496  copy(m->p, p, ii);
497 
498  // copy part after erased element
499  copy(m->p + ii, p + ii + 1, n - ii - 1);
500 
501  m->ind = ind;
502  m->set_field(idx, 0);
503  m->shift_indices(ii);
504 
505  return m;
506  }
507 
508  small_mapt *copy_and_insert(std::size_t idx, const T &value) const
509  {
510  PRECONDITION(idx < NUM);
511 
512  std::size_t v = get_field(idx);
513  INVARIANT(!(v & 1), "element must not be in map");
514 
515  std::size_t n = size();
516  std::size_t ii = n;
517 
518  small_mapt *m = new small_mapt();
519 
520  m->p = allocate(n + 1);
521 
522  // copy old elements
523  copy(m->p, p, n);
524 
525  // new element
526  new(m->p + ii) T(value);
527 
528  m->ind = ind;
529  v = (ii << 1) | 1;
530  m->set_field(idx, v);
531 
532  return m;
533  }
534 
535  std::size_t size() const
536  {
537  std::bitset<N_BITS> bs(ind & IND);
538  return bs.count();
539  }
540 
541  bool empty() const
542  {
543  return !ind;
544  }
545 
546 #ifdef _SMALL_MAP_REALLOC_STATS
547  mutable std::size_t realloc_failure = 0;
548  mutable std::size_t realloc_success = 0;
549 #endif
550 
551 private:
553  T *p;
554 };
555 
556 #endif
bool operator==(const const_iterator &other) const
Definition: small_map.h:296
const_iterator(const small_mapt &m, const std::size_t idx, const std::size_t ii)
Definition: small_map.h:250
const_iterator operator++()
Definition: small_map.h:277
bool operator!=(const const_iterator &other) const
Definition: small_map.h:301
const valuet operator*() const
Definition: small_map.h:258
const std::shared_ptr< valuet > operator->() const
Definition: small_map.h:263
const_iterator(const small_mapt &m)
Definition: small_map.h:245
const T & get_value() const
Non-standard direct access to the underlying value instead of creating a valuet object.
Definition: small_map.h:272
const_iterator operator++(int)
Definition: small_map.h:285
const small_mapt & m
Definition: small_map.h:322
Const value iterator.
Definition: small_map.h:343
const_value_iterator operator++(int)
Definition: small_map.h:366
const_value_iterator(const small_mapt &m, const int ii)
Definition: small_map.h:345
const_value_iterator operator++()
Definition: small_map.h:359
bool operator!=(const const_value_iterator &other) const
Definition: small_map.h:380
bool operator==(const const_value_iterator &other) const
Definition: small_map.h:375
const T & operator*() const
Definition: small_map.h:349
const T * operator->() const
Definition: small_map.h:354
Map from small integers to values.
Definition: small_map.h:69
static const std::size_t BITS
Definition: small_map.h:179
small_mapt * copy_and_insert(std::size_t idx, const T &value) const
Definition: small_map.h:508
T * allocate(std::size_t n) const
Definition: small_map.h:92
T & operator[](std::size_t idx)
Definition: small_map.h:402
static constexpr std::size_t S_BITS
Definition: small_map.h:177
static const std::size_t N_BITS
Definition: small_map.h:166
std::size_t size() const
Definition: small_map.h:535
small_mapt(const small_mapt &m)
Definition: small_map.h:133
const_iterator find(std::size_t idx) const
Definition: small_map.h:423
const_value_iterator value_begin() const
Definition: small_map.h:390
bool empty() const
Definition: small_map.h:541
static const index_fieldt IND
Definition: small_map.h:186
const_iterator end() const
Definition: small_map.h:332
void shift_indices(std::size_t ii)
Definition: small_map.h:217
const_iterator begin() const
Definition: small_map.h:327
index_fieldt ind
Definition: small_map.h:552
small_mapt()
Definition: small_map.h:71
static constexpr index_fieldt indicator_mask(const index_fieldt digit=1)
Definition: small_map.h:181
small_mapt * copy_and_erase(std::size_t idx) const
Definition: small_map.h:474
std::size_t erase(std::size_t idx)
Definition: small_map.h:437
const_value_iterator value_end() const
Definition: small_map.h:395
static constexpr std::size_t num_bits(const std::size_t n)
Definition: small_map.h:172
void set_field(std::size_t field, std::size_t v)
Definition: small_map.h:206
static const index_fieldt MASK
Definition: small_map.h:188
static const std::size_t NUM
Definition: small_map.h:168
std::pair< const std::size_t, const T & > valuet
Definition: small_map.h:237
std::size_t get_field(std::size_t field) const
Definition: small_map.h:198
friend void small_map_test()
void copy(T *dest, const T *src, std::size_t n) const
Definition: small_map.h:84
T * allocate(T *ptr, std::size_t n) const
Definition: small_map.h:105
Ind index_fieldt
Definition: small_map.h:76
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
void * realloc(void *ptr, __CPROVER_size_t malloc_size)
Definition: stdlib.c:542
void * malloc(__CPROVER_size_t malloc_size)
Definition: stdlib.c:212
void free(void *ptr)
Definition: stdlib.c:317
void * memmove(void *dest, const void *src, size_t n)
Definition: string.c:837