spot  2.16
bitvect.hh
1 // -*- coding: utf-8 -*-
2 // Copyright (C) by the Spot authors, see the AUTHORS file for details.
3 //
4 // This file is part of Spot, a model checking library.
5 //
6 // Spot is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // Spot is distributed in the hope that it will be useful, but WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 // License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 
19 #pragma once
20 
21 #include <spot/misc/common.hh>
22 #include <cstddef>
23 #include <cstdlib>
24 #include <cassert>
25 #include <iosfwd>
26 #include <iostream>
27 #include <algorithm>
28 #include <new>
29 
30 namespace spot
31 {
34 
35  class bitvect;
36  class bitvect_array;
37 
41  SPOT_API bitvect* make_bitvect(size_t bitcount);
42 
46  SPOT_API bitvect_array* make_bitvect_array(size_t bitcount,
47  size_t vectcount);
48 
50  class SPOT_API bitvect
51  {
52  private:
53  // Used by make_bitvect to construct a large bitvect in place.
54  bitvect(size_t size, size_t block_count);
55  bitvect(size_t size, size_t block_count, bool);
56 
57  public:
58  typedef unsigned long block_t;
59 
60  bitvect():
61  size_(0),
62  block_count_(1),
63  storage_(&local_storage_),
64  local_storage_(0)
65  {
66  }
67 
69  bitvect(const bitvect& other):
70  size_(other.size_),
71  block_count_(1),
72  storage_(&local_storage_)
73  {
74  *this = other;
75  }
76 
77  bitvect* clone() const;
78 
80  void operator delete(void *ptr)
81  {
82  // This object was allocated using a placement new.
83  ::operator delete(ptr);
84  }
85 
87  void make_empty()
88  {
89  size_ = 0;
90  }
91 
93  bitvect& operator=(const bitvect& other)
94  {
95  reserve_blocks(other.block_count_);
96  size_ = other.size();
97  for (size_t i = 0; i < block_count_; ++i)
98  storage_[i] = other.storage_[i];
99  return *this;
100  }
101 
102  ~bitvect()
103  {
104  if (storage_ != &local_storage_)
105  free(storage_);
106  }
107 
111  void reserve_blocks(size_t new_block_count)
112  {
113  if (new_block_count < block_count_)
114  return;
115  if (storage_ == &local_storage_)
116  {
117  block_t* new_storage = static_cast<block_t*>
118  (malloc(new_block_count * sizeof(block_t)));
119  if (SPOT_UNLIKELY(!new_storage))
120  throw std::bad_alloc();
121  for (size_t i = 0; i < block_count_; ++i)
122  new_storage[i] = storage_[i];
123  storage_ = new_storage;
124  }
125  else
126  {
127  block_t* new_storage = static_cast<block_t*>
128  (realloc(storage_, new_block_count * sizeof(block_t)));
129  if (SPOT_UNLIKELY(!new_storage))
130  // storage_, untouched, will be freed by the destructor.
131  throw std::bad_alloc();
132  storage_ = new_storage;
133  }
134  block_count_ = new_block_count;
135  }
136 
137  private:
138  void grow()
139  {
140  size_t new_block_count = (block_count_ + 1) * 7 / 5;
141  reserve_blocks(new_block_count);
142  }
143 
144  public:
145 
147  size_t used_blocks() const
148  {
149  const size_t bpb = 8 * sizeof(block_t);
150  return (size_ + bpb - 1) / bpb;
151  }
152 
154  size_t size() const
155  {
156  return size_;
157  }
158 
160  size_t capacity() const
161  {
162  return 8 * block_count_ * sizeof(block_t);
163  }
164 
166  size_t hash() const noexcept;
167 
169  bool get(size_t pos) const
170  {
171  SPOT_ASSERT(pos < size_);
172  const size_t bpb = 8 * sizeof(block_t);
173  return storage_[pos / bpb] & (1UL << (pos % bpb));
174  }
175 
177  void clear_all()
178  {
179  for (size_t i = 0; i < block_count_; ++i)
180  storage_[i] = 0;
181  }
182 
184  bool is_fully_clear() const
185  {
186  size_t i;
187  const size_t bpb = 8 * sizeof(bitvect::block_t);
188  size_t rest = size() % bpb;
189  for (i = 0; i < block_count_ - !!rest; ++i)
190  if (storage_[i] != 0)
191  return false;
192  // The last block might not be fully used, compare only the
193  // relevant portion.
194  if (!rest)
195  return true;
196  block_t mask = (1UL << rest) - 1;
197  return (storage_[i] & mask) == 0;
198  }
199 
201  bool is_fully_set() const
202  {
203  size_t i;
204  const size_t bpb = 8 * sizeof(bitvect::block_t);
205  size_t rest = size() % bpb;
206  for (i = 0; i < block_count_ - !!rest; ++i)
207  if (storage_[i] != -1UL)
208  return false;
209  if (!rest)
210  return true;
211  // The last block might not be fully used, compare only the
212  // relevant portion.
213  block_t mask = (1UL << rest) - 1;
214  return ((~storage_[i]) & mask) == 0;
215  }
216 
218  void set_all()
219  {
220  for (size_t i = 0; i < block_count_; ++i)
221  storage_[i] = -1UL;
222  }
223 
225  void flip_all()
226  {
227  for (size_t i = 0; i < block_count_; ++i)
228  storage_[i] = ~storage_[i];
229  }
230 
232  void set(size_t pos)
233  {
234  SPOT_ASSERT(pos < size_);
235  const size_t bpb = 8 * sizeof(block_t);
236  storage_[pos / bpb] |= 1UL << (pos % bpb);
237  }
238 
240  void clear(size_t pos)
241  {
242  SPOT_ASSERT(pos < size_);
243  const size_t bpb = 8 * sizeof(block_t);
244  storage_[pos / bpb] &= ~(1UL << (pos % bpb));
245  }
246 
248  void flip(size_t pos)
249  {
250  SPOT_ASSERT(pos < size_);
251  const size_t bpb = 8 * sizeof(block_t);
252  storage_[pos / bpb] ^= (1UL << (pos % bpb));
253  }
254 
255 
257  bitvect& operator|=(const bitvect& other)
258  {
259  SPOT_ASSERT(other.size_ <= size_);
260  unsigned m = std::min(other.block_count_, block_count_);
261  for (size_t i = 0; i < m; ++i)
262  storage_[i] |= other.storage_[i];
263  return *this;
264  }
265 
267  bitvect& operator&=(const bitvect& other)
268  {
269  SPOT_ASSERT(other.size_ <= size_);
270  unsigned m = std::min(other.block_count_, block_count_);
271  for (size_t i = 0; i < m; ++i)
272  storage_[i] &= other.storage_[i];
273  return *this;
274  }
275 
277  bitvect& add_common(const bitvect& other1, const bitvect& other2)
278  {
279  SPOT_ASSERT(other1.size_ <= size_ && other2.size_ <= size_);
280  unsigned m = std::min(other2.block_count_,
281  std::min(other1.block_count_, block_count_));
282  for (size_t i = 0; i < m; ++i)
283  storage_[i] |= other1.storage_[i] & other2.storage_[i];
284  return *this;
285  }
286 
288  bool intersects(const bitvect& other)
289  {
290  SPOT_ASSERT(other.size_ <= size_);
291  unsigned m = std::min(other.block_count_, block_count_);
292  for (size_t i = 0; i < m; ++i)
293  if (storage_[i] & other.storage_[i])
294  return true;
295  return false;
296  }
297 
299  bitvect& operator^=(const bitvect& other)
300  {
301  SPOT_ASSERT(other.size_ <= size_);
302  unsigned m = std::min(other.block_count_, block_count_);
303  for (size_t i = 0; i < m; ++i)
304  storage_[i] ^= other.storage_[i];
305  return *this;
306  }
307 
309  bitvect& operator-=(const bitvect& other)
310  {
311  SPOT_ASSERT(other.block_count_ <= block_count_);
312  for (size_t i = 0; i < other.block_count_; ++i)
313  storage_[i] &= ~other.storage_[i];
314  return *this;
315  }
316 
318  bool is_subset_of(const bitvect& other) const
319  {
320  SPOT_ASSERT(other.block_count_ >= block_count_);
321 
322  size_t i;
323  const size_t bpb = 8 * sizeof(bitvect::block_t);
324  size_t rest = size() % bpb;
325  for (i = 0; i < block_count_ - !!rest; ++i)
326  if ((storage_[i] & other.storage_[i]) != storage_[i])
327  return false;
328  if (!rest)
329  return true;
330 
331  // The last block might not be fully used, compare only the
332  // relevant portion.
333  block_t mask = (1UL << rest) - 1;
334  return ((storage_[i] & mask & other.storage_[i])
335  == (storage_[i] & mask));
336  }
337 
339  unsigned count() const
340  {
341  size_t i;
342  const size_t bpb = 8 * sizeof(bitvect::block_t);
343  size_t rest = size() % bpb;
344  size_t c = 0;
345  for (i = 0; i < block_count_; ++i)
346  {
347  block_t v = storage_[i];
348  if ((i == block_count_ - 1) && rest)
349  // The last block might not be fully used, scan only the
350  // relevant portion.
351  v &= (1UL << rest) - 1;
352 #ifdef __GNUC__
353  c += __builtin_popcountl(v);
354 #else
355  while (v)
356  {
357  ++c;
358  v &= v - 1;
359  }
360 #endif
361  }
362  return c;
363  }
364 
366  bool operator==(const bitvect& other) const
367  {
368  if (other.size_ != size_)
369  return false;
370  if (size_ == 0)
371  return true;
372  size_t i;
373  size_t m = other.used_blocks();
374  const size_t bpb = 8 * sizeof(bitvect::block_t);
375  size_t rest = size() % bpb;
376  for (i = 0; i < m - !!rest; ++i)
377  if (storage_[i] != other.storage_[i])
378  return false;
379  if (!rest)
380  return true;
381  // The last block might not be fully used, compare only the
382  // relevant portion.
383  block_t mask = (1UL << rest) - 1;
384  return (storage_[i] & mask) == (other.storage_[i] & mask);
385  }
386 
388  bool operator!=(const bitvect& other) const
389  {
390  return !(*this == other);
391  }
392 
394  bool operator<(const bitvect& other) const
395  {
396  if (size_ != other.size_)
397  return size_ < other.size_;
398  if (size_ == 0)
399  return false;
400  size_t i;
401  size_t m = other.used_blocks();
402  const size_t bpb = 8 * sizeof(bitvect::block_t);
403  size_t rest = size() % bpb;
404  for (i = 0; i < m - !!rest; ++i)
405  if (storage_[i] > other.storage_[i])
406  return false;
407  if (!rest)
408  return true;
409  // The last block might not be fully used, compare only the
410  // relevant portion.
411  block_t mask = (1UL << rest) - 1;
412  return (storage_[i] & mask) < (other.storage_[i] & mask);
413  }
414 
416  bool operator>=(const bitvect& other) const
417  {
418  return !(*this < other);
419  }
420 
422  bool operator>(const bitvect& other) const
423  {
424  return other < *this;
425  }
426 
428  bool operator<=(const bitvect& other) const
429  {
430  return !(other < *this);
431  }
432 
434  template<typename F>
435  void foreach_set_index(F callback) const
436  {
437  const size_t bpb = 8 * sizeof(bitvect::block_t);
438  size_t rest = size() % bpb;
439  for (size_t i = 0; i <= block_count_ - 1; ++i)
440  {
441  block_t b = storage_[i];
442  if ((i == block_count_ - 1) && rest)
443  // The last block might not be fully used, scan only the
444  // relevant portion.
445  b &= (1UL << rest) - 1;
446  if (b != 0)
447  {
448  unsigned base = i * bpb;
449 #if __GNUC__
450  // This version is probably faster on sparse bitsets.
451  do
452  {
453  unsigned bit = __builtin_ctzl(b);
454  b ^= 1UL << bit;
455  callback(base + bit);
456  }
457  while (b);
458 #else
459  unsigned bit = 0;
460  do
461  {
462  if (b & 1)
463  callback(base + bit);
464  ++bit;
465  b >>= 1;
466  }
467  while (b);
468 #endif
469  }
470  }
471  }
472 
473  friend SPOT_API bitvect* make_bitvect(size_t bitcount);
474 
476  friend SPOT_API std::ostream& operator<<(std::ostream&,
477  const bitvect&);
478 
479  private:
480  friend SPOT_API bitvect_array* make_bitvect_array(size_t bitcount,
481  size_t vectcount);
482 
483  size_t size_;
484  size_t block_count_;
485  // storage_ points to local_storage_ as long as size_ <= block_count_ * 8.
486  block_t* storage_;
487  // Keep this at the end of the structure: when make_bitvect is used,
488  // it may allocate more block_t at the end of this structure.
489  block_t local_storage_;
490  };
491 
493  class SPOT_API bitvect_array
494  {
495  private:
497  bitvect_array(size_t size, size_t bvsize):
498  size_(size),
499  bvsize_(bvsize)
500  {
501  }
502 
503  SPOT_LOCAL bitvect_array(const bitvect_array&) = delete;
504  SPOT_LOCAL void operator=(const bitvect_array&) = delete;
505 
506  // Extra storage has been allocated at the end of the struct.
507  char* storage()
508  {
509  return reinterpret_cast<char*>(this) + sizeof(*this);
510  }
511 
512  const char* storage() const
513  {
514  return reinterpret_cast<const char*>(this) + sizeof(*this);
515  }
516 
517  public:
518  ~bitvect_array()
519  {
520  for (size_t i = 0; i < size_; ++i)
521  at(i).~bitvect();
522  }
523 
525  void operator delete(void *ptr)
526  {
527  // This object was allocated using a placement new.
528  ::operator delete(ptr);
529  }
530 
532  size_t size() const
533  {
534  return size_;
535  }
536 
538  void clear_all()
539  {
540  // FIXME: This could be changed into a large memset if the
541  // individual vectors where not allowed to be reallocated.
542  for (unsigned s = 0; s < size_; s++)
543  at(s).clear_all();
544  }
545 
547  bitvect& at(const size_t index)
548  {
549  SPOT_ASSERT(index < size_);
550  // The double cast is to prevent -Wcast-align diagnostics
551  // about the fact that char* (the type of storage) has a
552  // smaller required alignment than bitvect*.
553  auto v = static_cast<void*>(storage() + index * bvsize_);
554  return *static_cast<bitvect*>(v);
555  }
556 
558  const bitvect& at(const size_t index) const
559  {
560  SPOT_ASSERT(index < size_);
561  auto v = static_cast<const void*>(storage() + index * bvsize_);
562  return *static_cast<const bitvect*>(v);
563  }
564 
565  friend SPOT_API bitvect_array* make_bitvect_array(size_t bitcount,
566  size_t vectcount);
567 
568 
570  friend SPOT_API std::ostream& operator<<(std::ostream&,
571  const bitvect_array&);
572 
573  private:
574  size_t size_;
575  size_t bvsize_;
576  };
577 
579 }
An array of fixed-size bit vectors allocated contiguously.
Definition: bitvect.hh:494
friend std::ostream & operator<<(std::ostream &, const bitvect_array &)
Print a bitvect_array.
bitvect & at(const size_t index)
Return the bit-vector at index.
Definition: bitvect.hh:547
void clear_all()
Clear all bits in all vectors of the array.
Definition: bitvect.hh:538
friend bitvect_array * make_bitvect_array(size_t bitcount, size_t vectcount)
Allocate vectcount bit-vectors of bitcount bits.
const bitvect & at(const size_t index) const
Return the bit-vector at index.
Definition: bitvect.hh:558
size_t size() const
The number of bitvect in the array.
Definition: bitvect.hh:532
A bit vector.
Definition: bitvect.hh:51
bitvect & operator-=(const bitvect &other)
Clear in *this the bits that are set in other.
Definition: bitvect.hh:309
friend bitvect * make_bitvect(size_t bitcount)
Allocate a bit-vector of bitcount bits.
bool operator<=(const bitvect &other) const
Lexicographic less-than-or-equal comparison.
Definition: bitvect.hh:428
void reserve_blocks(size_t new_block_count)
Definition: bitvect.hh:111
bool is_fully_set() const
Return true iff all bits are set to one.
Definition: bitvect.hh:201
friend std::ostream & operator<<(std::ostream &, const bitvect &)
Print a bitvect.
void set(size_t pos)
Set bit at position pos to one.
Definition: bitvect.hh:232
bool is_fully_clear() const
Return true iff all bits are zero.
Definition: bitvect.hh:184
friend bitvect_array * make_bitvect_array(size_t bitcount, size_t vectcount)
Allocate vectcount bit-vectors of bitcount bits.
bool intersects(const bitvect &other)
Return true if *this and other share any set bit.
Definition: bitvect.hh:288
void make_empty()
Reset the size to zero without freeing storage.
Definition: bitvect.hh:87
bool operator==(const bitvect &other) const
Equality comparison.
Definition: bitvect.hh:366
unsigned long block_t
Storage block type.
Definition: bitvect.hh:58
bool is_subset_of(const bitvect &other) const
Return true if every set bit of *this is also set in other.
Definition: bitvect.hh:318
bitvect & add_common(const bitvect &other1, const bitvect &other2)
OR into *this the bits common to other1 and other2.
Definition: bitvect.hh:277
bitvect * clone() const
Return a heap-allocated copy.
void set_all()
Set all bits to one.
Definition: bitvect.hh:218
bool operator>(const bitvect &other) const
Lexicographic greater-than comparison.
Definition: bitvect.hh:422
void foreach_set_index(F callback) const
Call callback with the index of each set bit.
Definition: bitvect.hh:435
size_t hash() const noexcept
Return a hash of the bit vector.
bitvect & operator=(const bitvect &other)
Copy-assignment operator.
Definition: bitvect.hh:93
size_t capacity() const
Return the maximum number of bits the storage can hold.
Definition: bitvect.hh:160
bitvect & operator&=(const bitvect &other)
In-place bitwise AND with other.
Definition: bitvect.hh:267
bitvect & operator|=(const bitvect &other)
In-place bitwise OR with other.
Definition: bitvect.hh:257
void clear(size_t pos)
Clear bit at position pos (set it to zero).
Definition: bitvect.hh:240
size_t size() const
Return the number of bits in the vector.
Definition: bitvect.hh:154
size_t used_blocks() const
Return the number of blocks currently in use.
Definition: bitvect.hh:147
void clear_all()
Clear all bits (set them to zero).
Definition: bitvect.hh:177
void flip(size_t pos)
Flip the bit at position pos.
Definition: bitvect.hh:248
bool operator>=(const bitvect &other) const
Lexicographic greater-than-or-equal comparison.
Definition: bitvect.hh:416
bool operator!=(const bitvect &other) const
Inequality comparison.
Definition: bitvect.hh:388
bitvect & operator^=(const bitvect &other)
In-place bitwise XOR with other.
Definition: bitvect.hh:299
bitvect(const bitvect &other)
Copy constructor.
Definition: bitvect.hh:69
unsigned count() const
Return the number of set bits (popcount).
Definition: bitvect.hh:339
void flip_all()
Flip all bits.
Definition: bitvect.hh:225
bool operator<(const bitvect &other) const
Lexicographic less-than comparison.
Definition: bitvect.hh:394
Definition: automata.hh:26
bitvect_array * make_bitvect_array(size_t bitcount, size_t vectcount)
Allocate vectcount bit-vectors of bitcount bits.
bitvect * make_bitvect(size_t bitcount)
Allocate a bit-vector of bitcount bits.

Please direct any question, comment, or bug report to the Spot mailing list at spot@lrde.epita.fr.
Generated on Fri Feb 27 2015 10:00:07 for spot by doxygen 1.9.1