Fast DDS  Version 3.6.1.0
Fast DDS
fixed_size_bitmap.hpp
1 // Copyright 2018 Proyectos y Sistemas de Mantenimiento SL (eProsima).
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
20 #ifndef FASTDDS_UTILS__FIXED_SIZE_BITMAP_HPP
21 #define FASTDDS_UTILS__FIXED_SIZE_BITMAP_HPP
22 
23 #include <array>
24 #include <cstdint>
25 #include <string.h>
26 #include <limits>
27 
28 #if _MSC_VER
29 #include <intrin.h>
30 
31 #if defined(max)
32 #pragma push_macro("max")
33 #undef max
34 #define FASTDDS_RESTORE_MAX
35 #endif // defined(max)
36 
37 #if defined(min)
38 #pragma push_macro("min")
39 #undef min
40 #define FASTDDS_RESTORE_MIN
41 #endif // defined(min)
42 
43 #endif // if _MSC_VER
44 
45 #ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC
46 namespace eprosima {
47 namespace fastdds {
48 
49 using std::uint32_t;
50 
51 template<class T>
53 {
54  constexpr auto operator () (
55  T a,
56  T b) const
57  -> decltype(a - b)
58  {
59  return a - b;
60  }
61 
62 };
63 
74 template<class T, class Diff = DiffFunction<T>, uint32_t NBITS = 256>
76 {
77  #define NITEMS ((NBITS + 31u) / 32u)
78 
79 public:
80 
81  // Alias to improve readability.
82  using bitmap_type = std::array<uint32_t, NITEMS>;
83 
88  BitmapRange() noexcept
89  : base_()
90  , range_max_(base_ + (NBITS - 1))
91  , bitmap_()
92  , num_bits_(0u)
93  {
94  }
95 
102  explicit BitmapRange(
103  T base) noexcept
104  : BitmapRange(
105  base,
106  NBITS - 1)
107  {
108  }
109 
118  T base,
119  uint32_t max_bits) noexcept
120  : base_(base)
121  , range_max_(base_ + (std::min)(max_bits, NBITS - 1))
122  , bitmap_()
123  , num_bits_(0u)
124  {
125  }
126 
127  // We don't need to define copy/move constructors/assignment operators as the default ones would be enough
128 
133  T base() const noexcept
134  {
135  return base_;
136  }
137 
144  void base(
145  T base) noexcept
146  {
147  base_ = base;
148  range_max_ = base_ + (NBITS - 1);
149  num_bits_ = 0;
150  bitmap_.fill(0u);
151  }
152 
160  void base(
161  T base,
162  uint32_t max_bits) noexcept
163  {
164  base_ = base;
165  range_max_ = base_ + (std::min)(max_bits, NBITS - 1);
166  num_bits_ = 0;
167  bitmap_.fill(0u);
168  }
169 
177  T base) noexcept
178  {
179  // Do nothing if base is not changing
180  if (base == base_)
181  {
182  return;
183  }
184 
185  Diff d_func;
186  if (base > base_)
187  {
188  // Current content should move left
189  uint32_t n_bits = d_func(base, base_);
190  shift_map_left(n_bits);
191  }
192  else
193  {
194  // Current content should move right
195  uint32_t n_bits = d_func(base_, base);
196  shift_map_right(n_bits);
197  }
198 
199  // Update base and range
200  base_ = base;
201  range_max_ = base_ + (NBITS - 1);
202  }
203 
209  bool empty() const noexcept
210  {
211  return num_bits_ == 0u;
212  }
213 
219  T max() const noexcept
220  {
221  return base_ + (num_bits_ - 1);
222  }
223 
229  T min() const noexcept
230  {
231  // Traverse through the significant items on the bitmap
232  T item = base_;
233  uint32_t n_longs = (num_bits_ + 31u) / 32u;
234  for (uint32_t i = 0; i < n_longs; i++)
235  {
236  // Check if item has at least one bit set
237  uint32_t bits = bitmap_[i];
238  if (bits)
239  {
240  // We use an intrinsic to find the index of the highest bit set.
241  // Most modern CPUs have an instruction to count the leading zeroes of a word.
242  // The number of leading zeroes will give us the index we need.
243 #if _MSC_VER
244  unsigned long bit;
245  _BitScanReverse(&bit, bits);
246  uint32_t offset = 31u ^ bit;
247 #else
248  uint32_t offset = static_cast<uint32_t>(__builtin_clz(bits));
249 #endif // if _MSC_VER
250 
251  // Found first bit set in bitmap
252  return item + offset;
253  }
254 
255  // There are 32 items on each bitmap item.
256  item = item + 32u;
257  }
258 
259  return base_;
260  }
261 
269  bool is_set(
270  const T& item) const noexcept
271  {
272  // Check item is inside the allowed range.
273  if ((item >= base_) && (range_max_ >= item))
274  {
275  // Calc distance from base to item, and check the corresponding bit.
276  Diff d_func;
277  uint32_t diff = d_func(item, base_);
278  if (diff < num_bits_)
279  {
280  uint32_t pos = diff >> 5;
281  diff &= 31u;
282  return (bitmap_[pos] & (1u << (31u - diff))) != 0;
283  }
284  }
285 
286  return false;
287  }
288 
297  bool add(
298  const T& item) noexcept
299  {
300  // Check item is inside the allowed range.
301  if ((item >= base_) && (range_max_ >= item))
302  {
303  // Calc distance from base to item, and set the corresponding bit.
304  Diff d_func;
305  uint32_t diff = d_func(item, base_);
306  num_bits_ = std::max(diff + 1, num_bits_);
307  uint32_t pos = diff >> 5;
308  diff &= 31u;
309  bitmap_[pos] |= (1u << (31u - diff));
310  return true;
311  }
312 
313  return false;
314  }
315 
325  void add_range(
326  const T& from,
327  const T& to)
328  {
329  constexpr uint32_t full_mask = (std::numeric_limits<uint32_t>::max)();
330 
331  // Adapt incoming range to range limits
332  T min = (base_ >= from) ? base_ : from;
333  T max = (to >= base_ + NBITS) ? base_ + NBITS : to;
334 
335  // Check precondition. Max should be explicitly above min.
336  if (min >= max)
337  {
338  return;
339  }
340 
341  // Calc offset (distance from base) and num_bits (bits to be set)
342  Diff d_func;
343  uint32_t offset = d_func(min, base_); // Bit position from base
344  uint32_t n_bits = d_func(max, min); // Number of bits pending
345 
346  num_bits_ = std::max(num_bits_, offset + n_bits);
347 
348  uint32_t pos = offset >> 5; // Item position
349  offset &= 31u; // Bit position inside item
350  uint32_t mask = full_mask; // Mask with all bits set
351  mask >>= offset; // Remove first 'offset' bits from mask
352  uint32_t bits_in_mask = 32u - offset; // Take note of number of set bits in mask
353 
354  // This loop enters whenever the whole mask should be added
355  while (n_bits >= bits_in_mask)
356  {
357  bitmap_[pos] |= mask; // Set whole mask of bits
358  pos++; // Go to next position in the array
359  n_bits -= bits_in_mask; // Decrease number of pending bits
360  mask = full_mask; // Mask with all bits set
361  bits_in_mask = 32u; // All bits set in mask (32)
362  }
363 
364  // This condition will be true if the last bits of the mask should not be used
365  if (n_bits > 0)
366  {
367  bitmap_[pos] |= mask & (full_mask << (bits_in_mask - n_bits));
368  }
369  }
370 
377  void remove(
378  const T& item) noexcept
379  {
380  // Check item is inside the allowed range.
381  T max_value = max();
382  if ((item >= base_) && (max_value >= item))
383  {
384  // Calc distance from base to item, and set the corresponding bit.
385  Diff d_func;
386  uint32_t diff = d_func(item, base_);
387  uint32_t pos = diff >> 5;
388  diff &= 31u;
389  bitmap_[pos] &= ~(1u << (31u - diff));
390 
391  if (item == max_value)
392  {
393  calc_maximum_bit_set(pos + 1, 0);
394  }
395  }
396  }
397 
407  uint32_t& num_bits,
408  bitmap_type& bitmap,
409  uint32_t& num_longs_used) const noexcept
410  {
411  num_bits = num_bits_;
412  num_longs_used = (num_bits_ + 31u) / 32u;
413  bitmap = bitmap_;
414  }
415 
421  uint32_t count() const noexcept
422  {
423  uint32_t total = 0;
424  // Traverse through the significant items on the bitmap
425  uint32_t n_longs = (num_bits_ + 31u) / 32u;
426  for (uint32_t i = 0; i < n_longs; i++)
427  {
428  // Count bits set on the item
429  uint32_t bits = bitmap_[i];
430  // Use algorithm from https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
431  // to count number of bits set in a 32-bit integer
432  bits = bits - ((bits >> 1) & 0x55555555);
433  bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333);
434  bits = (bits + (bits >> 4)) & 0x0F0F0F0F;
435  total += (bits * 0x1010101) >> 24;
436  }
437 
438  return total;
439  }
440 
449  uint32_t num_bits,
450  const uint32_t* bitmap) noexcept
451  {
452  num_bits_ = std::min(num_bits, NBITS);
453  uint32_t num_items = ((num_bits_ + 31u) / 32u);
454  uint32_t num_bytes = num_items * static_cast<uint32_t>(sizeof(uint32_t));
455  bitmap_.fill(0u);
456  memcpy(bitmap_.data(), bitmap, num_bytes);
457  // trim unused bits if (0 < num_bits && num_bits % 32 != 0)
458  short shift = num_bits & 31u;
459  if (0 < num_bits && shift != 0)
460  {
461  bitmap_[num_items - 1] &= ~((std::numeric_limits<uint32_t>::max)() >> shift);
462  }
463  calc_maximum_bit_set(num_items, 0);
464  }
465 
471  template<class UnaryFunc>
472  void for_each(
473  UnaryFunc f) const
474  {
475  T item = base_;
476 
477  // Traverse through the significant items on the bitmap
478  uint32_t n_longs = (num_bits_ + 31u) / 32u;
479  for (uint32_t i = 0; i < n_longs; i++)
480  {
481  // Traverse through the bits set on the item, msb first.
482  // Loop will stop when there are no bits set.
483  uint32_t bits = bitmap_[i];
484  while (bits)
485  {
486  // We use an intrinsic to find the index of the highest bit set.
487  // Most modern CPUs have an instruction to count the leading zeroes of a word.
488  // The number of leading zeroes will give us the index we need.
489 #if _MSC_VER
490  unsigned long bit;
491  _BitScanReverse(&bit, bits);
492  uint32_t offset = 31u ^ bit;
493 #else
494  uint32_t offset = static_cast<uint32_t>(__builtin_clz(bits));
495  uint32_t bit = 31u ^ offset;
496 #endif // if _MSC_VER
497 
498  // Call the function for the corresponding item
499  f(item + offset);
500 
501  // Clear the most significant bit
502  bits &= ~(1u << bit);
503  }
504 
505  // There are 32 items on each bitmap item.
506  item = item + 32u;
507  }
508  }
509 
510 protected:
511 
512  T base_;
515  uint32_t num_bits_;
516 
517 private:
518 
519  void shift_map_left(
520  uint32_t n_bits)
521  {
522  if (n_bits >= num_bits_)
523  {
524  // Shifting more than most significant. Clear whole bitmap.
525  num_bits_ = 0;
526  bitmap_.fill(0u);
527  }
528  else
529  {
530  // Significant bit will move left by n_bits
531  num_bits_ -= n_bits;
532 
533  // Div and mod by 32
534  uint32_t n_items = n_bits >> 5;
535  n_bits &= 31u;
536  if (n_bits == 0)
537  {
538  // Shifting a multiple of 32 bits, just move the bitmap integers
539  std::copy(bitmap_.begin() + n_items, bitmap_.end(), bitmap_.begin());
540  std::fill_n(bitmap_.rbegin(), n_items, 0);
541  }
542  else
543  {
544  // Example. Shifting 44 bits. Should shift one complete word and 12 bits.
545  // Need to iterate forward and take 12 bits from next word (shifting it 20 bits).
546  // aaaaaaaa bbbbbbbb cccccccc dddddddd
547  // bbbbbccc bbbbbbbb cccccccc dddddddd
548  // bbbbbccc cccccddd ddddd000 dddddddd
549  // bbbbbccc cccccddd ddddd000 00000000
550  uint32_t overflow_bits = 32u - n_bits;
551  size_t last_index = NITEMS - 1u;
552  for (size_t i = 0, n = n_items; n < last_index; i++, n++)
553  {
554  bitmap_[i] = (bitmap_[n] << n_bits) | (bitmap_[n + 1] >> overflow_bits);
555  }
556  // Last one does not have next word
557  bitmap_[last_index - n_items] = bitmap_[last_index] << n_bits;
558  // Last n_items will become 0
559  std::fill_n(bitmap_.rbegin(), n_items, 0);
560  }
561  }
562  }
563 
564  void shift_map_right(
565  uint32_t n_bits)
566  {
567  if (n_bits >= NBITS)
568  {
569  // Shifting more than total bitmap size. Clear whole bitmap.
570  num_bits_ = 0;
571  bitmap_.fill(0u);
572  }
573  else
574  {
575  // Detect if highest bit will be dropped and take note, as we will need
576  // to find new maximum bit in that case
577  uint32_t new_num_bits = num_bits_ + n_bits;
578  bool find_new_max = new_num_bits > NBITS;
579 
580  // Div and mod by 32
581  uint32_t n_items = n_bits >> 5;
582  n_bits &= 31u;
583  if (n_bits == 0)
584  {
585  // Shifting a multiple of 32 bits, just move the bitmap integers
586  std::copy(bitmap_.rbegin() + n_items, bitmap_.rend(), bitmap_.rbegin());
587  std::fill_n(bitmap_.begin(), n_items, 0);
588  }
589  else
590  {
591  // Example. Shifting 44 bits. Should shift one complete word and 12 bits.
592  // Need to iterate backwards and take 12 bits from previous word (shifting it 20 bits).
593  // aaaaaaaa bbbbbbbb cccccccc dddddddd
594  // aaaaaaaa bbbbbbbb cccccccc bbbccccc
595  // aaaaaaaa bbbbbbbb aaabbbbb bbbccccc
596  // aaaaaaaa 000aaaaa aaabbbbb bbbccccc
597  // 00000000 000aaaaa aaabbbbb bbbccccc
598  uint32_t overflow_bits = 32u - n_bits;
599  size_t last_index = NITEMS - 1u;
600  for (size_t i = last_index, n = last_index - n_items; n > 0; i--, n--)
601  {
602  bitmap_[i] = (bitmap_[n] >> n_bits) | (bitmap_[n - 1] << overflow_bits);
603  }
604  // First item does not have previous word
605  bitmap_[n_items] = bitmap_[0] >> n_bits;
606  // First n_items will become 0
607  std::fill_n(bitmap_.begin(), n_items, 0);
608  }
609 
610  num_bits_ = new_num_bits;
611  if (find_new_max)
612  {
613  calc_maximum_bit_set(NITEMS, n_items);
614  }
615  }
616  }
617 
618  void calc_maximum_bit_set(
619  uint32_t starting_index,
620  uint32_t min_index)
621  {
622  num_bits_ = 0;
623  for (uint32_t i = starting_index; i > min_index;)
624  {
625  --i;
626  uint32_t bits = bitmap_[i];
627  if (bits != 0)
628  {
629  bits = (bits & ~(bits - 1));
630 #if _MSC_VER
631  unsigned long bit;
632  _BitScanReverse(&bit, bits);
633  uint32_t offset = (31u ^ bit) + 1;
634 #else
635  uint32_t offset = static_cast<uint32_t>(__builtin_clz(bits)) + 1u;
636 #endif // if _MSC_VER
637  num_bits_ = (i << 5u) + offset;
638  break;
639  }
640  }
641  }
642 
643 };
644 
645 } // namespace fastdds
646 } // namespace eprosima
647 
648 #endif // DOXYGEN_SHOULD_SKIP_THIS_PUBLIC
649 
650 #if _MSC_VER
651 
652 #if defined(FASTDDS_RESTORE_MIN)
653 #pragma pop_macro("min")
654 #undef FASTDDS_RESTORE_MIN
655 #endif // defined(FASTDDS_RESTORE_MIN)
656 
657 #if defined(FASTDDS_RESTORE_MAX)
658 #pragma pop_macro("max")
659 #undef FASTDDS_RESTORE_MAX
660 #endif // defined(FASTDDS_RESTORE_MAX)
661 
662 #endif // if _MSC_VER
663 
664 #endif // FASTDDS_UTILS__FIXED_SIZE_BITMAP_HPP
Template class to hold a range of items using a custom bitmap.
Definition: fixed_size_bitmap.hpp:76
bitmap_type bitmap_
Holds the bitmap values.
Definition: fixed_size_bitmap.hpp:514
T base() const noexcept
Get base of the range.
Definition: fixed_size_bitmap.hpp:133
uint32_t num_bits_
Holds the highest bit set in the bitmap.
Definition: fixed_size_bitmap.hpp:515
bool empty() const noexcept
Returns whether the range is empty (i.e.
Definition: fixed_size_bitmap.hpp:209
bool add(const T &item) noexcept
Adds an element to the range.
Definition: fixed_size_bitmap.hpp:297
void bitmap_get(uint32_t &num_bits, bitmap_type &bitmap, uint32_t &num_longs_used) const noexcept
Gets the current value of the bitmap.
Definition: fixed_size_bitmap.hpp:406
T min() const noexcept
Returns the lowest value set in the range.
Definition: fixed_size_bitmap.hpp:229
std::array< uint32_t, NITEMS > bitmap_type
Definition: fixed_size_bitmap.hpp:82
void bitmap_set(uint32_t num_bits, const uint32_t *bitmap) noexcept
Sets the current value of the bitmap.
Definition: fixed_size_bitmap.hpp:448
T base_
Holds base value of the range.
Definition: fixed_size_bitmap.hpp:512
void add_range(const T &from, const T &to)
Adds a range of elements to the range.
Definition: fixed_size_bitmap.hpp:325
void base_update(T base) noexcept
Set a new base for the range, keeping old values where possible.
Definition: fixed_size_bitmap.hpp:176
T range_max_
Holds maximum allowed value of the range.
Definition: fixed_size_bitmap.hpp:513
BitmapRange(T base) noexcept
Base-specific constructor.
Definition: fixed_size_bitmap.hpp:102
void base(T base, uint32_t max_bits) noexcept
Set a new base and maximum bits for the range.
Definition: fixed_size_bitmap.hpp:160
void for_each(UnaryFunc f) const
Apply a function on every item on the range.
Definition: fixed_size_bitmap.hpp:472
uint32_t count() const noexcept
Counts the number of elements set in the bitmap.
Definition: fixed_size_bitmap.hpp:421
bool is_set(const T &item) const noexcept
Checks if an element is present in the bitmap.
Definition: fixed_size_bitmap.hpp:269
BitmapRange(T base, uint32_t max_bits) noexcept
Range specific constructor.
Definition: fixed_size_bitmap.hpp:117
BitmapRange() noexcept
Default constructor.
Definition: fixed_size_bitmap.hpp:88
void base(T base) noexcept
Set a new base for the range.
Definition: fixed_size_bitmap.hpp:144
T max() const noexcept
Returns the highest value set in the range.
Definition: fixed_size_bitmap.hpp:219
void remove(const T &item) noexcept
Removes an element from the range.
Definition: fixed_size_bitmap.hpp:377
Definition: fixed_size_bitmap.hpp:53
constexpr auto operator()(T a, T b) const -> decltype(a - b)
Definition: fixed_size_bitmap.hpp:54