HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
compressedBits.h
Go to the documentation of this file.
1 //
2 // Copyright 2024 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_TF_COMPRESSED_BITS_H
8 #define PXR_BASE_TF_COMPRESSED_BITS_H
9 
10 #include "pxr/base/arch/align.h"
11 #include "pxr/base/tf/api.h"
12 #include "pxr/base/tf/diagnostic.h"
13 #include "pxr/base/tf/hash.h"
14 #include "pxr/base/tf/iterator.h"
15 #include "pxr/base/tf/staticData.h"
16 
17 #include <algorithm>
18 #include <cstdint>
19 #include <iosfwd>
20 #include <string>
21 #include <type_traits>
22 #include <vector>
23 
25 
26 /// Forward Declarations
27 ///
28 class TfBits;
29 
30 /// \class TfCompressedBits
31 ///
32 /// \brief Fast, compressed bit array which is capable of performing logical
33 /// operations without first decompressing the internal data representation.
34 ///
35 /// The internal data compression is based on a form of RLE, where words are
36 /// used to indicate the number of bits set to the same value. Each subsequent
37 /// word denotes that the bit value has changed and a "runningBit" is set
38 /// internally, in order to denote the bit value for the first word.
39 ///
40 /// Internally, a bitset like this:
41 ///
42 /// 111000101000
43 ///
44 /// Will be represented as:
45 ///
46 /// 1 331113
47 ///
48 /// i.e., the running bit is '1', and there are 3 of those, followed by 3
49 /// zeroes, followed by 1 one, followed by 1 zero, followed by 1 one, followed
50 /// by three zeroes. Each word is called a "platform".
51 ///
52 /// Compressed bits are very fast when used for logical operations (conjugate,
53 /// and, or, xor, etc.), and when iterated over. Contains and Overlaps are also
54 /// very fast. The representation is lightweight in memory and hence very cache
55 /// efficient.
56 ///
57 /// Whenever indexing, setting and resetting of seemingly random bits is a
58 /// requirement, however, TfBits will perform better, since finding a specific
59 /// bit requires a linear search.
60 ///
62 {
63 private:
64  // Type of one word stored in the word array
65  typedef uint32_t _WordType;
66 
67  // Lightweight, re-allocating array type optimized for native, word data.
68  //
69  // Note, this is not a std::vector, because we really want a container,
70  // which is optimized for native types, allowing fast memcpy capabilities,
71  // and providing local storage optimizations.
72  class _WordArray
73  {
74  public:
75  static const uint32_t LOCAL_SIZE = 6;
76 
77  _WordArray() :
78  _data(_local),
79  _numAllocated(LOCAL_SIZE),
80  _num(0) {}
81 
82  _WordArray(const _WordArray &rhs) :
83  _data(_local),
84  _numAllocated(LOCAL_SIZE),
85  _num(rhs._num) {
86  _Duplicate(rhs);
87  }
88 
89  ~_WordArray() {
90  _Deallocate();
91  }
92 
93  _WordArray& operator=(const _WordArray &rhs) {
94  if (this == &rhs) {
95  return *this;
96  }
97 
98  _Duplicate(rhs);
99  return *this;
100  }
101 
102  // Clear all words
103  void Clear() {
104  _num = 0;
105  }
106 
107  // Add a word (may cause re-allocation)
108  void PushBack(_WordType value) {
109  // Reallocate?
110  if (_num >= _numAllocated) {
111  _Reallocate();
112  }
113 
114  // PushBack
115  _data[_num] = value;
116  ++_num;
117  }
118 
119  // Remove a word
120  void PopBack() {
121  --_num;
122  }
123 
124  // Remove multiple words
125  void PopBackNum(uint32_t popNum) {
126  _num -= popNum;
127  }
128 
129  // Move this representation into rhs. This representation will be
130  // invalid after this operation.
131  void MoveInto(_WordArray &rhs) {
132  rhs._numAllocated = _numAllocated;
133  rhs._num = _num;
134 
135  rhs._Deallocate();
136 
137  // If the data is stored locally, copy it
138  if (_IsStoredLocally()) {
139  // The compiler will unroll this loop, making this faster
140  // than memcpy!
141  for (size_t i = 0; i < LOCAL_SIZE; ++i) {
142  rhs._data[i] = _data[i];
143  }
144  }
145 
146  // Otherwise, just assign pointers
147  else {
148  rhs._data = _data;
149 
150  // Set our pointer back to local, so we won't deallocate the
151  // storage, which is now owned by rhs.
152  _data = _local;
153  _numAllocated = LOCAL_SIZE;
154  }
155  }
156 
157  // Swap two representations
158  void Swap(_WordArray &rhs) {
159  if (!_IsStoredLocally() && !rhs._IsStoredLocally()) {
160  std::swap(_data, rhs._data);
161  std::swap(_numAllocated, rhs._numAllocated);
162  std::swap(_num, rhs._num);
163  } else {
164  // Fall back to a copy. This could be optimized.
165  std::swap(*this, rhs);
166  }
167  }
168 
169  // Index operator
170  _WordType &operator[](size_t index) {
171  return _data[index];
172  }
173 
174  const _WordType &operator[](size_t index) const {
175  return _data[index];
176  }
177 
178  // Returns the number of words stored (not allocated)
179  uint32_t GetNum() const {
180  return _num;
181  }
182 
183  // Return the number of allocated words
184  uint32_t GetNumAllocated() const {
185  return _numAllocated;
186  }
187 
188  // Return a pointer to the first word
189  const _WordType *Begin() const {
190  return _data;
191  }
192 
193  // Return a pointer one past the end of the array.
194  const _WordType *End() const {
195  return _data + _num;
196  }
197 
198  // Returns the first word
199  _WordType &Front() {
200  return _data[0];
201  }
202 
203  const _WordType &Front() const {
204  return _data[0];
205  }
206 
207  // Returns the last word
208  _WordType &Back() {
209  return _data[_num - 1];
210  }
211 
212  const _WordType &Back() const {
213  return _data[_num - 1];
214  }
215 
216  private:
217  bool _IsStoredLocally() const {
218  return _data == _local;
219  }
220 
221  void _Deallocate() {
222  if (!_IsStoredLocally()) {
223  delete[] _data;
224  _data = _local;
225  }
226  }
227 
228  void _Duplicate(const _WordArray &rhs) {
229  if (rhs._num > 0) {
230  if (_numAllocated < rhs._num) {
231  _Deallocate();
232  _data = new _WordType[rhs._numAllocated];
233  _numAllocated = rhs._numAllocated;
234  }
235 
236  if (rhs._IsStoredLocally()) {
237  // The compiler will unroll this loop, making this faster
238  // than memcpy!
239  for (size_t i = 0; i < LOCAL_SIZE; ++i) {
240  _data[i] = rhs._data[i];
241  }
242  } else {
243  memcpy(_data, rhs._data, sizeof(_WordType) * rhs._num);
244  }
245  }
246 
247  _num = rhs._num;
248  }
249 
250  void _Reallocate() {
251  _numAllocated <<= 1;
252  _WordType *newData = new _WordType[_numAllocated];
253  memcpy(newData, _data, sizeof(_WordType) * _num);
254  _Deallocate();
255  _data = newData;
256  }
257 
258  // Pointer to the data
259  _WordType *_data;
260 
261  // Local storage optimization
262  _WordType _local[LOCAL_SIZE];
263 
264  // Number of words allocated
265  uint32_t _numAllocated;
266 
267  // Number of words stored
268  uint32_t _num;
269  };
270 
271 public:
272 
273  // View and iterator modes: All bits, all set bits, all unset bits,
274  // platforms (iterator provides platform size and value)
275  enum class Mode { All, AllSet, AllUnset, Platforms };
276 
277  /// Hash for TfCompressedBits.
278  ///
279  /// This hash is linear in time as it considers all the words between.
280  /// If you need a constant-time hash, see FastHash, it may be suitable for
281  /// your needs.
282  ///
283  struct Hash {
284  size_t operator()(const TfCompressedBits &bits) const {
285  return bits.GetHash();
286  }
287  };
288 
289  /// A hash functor for TfCompressedBits that is faster than Hash.
290  ///
291  /// This hash can be computed in constant time because it only uses a
292  /// fixed subset of data: the number of bits in total, the running bit,
293  /// the number of words and the first cache line of words.
294  ///
295  struct FastHash {
296  size_t operator()(const TfCompressedBits &bits) const {
297  if (bits.GetSize() == 0) {
298  return 0;
299  }
300 
301  // Hash the running bit and number of platforms.
302  size_t hash = TfHash::Combine(
303  bits.GetSize(),
304  bits._runningBit,
305  bits._platforms.GetNum());
306 
307  // Hash a single cache line of platform data.
308  const uint32_t n = std::min<uint32_t>(
309  bits._platforms.GetNum(),
310  ARCH_CACHE_LINE_SIZE / sizeof(uint32_t));
311  for (uint32_t i = 0; i < n; ++i) {
312  hash = TfHash::Combine(hash, bits._platforms[i]);
313  }
314 
315  return hash;
316  }
317  };
318 
319  /// Constructs a fixed size bit array, clears all bits.
320  ///
321  explicit TfCompressedBits(size_t num = 0) :
322  _num(num),
323  _runningBit(0) {
324  _platforms.PushBack(num);
325  }
326 
327  /// Constructs a fixed size bit array, with a range of bits set.
328  ///
329  explicit TfCompressedBits(size_t num, size_t first, size_t last) :
330  _num(num),
331  _runningBit(0) {
332 
333  // Empty bitset
334  if (num == 0) {
335  _platforms.PushBack(0);
336  return;
337  }
338 
339  // Range error (clear the whole bitset):
340  if (!TF_VERIFY(first < num && last < num && first <= last)) {
341  _platforms.PushBack(num);
342  return;
343  }
344 
345  size_t trailingZeroes = 0;
346  const size_t range = last - first + 1;
347  if (first == 0) {
348  _runningBit = 1;
349  _platforms.PushBack(range);
350  trailingZeroes = num - range;
351  } else {
352  _platforms.PushBack(first);
353  _platforms.PushBack(range);
354  trailingZeroes = num - last - 1;
355  }
356 
357  // Only push trailing zeroes, if there are any. Otherwise the
358  // _platforms array will be in an inconsistent state (containing
359  // platforms of size 0, when _num != 0).
360  if (trailingZeroes != 0) {
361  _platforms.PushBack(trailingZeroes);
362  }
363  }
364 
365  /// Copy-constructs a fixed size bit array.
366  ///
368  _platforms(rhs._platforms),
369  _num(rhs._num),
370  _runningBit(rhs._runningBit) {}
371 
372  /// Copy-construct a fixed sized bit array, from the complement of the
373  /// \p rhs bitset.
374  ///
377  _platforms(rhs._platforms),
378  _num(rhs._num),
379  _runningBit(1 - rhs._runningBit) {
380  if (_num == 0) {
381  _runningBit = 0;
382  }
383  }
384 
385  /// Construct a TfCompressedBits array from a TfBits array.
386  ///
387  TF_API
388  explicit TfCompressedBits(const TfBits &bits);
389 
390  /// Move Constructor
391  ///
393  _num(rhs._num),
394  _runningBit(rhs._runningBit) {
395  rhs._platforms.MoveInto(_platforms);
396  rhs._platforms.Clear();
397  rhs._num = 0;
398  rhs._runningBit = 0;
399  }
400 
401  /// Destructor
402  ///
404 
405  /// Assignment operator
406  ///
408  if (this == &rhs) {
409  return *this;
410  }
411 
412  _platforms = rhs._platforms;
413  _num = rhs._num;
414  _runningBit = rhs._runningBit;
415 
416  return *this;
417  }
418 
419  /// Move assignment operator.
420  ///
422  if (this == &rhs) {
423  return *this;
424  }
425 
426  _num = rhs._num;
427  _runningBit = rhs._runningBit;
428  rhs._platforms.MoveInto(_platforms);
429  rhs._platforms.Clear();
430  rhs._num = 0;
431  rhs._runningBit = 0;
432 
433  return *this;
434  }
435 
436  /// Resize the bitset, while keeping the contents, unless trimmed.
437  ///
438  void ResizeKeepContents(size_t num) {
439  if (_num == num) {
440  return;
441  }
442 
443  // Reduce size to 0
444  if (num == 0) {
445  _platforms.Clear();
446  _platforms.PushBack(0);
447  _runningBit = 0;
448  _num = 0;
449  return;
450  }
451 
452  // Grow
453  if (_num < num) {
454  if ((UINT32_C(1) - _runningBit) ==
455  (_platforms.GetNum() & UINT32_C(1))) {
456  _platforms.Back() += (num - _num);
457  } else {
458  _platforms.PushBack(num - _num);
459  }
460  }
461 
462  // Shrink
463  else if (_num > num) {
464  uint32_t diff = _num - num;
465  while (_platforms.Back() <= diff) {
466  diff -= _platforms.Back();
467  _platforms.PopBack();
468  }
469  _platforms.Back() -= diff;
470  }
471 
472  _num = num;
473  }
474 
475  /// Provides a fast swap.
476  ///
477  void Swap(TfCompressedBits &rhs) {
478  std::swap(_num, rhs._num);
479  std::swap(_runningBit, rhs._runningBit);
480  _platforms.Swap(rhs._platforms);
481  }
482 
483  /// Clears all bits to zero.
484  ///
485  void ClearAll() {
486  if (_num <= 0 || (_runningBit == 0 && _platforms.GetNum() == 1)) {
487  return;
488  }
489 
490  _runningBit = 0;
491  _platforms.Clear();
492  _platforms.PushBack(_num);
493  }
494 
495  /// Sets all bits to one.
496  ///
497  void SetAll() {
498  if (_num <= 0 || (_runningBit == 1 && _platforms.GetNum() == 1)) {
499  return;
500  }
501 
502  _runningBit = 1;
503  _platforms.Clear();
504  _platforms.PushBack(_num);
505  }
506 
507  /// Clears bit # index to zero.
508  ///
509  /// Note: This is a slow operation on TfCompressedBits!
510  ///
511  void Clear(size_t index) {
512  if (!TF_VERIFY(index < _num)) {
513  return;
514  }
515 
516  TfCompressedBits tmp(_num, index, index);
517  tmp.Complement();
518  *this &= tmp;
519  }
520 
521  /// Sets bit # index to zero.
522  ///
523  /// Note: This is a slow operation on TfCompressedBits!
524  ///
525  void Set(size_t index) {
526  if (!TF_VERIFY(index < _num)) {
527  return;
528  }
529 
530  TfCompressedBits tmp(_num, index, index);
531  *this |= tmp;
532  }
533 
534  /// Sets the bit within the range of first and last.
535  ///
536  /// Note: This is a slow operation on TfCompressedBits!
537  ///
538  void SetRange(size_t first, size_t last) {
539  // Range constructor does error checking
540  TfCompressedBits tmp(_num, first, last);
541  *this |= tmp;
542  }
543 
544  /// Append a number of bits with the given \p value to this bitset.
545  /// This also increases the size of the bitset by the number of bits added.
546  ///
547  void Append(size_t num, bool value) {
548  if (num == 0) {
549  return;
550  }
551 
552  if (_num == 0) {
553  _platforms[0] = num;
554  _runningBit = value;
555  _num = num;
556  return;
557  }
558 
559  const bool lastValue = _runningBit == (_platforms.GetNum() & 1);
560  if (value == lastValue) {
561  _platforms.Back() += num;
562  } else {
563  _platforms.PushBack(num);
564  }
565 
566  _num += num;
567  }
568 
569  /// Assigns val to bit # index.
570  ///
571  void Assign(size_t index, bool value) {
572  if (value) {
573  Set(index);
574  } else {
575  Clear(index);
576  }
577  }
578 
579  /// Shift this bitset a given number of \p bits to the right, and extend to
580  /// the left with zeroes.
581  ///
582  void ShiftRight(size_t bits) {
583  if (_num == 0 || bits == 0) {
584  return;
585  }
586 
587  // If the running bit is 0, just increment the first word (num zeroes)
588  if (_runningBit == 0) {
589  _platforms.Front() += bits;
590  }
591 
592  // If the running bit is 1, shift all the _platforms to the right and
593  // flip the running bit. Set the first platform (num zeroes) to the
594  // number of bits shifted.
595  else {
596  _runningBit = 0;
597  _platforms.PushBack(0);
598  for (size_t i = _platforms.GetNum() - 1; i > 0; --i) {
599  _platforms[i] = _platforms[i - 1];
600  }
601  _platforms[0] = bits;
602  }
603 
604  // Now trim the _platforms on the right
605  while (_platforms.Back() <= bits) {
606  bits -= _platforms.Back();
607  _platforms.PopBack();
608  }
609  _platforms.Back() -= bits;
610  }
611 
612  /// Shift this bitset a given number of \p bits to the left, and extend the
613  /// right with zeroes.
614  ///
615  void ShiftLeft(size_t bits) {
616  if (_num == 0 || bits == 0) {
617  return;
618  }
619 
620  // How many platforms to trim on the left?
621  size_t trimBits = bits;
622  size_t platformIndex = 0;
623  while (platformIndex < _platforms.GetNum() &&
624  _platforms[platformIndex] <= trimBits) {
625  trimBits -= _platforms[platformIndex];
626  ++platformIndex;
627  }
628 
629  // Reduce the size of the first platform or, if the shift clears the
630  // whole bitset, remove all platforms.
631  if (platformIndex < _platforms.GetNum()) {
632  _platforms[platformIndex] -= trimBits;
633  } else {
634  _platforms.Clear();
635  _runningBit = 0;
636  platformIndex = 0;
637  }
638 
639  // Are there any platforms to be trimmed on the left?
640  if (platformIndex > 0) {
641  // Shift the platforms to the left, by the number of
642  // platforms trimmed
643  const size_t last = _platforms.GetNum() - platformIndex;
644  for (size_t i = 0; i < last; ++i) {
645  _platforms[i] = _platforms[i + platformIndex];
646  }
647  _platforms.PopBackNum(platformIndex);
648 
649  // Flip the running bit, if necessary
650  if (platformIndex & 1) {
651  _runningBit = 1 - _runningBit;
652  }
653  }
654 
655  // Extend on the right, by adding zeros, if the last platform
656  // is zeros ...
657  if ((UINT32_C(1) - _runningBit) ==
658  (_platforms.GetNum() & UINT32_C(1))) {
659  _platforms.Back() += bits;
660  return;
661  }
662 
663  // ... or adding a new platform with zeros, if the last platform
664  // is ones
665  _platforms.PushBack(std::min<_WordType>(_num, bits));
666  }
667 
668  /// Returns true, if bit # index is set.
669  ///
670  /// Note: This is a slow operation on TfCompressedBits.
671  /// Please, use an iterator if possible. Iterators are fast!
672  ///
673  bool IsSet(size_t index) const {
674  if (!TF_VERIFY(index < _num)) {
675  return false;
676  }
677 
678  size_t platformIndex, bitCount;
679  return _LinearSearch(index, &platformIndex, &bitCount) == 1;
680  }
681 
682  /// Returns the index of the n-th bit set in this bit set.
683  ///
684  /// This function counts the set bits up to the \p nth bit, and returns
685  /// the index of that n-th set bit. If there are less than \p nth bits set,
686  /// returns GetSize().
687  ///
688  /// Note: This operation is slower than using an iterator. For forward or
689  /// reverse iteration, use the iterator instead.
690  ///
691  size_t FindNthSet(size_t nth) const {
692  size_t index = 0;
693  size_t count = 0;
694  uint8_t bit = _runningBit;
695 
696  // Iterate over all platforms to find the nth set bit using a running
697  // count of set bits, and an up-to-date index into the bitset.
698  for (size_t i = 0; i < _platforms.GetNum(); ++i) {
699  const _WordType platform = _platforms[i];
700 
701  // Since bit toggles between 1 and 0 for every iteration of the
702  // loop, using it in a conditional guarantees a misprediction every
703  // time. Doing the multiplication instead is cheap and doesn't
704  // change the result of the conditional until we find the right
705  // index.
706  if (((count + platform) * bit) > nth) {
707  return index + (nth - count);
708  }
709 
710  index += platform;
711  count += (platform * bit);
712  bit = 1 - bit;
713  }
714 
715  // Less than nth bits are set, so return the size.
716  return _num;
717  }
718 
719  /// Find the next bit set that is higher or equal to index.
720  /// If no more set bits are found, index returns 'GetSize()'.
721  ///
722  /// Note: This is a slow operation on TfCompressedBits.
723  /// Please, use an iterator if possible. Iterators are fast!
724  ///
725  size_t FindNextSet(size_t index) const
726  {
727  if (index >= _num) {
728  return _num;
729  }
730 
731  size_t platformIndex, bitCount;
732  const uint8_t bit = _LinearSearch(index, &platformIndex, &bitCount);
733 
734  if (bit == 1) {
735  return index;
736  }
737 
738  return bitCount;
739  }
740 
741  /// Finds the next unset bit that has a higher or equal index than index.
742  /// If no more set bits are found, index returns 'GetSize()'.
743  ///
744  /// Note: This is a slow operation on TfCompressedBits.
745  /// Please, use an iterator if possible. Iterators are fast!
746  ///
747  size_t FindPrevSet(size_t index) const
748  {
749  if (index >= _num) {
750  return _num;
751  }
752 
753  size_t platformIndex, bitCount;
754  const uint8_t bit = _LinearSearch(index, &platformIndex, &bitCount);
755 
756  if (bit == 1) {
757  return index;
758  }
759 
760  const size_t first = bitCount - _platforms[platformIndex];
761  if (first > 0) {
762  return first - 1;
763  }
764 
765  return _num;
766  }
767 
768  /// Finds the next unset bit that has a higher or equal index than index.
769  /// If no more set bits are found, index returns 'GetSize()'.
770  ///
771  /// Note: This is a slow operation on TfCompressedBits.
772  /// Please, use an iterator if possible. Iterators are fast!
773  ///
774  size_t FindNextUnset(size_t index) const
775  {
776  if (index >= _num) {
777  return _num;
778  }
779 
780  size_t platformIndex, bitCount;
781  const uint8_t bit = _LinearSearch(index, &platformIndex, &bitCount);
782 
783  if (bit == 0) {
784  return index;
785  }
786 
787  return bitCount;
788  }
789 
790  /// Count the bits set, and also return the largest gap between bits.
791  ///
792  void Count(size_t *numSet, size_t *maxGap) const {
793  const uint32_t lastIndex = _platforms.GetNum() - 1;
794  uint32_t num = 0;
795  uint32_t max = 0;
796  uint8_t bit = _runningBit;
797  for (size_t i = 0; i < _platforms.GetNum(); ++i) {
798  // Accumulate set bits.
799  if (bit == 1) {
800  num += _platforms[i];
801  }
802  // Don't account the leading and trailing zeros as gaps.
803  else if (i > 0 && i < lastIndex) {
804  max = std::max(max, _platforms[i]);
805  }
806  bit = 1 - bit;
807  }
808  *numSet = num;
809  *maxGap = max;
810  }
811 
812  /// Returns the size of the bit array, ie. the # of bits it can hold.
813  ///
814  size_t GetSize() const {
815  return _num;
816  }
817 
818  /// Returns \c true if this bit array is empty, i.e. it is of size zero.
819  ///
820  bool IsEmpty() const {
821  return _num == 0;
822  }
823 
824  /// Returns the index of the first bit set in the bit array. If no bits
825  /// are set, the return value is 'GetSize()'.
826  ///
827  size_t GetFirstSet() const {
828  if (_num == 0 || _runningBit == 1) {
829  return 0;
830  }
831 
832  return _platforms.Front();
833  }
834 
835  /// Returns the index of the last bit set in the bit array. If no bits
836  /// are set, the return value is 'GetSize()'.
837  ///
838  size_t GetLastSet() const {
839  // Zero size or all zeros case
840  if (_num == 0 || (_runningBit == 0 && _platforms.GetNum() == 1)) {
841  return _num;
842  }
843 
844  // If _runningBit == 1 and number of words is odd or
845  // _runningBit == 0 and number of words is even
846  if (_runningBit == (_platforms.GetNum() & 1)) {
847  return _num - 1;
848  }
849 
850  return _num - 1 - _platforms.Back();
851  }
852 
853  /// Returns the number of bits currently set in this array.
854  ///
855  size_t GetNumSet() const {
856  size_t numSet = 0;
857  for (size_t i = 1 - _runningBit; i < _platforms.GetNum(); i += 2) {
858  numSet += _platforms[i];
859  }
860  return numSet;
861  }
862 
863  /// Returns the number of platforms (zeros or ones) in this bitset.
864  ///
865  size_t GetNumPlatforms() const {
866  if (_num == 0) {
867  return 0;
868  }
869 
870  return _platforms.GetNum();
871  }
872 
873  /// Returns the number of set (ones) platforms in this bitset.
874  ///
875  size_t GetNumSetPlatforms() const {
876  if (_num == 0) {
877  return 0;
878  }
879 
880  const uint32_t numP = _platforms.GetNum();
881  return (numP / 2) + (numP & _runningBit);
882  }
883 
884  /// Returns the number of unset (zeros) platforms in this bitset.
885  ///
886  size_t GetNumUnsetPlatforms() const {
887  if (_num == 0) {
888  return 0;
889  }
890 
891  const uint32_t numP = _platforms.GetNum();
892  return (numP / 2) + (numP & (1 - _runningBit));
893  }
894 
895  /// Returns true, if all the bits in this bit array are set.
896  ///
897  bool AreAllSet() const {
898  return _num == 0 || (_runningBit == 1 && _platforms.GetNum() == 1);
899  }
900 
901  /// Returns true, if all the bits in this bit array are unset.
902  ///
903  bool AreAllUnset() const {
904  return !IsAnySet();
905  }
906 
907  /// Returns true, if there is at least a single set bit.
908  ///
909  bool IsAnySet() const {
910  return _num > 0 && (_runningBit == 1 || _platforms.GetNum() > 1);
911  }
912 
913  /// Returns true, if there is at least a single unset bit.
914  ///
915  bool IsAnyUnset() const {
916  return _num > 0 && (_runningBit == 0 || _platforms.GetNum() > 1);
917  }
918 
919  /// Returns true if the set bits in this bit array are contiguous.
920  ///
921  /// Note: This returns false if there are no set bits.
922  ///
923  bool AreContiguouslySet() const {
924  const uint32_t numP = _platforms.GetNum();
925  return
926  _num > 0 && numP <= 3 &&
927  (numP == 2 ||
928  (_runningBit == 1 && numP == 1) ||
929  (_runningBit == 0 && numP == 3));
930  }
931 
932  /// Returns the amount of memory this object holds on to.
933  ///
934  size_t GetAllocatedSize() const
935  {
936  size_t size = sizeof(TfCompressedBits);
937  if (_platforms.GetNumAllocated() > _WordArray::LOCAL_SIZE) {
938  size += sizeof(_WordType) * _platforms.GetNumAllocated();
939  }
940  return size;
941  }
942 
943  /// Returns a hash for this instance.
944  ///
945  TF_API
946  size_t GetHash() const;
947 
948  /// Returns a string representing the bits for debugging with bits
949  /// ordered from left to right with increasing indices.
950  ///
951  TF_API
952  std::string GetAsStringLeftToRight() const;
953 
954  /// Returns a string representing the bits for debugging with bits
955  /// ordered from right to left with increasing indices.
956  ///
957  TF_API
958  std::string GetAsStringRightToLeft() const;
959 
960  /// Returns a string representing the bits for debugging with bits
961  /// represented in run-length encoding form.
962  ///
963  TF_API
964  std::string GetAsRLEString() const;
965 
966  /// Returns a bitset constructed from the supplied string representation.
967  ///
968  /// The string representation can be either a RLE encoded bitset, such as
969  /// '1x5-0x5-1x100', or a string of zeros and ones, such as '1111100000'.
970  /// Note that whitespace anywhere in the string representation is ignored.
971  ///
972  /// Any character other than whitespace, a digit, 'x' or '-' in the string
973  /// representation is considered invalid. Invalid string representations
974  /// will return an empty bitset.
975  /// An empty string representation (or a string purely comprised of
976  /// whitespace), however, is considered a valid representation describing
977  /// an empty bitset.
978  ///
979  TF_API
980  static TfCompressedBits FromString(const std::string &source);
981 
982  /// \name Operators
983  /// @{
984 
985  /// Returns true if this == \p rhs.
986  ///
987  bool operator==(const TfCompressedBits &rhs) const {
988  if (this == &rhs || (_num == 0 && rhs._num == 0)) {
989  return true;
990  }
991 
992  // Fast comparisons, first
993  if (_num == rhs._num &&
994  _runningBit == rhs._runningBit &&
995  _platforms.GetNum() == rhs._platforms.GetNum()) {
996 
997  // Worst case, scenario: Must compare every word
998  for (size_t i = 0; i < _platforms.GetNum(); ++i) {
999  // Early bailout, if two words don't match
1000  if (_platforms[i] != rhs._platforms[i]) {
1001  return false;
1002  }
1003  }
1004 
1005  // All words match
1006  return true;
1007  }
1008 
1009  // Early comparison failed
1010  return false;
1011  }
1012 
1013  /// Returns true if this != \p rhs.
1014  ///
1015  bool operator!=(const TfCompressedBits &rhs) const {
1016  return !(*this == rhs);
1017  }
1018 
1019  /// Ands these bits with the \p rhs bits.
1020  ///
1021  /// The resulting bit set is the intersection of the two bit sets.
1022  ///
1024  if (!TF_VERIFY(_num == rhs._num) ||
1025  _num == 0 || rhs._num == 0) {
1026  return *this;
1027  }
1028 
1029  const uint32_t numA = _platforms.GetNum();
1030  const uint32_t numB = rhs._platforms.GetNum();
1031  const uint8_t bitA = _runningBit;
1032  const uint8_t bitB = rhs._runningBit;
1033 
1034  // Early bailout: This is all zeroes or all ones
1035  if (numA == 1) {
1036  if (bitA == 0) {
1037  return *this;
1038  }
1039 
1040  _runningBit = bitB;
1041  _platforms = rhs._platforms;
1042  return *this;
1043  }
1044 
1045  // Early bailout: Rhs is all zeroes or all ones
1046  if (numB == 1) {
1047  if (bitB == 1) {
1048  return *this;
1049  }
1050 
1051  ClearAll();
1052  return *this;
1053  }
1054 
1055  // Early bailout: No bits will overlap, if sets are disjoint
1056  if (_AreBoundsDisjoint(rhs)) {
1057  ClearAll();
1058  return *this;
1059  }
1060 
1061  return _Logical<_And>(bitB, rhs._platforms);
1062  }
1063 
1064  /// Returns these bits and'ed with \p rhs.
1065  ///
1067  TfCompressedBits r(*this);
1068  r &= rhs;
1069  return r;
1070  }
1071 
1072  /// Ors these bits with the \p rhs bits.
1073  ///
1074  /// The resulting bit set is the union of the two bit sets.
1075  ///
1077  if (!TF_VERIFY(_num == rhs._num) ||
1078  _num == 0 || rhs._num == 0) {
1079  return *this;
1080  }
1081 
1082  const uint32_t numA = _platforms.GetNum();
1083  const uint32_t numB = rhs._platforms.GetNum();
1084  const uint8_t bitA = _runningBit;
1085  const uint8_t bitB = rhs._runningBit;
1086 
1087  // Early bailout: This is all zeroes or all ones
1088  if (numA == 1) {
1089  if (bitA == 1) {
1090  return *this;
1091  }
1092 
1093  _runningBit = bitB;
1094  _platforms = rhs._platforms;
1095  return *this;
1096  }
1097 
1098  // Early bailout: Rhs is all zeroes or all ones
1099  if (numB == 1) {
1100  if (bitB == 0) {
1101  return *this;
1102  }
1103 
1104  SetAll();
1105  return *this;
1106  }
1107 
1108  // If this set already contains all the bits in rhs, there is no
1109  // point in proceeding with the full logical OR. Note, that although
1110  // this operation needs to look at all the platforms, it only performs
1111  // reads from memory, which makes it faster than the logical OR. If
1112  // this check fails, the data is already prefetched and ready to be
1113  // consumed by the logical OR.
1114  if (Contains(rhs)) {
1115  return *this;
1116  }
1117 
1118  return _Logical<_Or>(bitB, rhs._platforms);
1119  }
1120 
1121  /// Returns these bits or'ed with the \p rhs.
1122  ///
1124  TfCompressedBits r(*this);
1125  r |= rhs;
1126  return r;
1127  }
1128 
1129  /// Xors these bits with the \p rhs bits.
1130  ///
1131  /// The resulting bit set is the union of the two bit sets minus the
1132  /// intersection of the two bit sets.
1133  ///
1135  if (!TF_VERIFY(_num == rhs._num) ||
1136  _num == 0 || rhs._num == 0) {
1137  return *this;
1138  }
1139 
1140  // Early bailout: This is all zeroes
1141  if (AreAllUnset()) {
1142  *this = rhs;
1143  return *this;
1144  }
1145 
1146  // Early bailout: Rhs is all zeroes
1147  if (rhs.AreAllUnset()) {
1148  return *this;
1149  }
1150 
1151  return _Logical<_Xor>(rhs._runningBit, rhs._platforms);
1152  }
1153 
1154  /// Returns these bits xor'ed with \p rhs.
1155  ///
1157  TfCompressedBits r(*this);
1158  r ^= rhs;
1159  return r;
1160  }
1161 
1162  /// Removes all bits in the \p rhs bits from these bits.
1163  ///
1164  /// The resulting bit set is the asymmetric set difference of
1165  /// the two bit sets.
1166  ///
1168  if (!TF_VERIFY(_num == rhs._num) ||
1169  _num == 0 || rhs._num == 0) {
1170  return *this;
1171  }
1172 
1173  const uint32_t numA = _platforms.GetNum();
1174  const uint32_t numB = rhs._platforms.GetNum();
1175  const uint8_t bitA = _runningBit;
1176  const uint8_t bitB = rhs._runningBit;
1177 
1178  // Early bailout: This is all zeroes or all ones
1179  if (numA == 1) {
1180  if (bitA == 0) {
1181  return *this;
1182  }
1183 
1184  _runningBit = 1 - bitB;
1185  _platforms = rhs._platforms;
1186  return *this;
1187  }
1188 
1189  // Early bailout: Rhs is all zeroes or all ones
1190  if (numB == 1) {
1191  if (bitB == 0) {
1192  return *this;
1193  }
1194 
1195  ClearAll();
1196  return *this;
1197  }
1198 
1199  // Early bailout: No bits will be subtracted, if sets are disjoint.
1200  // Note, that although this operation needs to look at all the
1201  // platforms, it only performs reads from memory, which makes it faster
1202  // than the logical AND. If this check fails, the data is already
1203  // prefetched and ready to be consumed by the logical AND.
1204  if (_AreBoundsDisjoint(rhs) || !HasNonEmptyIntersection(rhs)) {
1205  return *this;
1206  }
1207 
1208  return _Logical<_And>(1 - bitB, rhs._platforms);
1209  }
1210 
1211  /// Returns bits with all the bits in \p rhs removed from these bits.
1212  ///
1214  TfCompressedBits r(*this);
1215  r -= rhs;
1216  return r;
1217  }
1218 
1219  /// Flips all bits.
1220  ///
1221  /// The resulting bit set is the complement of this bit set.
1222  ///
1224  if (_num != 0) {
1225  _runningBit = 1 - _runningBit;
1226  }
1227  return *this;
1228  }
1229 
1230  /// Returns bit at \p index.
1231  ///
1232  /// Note: This is a slow operation on TfCompressedBits!
1233  ///
1234  bool operator[](size_t index) const {
1235  return IsSet(index);
1236  }
1237 
1238  /// Shifts to the right (see ShiftRight)
1239  ///
1241  ShiftRight(bits);
1242  return *this;
1243  }
1244 
1245  /// Returns bits shifted to the right.
1246  ///
1247  TfCompressedBits operator>>(size_t bits) const {
1248  TfCompressedBits r(*this);
1249  r >>= bits;
1250  return r;
1251  }
1252 
1253  /// Shifts to the left (see ShiftLeft)
1254  ///
1256  ShiftLeft(bits);
1257  return *this;
1258  }
1259 
1260  /// Returns bits shifted to the left.
1261  ///
1262  TfCompressedBits operator<<(size_t bits) const {
1263  TfCompressedBits r(*this);
1264  r <<= bits;
1265  return r;
1266  }
1267 
1268  /// @}
1269 
1270 
1271  /// Returns true if the result of the intersection with \p rhs would be
1272  /// non-zero.
1273  ///
1274  /// This method can be used for efficiency because it doesn't perform
1275  /// the full AND operation on a copy, and it can return early.
1276  ///
1278  if (!TF_VERIFY(_num == rhs._num) ||
1279  _num == 0 || rhs._num == 0) {
1280  return false;
1281  }
1282 
1283  uint8_t bitA = _runningBit;
1284  uint8_t bitB = rhs._runningBit;
1285  if (bitA & bitB) {
1286  return true;
1287  }
1288 
1289  const uint32_t numA = _platforms.GetNum();
1290  const uint32_t numB = rhs._platforms.GetNum();
1291  if (numA == 1) {
1292  if (bitA == 0) {
1293  return false;
1294  }
1295 
1296  return rhs.IsAnySet();
1297  }
1298 
1299  if (numB == 1) {
1300  if (bitB == 0) {
1301  return false;
1302  }
1303 
1304  return IsAnySet();
1305  }
1306 
1307  // We can bail out early if the ranges of set bits do not overlap
1308  if (_AreBoundsDisjoint(rhs)) {
1309  return false;
1310  }
1311 
1312  return _HasLogical<_And>(bitB, rhs._platforms);
1313  }
1314 
1315  /// Returns true if the result of an asymmetric set different is non-zero.
1316  /// This is the equivalent to computing:
1317  /// return (this - rhs).GetNumSet() != 0
1318  /// but avoids creating temporary copies.
1319  ///
1320  bool HasNonEmptyDifference(const TfCompressedBits &rhs) const {
1321  if (!TF_VERIFY(_num == rhs._num) ||
1322  _num == 0 || rhs._num == 0) {
1323  return false;
1324  }
1325 
1326  uint8_t bitA = _runningBit;
1327  uint8_t bitB = rhs._runningBit;
1328  if (bitA && !bitB) {
1329  return true;
1330  }
1331 
1332  const uint32_t numA = _platforms.GetNum();
1333  const uint32_t numB = rhs._platforms.GetNum();
1334  if (numA == 1) {
1335  if (bitA == 0) {
1336  return false;
1337  }
1338 
1339  return rhs.IsAnyUnset();
1340  }
1341 
1342  if (numB == 1) {
1343  if (bitB == 0) {
1344  return IsAnySet();
1345  }
1346 
1347  return false;
1348  }
1349 
1350  // We can bail out early, if the ranges of set bits do not overlap.
1351  // Check the first set bits first, because checking for the last set
1352  // bit is more expensive.
1353  const size_t firstSet = GetFirstSet();
1354  const size_t rhsFirstSet = rhs.GetFirstSet();
1355  if (firstSet < rhsFirstSet) {
1356  return true;
1357  }
1358 
1359  // If we still haven't bailed out yet, check the last set bit.
1360  const size_t lastSet = GetLastSet();
1361  const size_t rhsLastSet = rhs.GetLastSet();
1362  if (lastSet > rhsLastSet ||
1363  firstSet > rhsLastSet ||
1364  lastSet < rhsFirstSet) {
1365  return true;
1366  }
1367 
1368  return _HasLogical<_And>(1 - bitB, rhs._platforms);
1369  }
1370 
1371  /// Returns true if this bit array contains \p rhs by computing:
1372  /// (rhs - this).GetNumSet() == 0.
1373  ///
1374  /// Ie. it will return true if all bits of \p rhs are also set in this.
1375  ///
1376  bool Contains(const TfCompressedBits &rhs) const {
1377  return !rhs.HasNonEmptyDifference(*this);
1378  }
1379 
1380  /// Returns an empty TfBits.
1381  ///
1382  static const TfCompressedBits &GetEmpty() {
1383  static TfStaticData<TfCompressedBits> emptyBits;
1384  return *emptyBits;
1385  }
1386 
1387  /// Decompress the bits into a TfBits array.
1388  ///
1389  TF_API
1390  void Decompress(TfBits *bits) const;
1391 
1392  /// Iterator support.
1393  ///
1394  template <Mode mode>
1395  class View;
1396 
1397  /// Returns an iteratable view for the bits that steps over all bits.
1398  ///
1399  typedef View<Mode::All> AllView;
1400  inline AllView GetAllView() const;
1401 
1402  /// Returns an iteratable view for the bits that steps over all set bits.
1403  ///
1405  inline AllSetView GetAllSetView() const;
1406 
1407  /// Returns an iteratable view for the bits that steps over all unset bits.
1408  ///
1410  inline AllUnsetView GetAllUnsetView() const;
1411 
1412  /// Returns an iteratable view for the bits that steps over all platforms.
1413  ///
1415  inline PlatformsView GetPlatformsView() const;
1416 
1417 private:
1418  // Functor for logical operation: AND
1419  struct _And {
1420  inline uint8_t operator() (uint8_t a, uint8_t b) {
1421  return a & b;
1422  }
1423  };
1424 
1425  // Functor for logical operation: OR
1426  struct _Or {
1427  inline uint8_t operator() (uint8_t a, uint8_t b) {
1428  return a | b;
1429  }
1430  };
1431 
1432  // Functor for logical operation: XOR
1433  struct _Xor {
1434  inline uint8_t operator() (uint8_t a, uint8_t b) {
1435  return a ^ b;
1436  }
1437  };
1438 
1439  // This method performs a logical operation on the passed in running bit
1440  // and word array. OP denotes a functor implementing the logical operation.
1441  // The idea is that the compiler will be smart enough to inline the
1442  // operation, without actually having to call the function.
1443  template < class OP > TfCompressedBits &
1444  _Logical(uint8_t rhsRunningBit, const _WordArray &rhsPlatforms) {
1445  OP op;
1446 
1447  const uint32_t numA = _platforms.GetNum();
1448  const uint32_t numB = rhsPlatforms.GetNum();
1449  uint8_t bitA = _runningBit;
1450  uint8_t bitB = rhsRunningBit;
1451 
1452  uint8_t b = op(bitA, bitB);
1453  _WordArray result;
1454  _runningBit = b;
1455 
1456  size_t indexA = 0;
1457  size_t indexB = 0;
1458  _WordType platformA = _platforms[indexA];
1459  _WordType platformB = rhsPlatforms[indexB];
1460 
1461  uint32_t newTotal = 0;
1462  _WordType newPlatform = 0;
1463 
1464  while (true) {
1465  if (platformA < platformB) {
1466  newTotal += platformA;
1467  newPlatform += platformA;
1468  bitA = 1 - bitA;
1469 
1470  // Commit the new platform
1471  const uint8_t newBit = op(bitA, bitB);
1472  if (newBit != b) {
1473  result.PushBack(newPlatform);
1474  newPlatform = 0;
1475  b = newBit;
1476  }
1477 
1478  // Move on to the next platform
1479  ++indexA;
1480  platformB = platformB - platformA;
1481  if (indexA == numA) {
1482  platformA = _num - newTotal;
1483  } else if (indexA < numA) {
1484  platformA = _platforms[indexA];
1485  }
1486 
1487  } else if (platformA > platformB) {
1488  newTotal += platformB;
1489  newPlatform += platformB;
1490  bitB = 1 - bitB;
1491 
1492  // Commit the new platform
1493  const uint8_t newBit = op(bitA, bitB);
1494  if (newBit != b) {
1495  result.PushBack(newPlatform);
1496  newPlatform = 0;
1497  b = newBit;
1498  }
1499 
1500  // Move on to the next platform
1501  ++indexB;
1502  platformA = platformA - platformB;
1503  if (indexB == numB) {
1504  platformB = _num - newTotal;
1505  } else if(indexB < numB) {
1506  platformB = rhsPlatforms[indexB];
1507  }
1508 
1509  } else {
1510  newTotal += platformA;
1511  newPlatform += platformA;
1512  bitA = 1 - bitA;
1513  bitB = 1 - bitB;
1514 
1515  // Commit the new platform
1516  const uint8_t newBit = op(bitA, bitB);
1517  if (newBit != b || newTotal >= _num) {
1518  result.PushBack(newPlatform);
1519  newPlatform = 0;
1520  b = newBit;
1521  }
1522 
1523  if (newTotal >= _num)
1524  break;
1525 
1526  // Move on to the next platforms
1527  ++indexA;
1528  if (indexA == numA) {
1529  platformA = _num - newTotal;
1530  } else if (indexA < numA) {
1531  platformA = _platforms[indexA];
1532  }
1533 
1534  ++indexB;
1535  if (indexB == numB) {
1536  platformB = _num - newTotal;
1537  } else if (indexB < numB) {
1538  platformB = rhsPlatforms[indexB];
1539  }
1540  }
1541  }
1542 
1543  result.MoveInto(_platforms);
1544  return *this;
1545  }
1546 
1547  // Performs a logical operation, but breaks out and returns true, as soon
1548  // as the logical operation returns true. If the logical operation never
1549  // returns true, false is returned.
1550  template < class OP > bool
1551  _HasLogical(uint8_t rhsRunningBit, const _WordArray &rhsPlatforms) const {
1552  OP op;
1553 
1554  uint8_t bitA = _runningBit;
1555  uint8_t bitB = rhsRunningBit;
1556  const uint32_t numA = _platforms.GetNum();
1557  const uint32_t numB = rhsPlatforms.GetNum();
1558 
1559  size_t indexA = 0;
1560  size_t indexB = 0;
1561  _WordType sumPlatformA = _platforms[indexA];
1562  _WordType sumPlatformB = rhsPlatforms[indexB];
1563  while (indexA < numA && indexB < numB) {
1564  if (op(bitA, bitB)) {
1565  return true;
1566  }
1567 
1568  if (sumPlatformA < sumPlatformB) {
1569  bitA = 1 - bitA;
1570  ++indexA;
1571  sumPlatformA += _platforms[indexA];
1572 
1573  } else if (sumPlatformA > sumPlatformB) {
1574  bitB = 1 - bitB;
1575  ++indexB;
1576  sumPlatformB += rhsPlatforms[indexB];
1577 
1578  } else {
1579  bitA = 1 - bitA;
1580  bitB = 1 - bitB;
1581  ++indexA;
1582  ++indexB;
1583 
1584  if (indexA >= numA || indexB >= numB) {
1585  return false;
1586  }
1587 
1588  sumPlatformA += _platforms[indexA];
1589  sumPlatformB += rhsPlatforms[indexB];
1590  }
1591  }
1592 
1593  return false;
1594  }
1595 
1596  // Do a liner search for the bit index, returning its bit value.
1597  // Also returns the index of that bit in the word array, as well as the
1598  // bitCount denoting the number of bits counted up until the range that
1599  // terminates the current word, the index is found in.
1600  uint8_t _LinearSearch(
1601  size_t index, size_t *platformIndex, size_t *bitCount) const {
1602  uint8_t bit = _runningBit;
1603  size_t count = 0;
1604  size_t i;
1605 
1606  for (i = 0; i < _platforms.GetNum(); ++i) {
1607  count += _platforms[i];
1608  if (count > index) {
1609  break;
1610  }
1611  bit = 1 - bit;
1612  }
1613 
1614  *platformIndex = i;
1615  *bitCount = count;
1616  return bit;
1617  }
1618 
1619  // Returns true if this bit array's bounds are disjoint from the bounds
1620  // of the rhs bit array. The two are considered disjoint if the last bit
1621  // set of array A is at a lower index than the first bit set on array B
1622  // (or vice versa).
1623  // Note, that the bit arrays may still be disjoint, even if this method
1624  // returns false, but if this method returns true, the bit arrays are
1625  // guaranteed to be disjoint. This is basically a very cheap early out for
1626  // the Overlaps() method.
1627  bool _AreBoundsDisjoint(const TfCompressedBits &rhs) const {
1628  return
1629  GetLastSet() < rhs.GetFirstSet() ||
1630  GetFirstSet() > rhs.GetLastSet();
1631  }
1632 
1633  // The word array, storing the bit platforms.
1634  _WordArray _platforms;
1635 
1636  // The size of this bit array in number of bits.
1637  uint32_t _num;
1638 
1639  // The value of the running bit, indicating what the bit value of the first
1640  // word is.
1641  uint8_t _runningBit;
1642 
1643 };
1644 
1645 template <TfCompressedBits::Mode mode>
1647 {
1648 public:
1650  {
1651  public:
1652  using iterator_category = std::forward_iterator_tag;
1653  using value_type = const uint32_t;
1654  using reference = const uint32_t &;
1655  using pointer = const uint32_t *;
1656  using difference_type = const uint32_t;
1657 
1659  _bits(nullptr),
1660  _platformIndex(0),
1661  _bitIndex(0),
1662  _bitCounter(0),
1663  _value(0)
1664  {}
1665 
1666  reference operator*() const { return dereference(); }
1667  pointer operator->() const { return &(dereference()); }
1668 
1670  increment();
1671  return *this;
1672  }
1673 
1675  const_iterator r(*this);
1676  increment();
1677  return r;
1678  }
1679 
1680  bool operator==(const const_iterator& rhs) const {
1681  return equal(rhs);
1682  }
1683 
1684  bool operator!=(const const_iterator& rhs) const {
1685  return !equal(rhs);
1686  }
1687 
1688  bool IsSet() const {
1689  return _value == 1;
1690  }
1691 
1692  bool IsAtEnd() const {
1693  if (!_bits) {
1694  return true;
1695  }
1696  return _bitIndex >= _bits->GetSize();
1697  }
1698 
1699  private:
1700  friend class View;
1701 
1703  const TfCompressedBits *bits,
1704  uint32_t platformIndex,
1705  uint32_t bitIndex,
1706  uint8_t value) :
1707  _bits(bits),
1708  _platformIndex(platformIndex),
1709  _bitIndex(bitIndex),
1710  _bitCounter(0),
1711  _value(value)
1712  {}
1713 
1714  bool equal(const const_iterator &rhs) const {
1715  return _bits == rhs._bits && _bitIndex == rhs._bitIndex;
1716  }
1717 
1718  void increment() {
1719  // Note, this looks like quite a bit of logic, but mode is a
1720  // compile time constant. The compiler to the rescue!
1721  if (!_bits) {
1722  return;
1723  }
1724 
1725  // Increment bit index
1726  ++_bitIndex;
1727 
1728  // Increment bit counter (counting the current word)
1729  ++_bitCounter;
1730 
1731  // If the bit counter surpasses the current word,
1732  // skip ahead to the next word
1733  if (_bitCounter >= _bits->_platforms[_platformIndex]) {
1734 
1735  // If the iterator mode is not All, look at
1736  // every other word
1737  const uint32_t numP =
1738  _bits->_platforms.GetNum();
1739  if ((mode == Mode::AllSet || mode == Mode::AllUnset) &&
1740  (_platformIndex + 1) < numP) {
1741  _bitIndex += _bits->_platforms[_platformIndex + 1];
1742  _platformIndex += 2;
1743  }
1744 
1745  // Otherwise, look at every word and toggle
1746  // the value bit
1747  else {
1748  ++_platformIndex;
1749  _value = 1 - _value;
1750  }
1751 
1752  // Reset the bit counter
1753  _bitCounter = 0;
1754  }
1755  }
1756 
1757  const uint32_t &dereference() const {
1758  return _bitIndex;
1759  }
1760 
1761  const TfCompressedBits *_bits;
1762  uint32_t _platformIndex;
1763  uint32_t _bitIndex;
1764  uint32_t _bitCounter;
1765  uint8_t _value;
1766  };
1767 
1768  // Support for TF_FOR_ALL.
1770 
1772  const uint8_t bit = _bits->_runningBit;
1773 
1774  // Skip ahead one word, if looking at AllSet/AllUnset and the
1775  // first word describes an unset/set platform of bits
1776  if ((mode == Mode::AllSet && bit == 0) ||
1777  (mode == Mode::AllUnset && bit == 1)) {
1778  return const_iterator(_bits, 1, _bits->_platforms[0], 1 - bit);
1779  }
1780 
1781  return const_iterator(_bits, 0, 0, bit);
1782  }
1783 
1785  return const_iterator(_bits, 0, _bits->GetSize(), 0);
1786  }
1787 
1788  /// Return true, if the view is empty.
1789  ///
1790  bool IsEmpty() const {
1791  return begin() == end();
1792  }
1793 
1794 private:
1795 
1796  // The TfCompressedBits can create new views.
1797  friend class TfCompressedBits;
1798 
1799  // Ctor.
1800  View(const TfCompressedBits *bits) :
1801  _bits(bits)
1802  {}
1803 
1804  const TfCompressedBits *_bits;
1805 };
1806 
1807 // Specialize the platform view because its iterators are much simpler than
1808 // the per-bit views.
1809 template <>
1811 {
1812 public:
1813  class const_iterator
1814  {
1815  public:
1816  using iterator_category = std::forward_iterator_tag;
1817  using value_type = const uint32_t;
1818  using reference = const uint32_t &;
1819  using pointer = const uint32_t *;
1820  using difference_type = const uint32_t;
1821 
1823  _platform(nullptr),
1824  _bitIndex(0),
1825  _value(0)
1826  {}
1827 
1828  reference operator*() const { return dereference(); }
1829  pointer operator->() const { return &(dereference()); }
1830 
1832  increment();
1833  return *this;
1834  }
1835 
1837  const_iterator r(*this);
1838  increment();
1839  return r;
1840  }
1841 
1842  bool operator==(const const_iterator& rhs) const {
1843  return equal(rhs);
1844  }
1845 
1846  bool operator!=(const const_iterator& rhs) const {
1847  return !equal(rhs);
1848  }
1849 
1850  bool IsSet() const {
1851  return _value == 1;
1852  }
1853 
1854  uint32_t GetPlatformSize() const {
1855  return *_platform;
1856  }
1857 
1858  private:
1859  friend class View;
1860 
1862  const uint32_t *platform,
1863  uint8_t value)
1864  : _platform(platform)
1865  , _bitIndex(0)
1866  , _value(value)
1867  {}
1868 
1869  bool equal(const const_iterator &rhs) const {
1870  return _platform == rhs._platform;
1871  }
1872 
1873  void increment() {
1874  _bitIndex += *_platform;
1875  ++_platform;
1876  _value = 1 - _value;
1877  }
1878 
1879  const uint32_t &dereference() const {
1880  return _bitIndex;
1881  }
1882 
1883  const uint32_t *_platform;
1884  uint32_t _bitIndex;
1885  uint8_t _value;
1886  };
1887 
1888  const_iterator begin() const {
1889  return const_iterator(_bits->_platforms.Begin(), _bits->_runningBit);
1890  }
1891 
1892  const_iterator end() const {
1893  return const_iterator(_bits->_platforms.End(), 0);
1894  }
1895 
1896  /// Return true, if the view is empty.
1897  ///
1898  bool IsEmpty() const {
1899  return begin() == end();
1900  }
1901 
1902 private:
1903 
1904  // The TfCompressedBits can create new views.
1905  friend class TfCompressedBits;
1906 
1907  // Ctor.
1908  View(const TfCompressedBits *bits) :
1909  _bits(bits)
1910  {}
1911 
1912  const TfCompressedBits *_bits;
1913 };
1914 
1917 {
1918  return View<Mode::All>(this);
1919 }
1920 
1923 {
1924  return View<Mode::AllSet>(this);
1925 }
1926 
1929 {
1930  return View<Mode::AllUnset>(this);
1931 }
1932 
1935 {
1936  return View<Mode::Platforms>(this);
1937 }
1938 
1939 // Specializing, so TfIterator knows to retain a copy when iterating.
1940 template<>
1942  std::true_type
1943 {};
1944 
1945 template<>
1947  std::true_type
1948 {};
1949 
1950 template<>
1952  std::true_type
1953 {};
1954 
1955 /// Output a TfBits, as a stream of 0s and 1s.
1956 /// \ingroup group_tf_DebuggingOutput
1957 TF_API std::ostream&
1958 operator<<(std::ostream &out, const TfCompressedBits &bits);
1959 
1961 
1962 #endif
GLint first
Definition: glcorearb.h:405
void ShiftLeft(size_t bits)
TfCompressedBits & operator<<=(size_t bits)
void ShiftRight(size_t bits)
size_t GetNumUnsetPlatforms() const
void ResizeKeepContents(size_t num)
bool AreAllSet() const
size_t GetAllocatedSize() const
GLenum GLint * range
Definition: glcorearb.h:1925
#define TF_API
Definition: api.h:23
TfCompressedBits(size_t num, size_t first, size_t last)
void Clear(size_t index)
static TF_API TfCompressedBits FromString(const std::string &source)
void swap(UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &a, UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &b)
Definition: UT_ArraySet.h:1699
GLsizei const GLfloat * value
Definition: glcorearb.h:824
AllSetView GetAllSetView() const
void Swap(TfCompressedBits &rhs)
size_t GetNumPlatforms() const
void Set(size_t index)
TF_API size_t GetHash() const
View< Mode::AllSet > AllSetView
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
IMATH_HOSTDEVICE constexpr bool equal(T1 a, T2 b, T3 t) IMATH_NOEXCEPT
Definition: ImathFun.h:105
size_t GetNumSetPlatforms() const
**But if you need a result
Definition: thread.h:622
bool HasNonEmptyDifference(const TfCompressedBits &rhs) const
Fast, compressed bit array which is capable of performing logical operations without first decompress...
TfCompressedBits(TfCompressedBits &&rhs)
bool operator[](size_t index) const
bool IsEmpty() const
View< Mode::AllUnset > AllUnsetView
bool Contains(const TfCompressedBits &rhs) const
std::forward_iterator_tag iterator_category
size_t FindNextSet(size_t index) const
TF_API std::string GetAsStringLeftToRight() const
GLdouble n
Definition: glcorearb.h:2008
TfCompressedBits(const TfCompressedBits &rhs, ComplementTagType)
Fast bit array that keeps track of the number of bits set and can find the next set in a timely manne...
Definition: bits.h:48
const_iterator end() const
TfCompressedBits & operator^=(const TfCompressedBits &rhs)
TfCompressedBits operator>>(size_t bits) const
size_t FindNextUnset(size_t index) const
TfCompressedBits operator&(const TfCompressedBits &rhs) const
size_t GetNumSet() const
TF_API std::string GetAsRLEString() const
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:803
bool IsSet(size_t index) const
bool operator==(const const_iterator &rhs) const
void Count(size_t *numSet, size_t *maxGap) const
TfCompressedBits & operator>>=(size_t bits)
size_t FindNthSet(size_t nth) const
TfCompressedBits & operator&=(const TfCompressedBits &rhs)
View< Mode::All > AllView
AllUnsetView GetAllUnsetView() const
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
bool AreAllUnset() const
TF_API std::ostream & operator<<(std::ostream &out, const TfCompressedBits &bits)
TfCompressedBits operator|(const TfCompressedBits &rhs) const
GLenum mode
Definition: glcorearb.h:99
bool HasNonEmptyIntersection(const TfCompressedBits &rhs) const
PlatformsView GetPlatformsView() const
__hostdev__ uint64_t last(uint32_t i) const
Definition: NanoVDB.h:5976
AllView GetAllView() const
#define ARCH_CACHE_LINE_SIZE
Definition: align.h:67
bool IsAnyUnset() const
TF_API std::string GetAsStringRightToLeft() const
GLsizeiptr size
Definition: glcorearb.h:664
size_t operator()(const TfCompressedBits &bits) const
size_t GetSize() const
void Append(size_t num, bool value)
static size_t Combine(Args &&...args)
Produce a hash code by combining the hash codes of several objects.
Definition: hash.h:487
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
size_t GetLastSet() const
static const TfCompressedBits & GetEmpty()
TfCompressedBits operator<<(size_t bits) const
void SetRange(size_t first, size_t last)
bool operator==(const TfCompressedBits &rhs) const
GLuint index
Definition: glcorearb.h:786
bool IsAnySet() const
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
TfCompressedBits & Complement()
TfCompressedBits & operator|=(const TfCompressedBits &rhs)
bool operator!=(const TfCompressedBits &rhs) const
TfCompressedBits & operator=(TfCompressedBits &&rhs)
TfCompressedBits(const TfCompressedBits &rhs)
size_t FindPrevSet(size_t index) const
TfCompressedBits operator-(const TfCompressedBits &rhs) const
GLboolean r
Definition: glcorearb.h:1222
size_t operator()(const TfCompressedBits &bits) const
const_iterator begin() const
void Assign(size_t index, bool value)
TfCompressedBits & operator-=(const TfCompressedBits &rhs)
size_t GetFirstSet() const
TF_API void Decompress(TfBits *bits) const
GLint GLsizei count
Definition: glcorearb.h:405
bool AreContiguouslySet() const
View< Mode::Platforms > PlatformsView
TfCompressedBits & operator=(const TfCompressedBits &rhs)
bool operator!=(const const_iterator &rhs) const
TfCompressedBits(size_t num=0)
TfCompressedBits operator^(const TfCompressedBits &rhs) const