fixed_size_bitmap.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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. /*!
  15. * @file fixed_size_string.hpp
  16. *
  17. */
  18. #ifndef FASTRTPS_UTILS_FIXED_SIZE_BITMAP_HPP_
  19. #define FASTRTPS_UTILS_FIXED_SIZE_BITMAP_HPP_
  20. #include <array>
  21. #include <string.h>
  22. #if _MSC_VER
  23. #include <intrin.h>
  24. #endif
  25. #ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC
  26. namespace eprosima {
  27. namespace fastrtps {
  28. template <class T>
  29. struct DiffFunction
  30. {
  31. constexpr auto operator () (T a, T b) const
  32. -> decltype(a - b)
  33. {
  34. return a - b;
  35. }
  36. };
  37. /**
  38. * Template class to hold a range of items using a custom bitmap.
  39. * @tparam T Type of the elements in the range.
  40. * Should have `>=` operator and `T + uint32_t` returning T.
  41. * @tparam Diff Functor calculating the difference of two T.
  42. * The result should be assignable to a uint32_t.
  43. * @tparam NBITS Size in bits of the bitmap.
  44. * Range of items will be [base, base + NBITS - 1].
  45. * @ingroup UTILITIES_MODULE
  46. */
  47. template<class T, class Diff = DiffFunction<T>, uint32_t NBITS = 256>
  48. class BitmapRange
  49. {
  50. #define NITEMS ((NBITS + 31ul) / 32ul)
  51. public:
  52. // Alias to improve readability.
  53. using bitmap_type = std::array<uint32_t, NITEMS>;
  54. /**
  55. * Default constructor.
  56. * Constructs an empty range with default base.
  57. */
  58. BitmapRange() noexcept
  59. : base_()
  60. , range_max_(base_ + (NBITS - 1))
  61. , bitmap_()
  62. , num_bits_(0u)
  63. {
  64. }
  65. /**
  66. * Base-specific constructor.
  67. * Constructs an empty range with specified base.
  68. *
  69. * @param base Specific base value for the created range.
  70. */
  71. explicit BitmapRange(
  72. T base) noexcept
  73. : base_(base)
  74. , range_max_(base + (NBITS - 1))
  75. , bitmap_()
  76. , num_bits_(0u)
  77. {
  78. }
  79. // We don't need to define copy/move constructors/assignment operators as the default ones would be enough
  80. /**
  81. * Get base of the range.
  82. * @return a copy of the range base.
  83. */
  84. T base() const noexcept
  85. {
  86. return base_;
  87. }
  88. /**
  89. * Set a new base for the range.
  90. * This method resets the range and sets a new value for its base.
  91. *
  92. * @param base New base value to set.
  93. */
  94. void base(
  95. T base) noexcept
  96. {
  97. base_ = base;
  98. range_max_ = base_ + (NBITS - 1);
  99. num_bits_ = 0;
  100. bitmap_.fill(0UL);
  101. }
  102. /**
  103. * Set a new base for the range, keeping old values where possible.
  104. * This method implements a sliding window mechanism for changing the base of the range.
  105. *
  106. * @param base New base value to set.
  107. */
  108. void base_update(
  109. T base) noexcept
  110. {
  111. // Do nothing if base is not changing
  112. if (base == base_)
  113. {
  114. return;
  115. }
  116. Diff d_func;
  117. if (base > base_)
  118. {
  119. // Current content should move left
  120. uint32_t n_bits = d_func(base, base_);
  121. shift_map_left(n_bits);
  122. }
  123. else
  124. {
  125. // Current content should move right
  126. uint32_t n_bits = d_func(base_, base);
  127. shift_map_right(n_bits);
  128. }
  129. // Update base and range
  130. base_ = base;
  131. range_max_ = base_ + (NBITS - 1);
  132. }
  133. /**
  134. * Returns whether the range is empty (i.e. has all bits unset).
  135. *
  136. * @return true if the range is empty, false otherwise.
  137. */
  138. bool empty() const noexcept
  139. {
  140. return num_bits_ == 0u;
  141. }
  142. /**
  143. * Returns the highest value set in the range.
  144. *
  145. * @return the highest value set in the range. If the range is empty, the result is undetermined.
  146. */
  147. T max() const noexcept
  148. {
  149. return base_ + (num_bits_ - 1);
  150. }
  151. /**
  152. * Returns the lowest value set in the range.
  153. *
  154. * @return the lowest value set in the range. If the range is empty, the result is undetermined.
  155. */
  156. T min() const noexcept
  157. {
  158. // Traverse through the significant items on the bitmap
  159. T item = base_;
  160. uint32_t n_longs = (num_bits_ + 31UL) / 32UL;
  161. for (uint32_t i = 0; i < n_longs; i++)
  162. {
  163. // Check if item has at least one bit set
  164. uint32_t bits = bitmap_[i];
  165. if (bits)
  166. {
  167. // We use an intrinsic to find the index of the highest bit set.
  168. // Most modern CPUs have an instruction to count the leading zeroes of a word.
  169. // The number of leading zeroes will give us the index we need.
  170. #if _MSC_VER
  171. unsigned long bit;
  172. _BitScanReverse(&bit, bits);
  173. uint32_t offset = 31UL ^ bit;
  174. #else
  175. uint32_t offset = __builtin_clz(bits);
  176. #endif
  177. // Found first bit set in bitmap
  178. return item + offset;
  179. }
  180. // There are 32 items on each bitmap item.
  181. item = item + 32UL;
  182. }
  183. return base_;
  184. }
  185. /**
  186. * Checks if an element is present in the bitmap.
  187. *
  188. * @param item Value to be checked.
  189. *
  190. * @return true if the item is present in the bitmap, false otherwise.
  191. */
  192. bool is_set(
  193. const T& item) const noexcept
  194. {
  195. // Check item is inside the allowed range.
  196. if ((item >= base_) && (range_max_ >= item))
  197. {
  198. // Calc distance from base to item, and check the corresponding bit.
  199. Diff d_func;
  200. uint32_t diff = d_func(item, base_);
  201. if (diff < num_bits_)
  202. {
  203. uint32_t pos = diff >> 5;
  204. diff &= 31UL;
  205. return (bitmap_[pos] & (1UL << (31UL - diff))) != 0;
  206. }
  207. }
  208. return false;
  209. }
  210. /**
  211. * Adds an element to the range.
  212. * Adds an element to the bitmap if it is in the allowed range.
  213. *
  214. * @param item Value to be added.
  215. *
  216. * @return true if the item has been added (i.e. is in the allowed range), false otherwise.
  217. */
  218. bool add(
  219. const T& item) noexcept
  220. {
  221. // Check item is inside the allowed range.
  222. if ((item >= base_) && (range_max_ >= item))
  223. {
  224. // Calc distance from base to item, and set the corresponding bit.
  225. Diff d_func;
  226. uint32_t diff = d_func(item, base_);
  227. num_bits_ = std::max(diff + 1, num_bits_);
  228. uint32_t pos = diff >> 5;
  229. diff &= 31UL;
  230. bitmap_[pos] |= (1UL << (31UL - diff) );
  231. return true;
  232. }
  233. return false;
  234. }
  235. /**
  236. * Adds a range of elements to the range.
  237. *
  238. * Add all elements in [from, to) to the range.
  239. * Equivalent to for(T i = from; i < to; i++) add(i);
  240. *
  241. * @param from Starting value of the range to add.
  242. * @param to Ending value of the range to add.
  243. */
  244. void add_range(
  245. const T& from,
  246. const T& to)
  247. {
  248. constexpr uint32_t full_mask = 0xFFFFFFFF;
  249. // Adapt incoming range to range limits
  250. T min = (base_ >= from) ? base_ : from;
  251. T max = (to >= base_ + NBITS) ? base_ + NBITS : to;
  252. // Check precondition. Max should be explicitly above min.
  253. if (min >= max)
  254. {
  255. return;
  256. }
  257. // Calc offset (distance from base) and num_bits (bits to be set)
  258. Diff d_func;
  259. uint32_t offset = d_func(min, base_); // Bit position from base
  260. uint32_t n_bits = d_func(max, min); // Number of bits pending
  261. num_bits_ = std::max(num_bits_, offset + n_bits);
  262. uint32_t pos = offset >> 5; // Item position
  263. offset &= 31UL; // Bit position inside item
  264. uint32_t mask = full_mask; // Mask with all bits set
  265. mask >>= offset; // Remove first 'offset' bits from mask
  266. uint32_t bits_in_mask = 32UL - offset; // Take note of number of set bits in mask
  267. // This loop enters whenever the whole mask should be added
  268. while (n_bits >= bits_in_mask)
  269. {
  270. bitmap_[pos] |= mask; // Set whole mask of bits
  271. pos++; // Go to next position in the array
  272. n_bits -= bits_in_mask; // Decrease number of pending bits
  273. mask = full_mask; // Mask with all bits set
  274. bits_in_mask = 32UL; // All bits set in mask (32)
  275. }
  276. // This condition will be true if the last bits of the mask should not be used
  277. if (n_bits > 0)
  278. {
  279. bitmap_[pos] |= mask & (full_mask << (bits_in_mask - n_bits));
  280. }
  281. }
  282. /**
  283. * Removes an element from the range.
  284. * Removes an element from the bitmap.
  285. *
  286. * @param item Value to be removed.
  287. */
  288. void remove(
  289. const T& item) noexcept
  290. {
  291. // Check item is inside the allowed range.
  292. T max_value = max();
  293. if ((item >= base_) && (max_value >= item))
  294. {
  295. // Calc distance from base to item, and set the corresponding bit.
  296. Diff d_func;
  297. uint32_t diff = d_func(item, base_);
  298. uint32_t pos = diff >> 5;
  299. diff &= 31UL;
  300. bitmap_[pos] &= ~(1UL << (31UL - diff));
  301. if (item == max_value)
  302. {
  303. calc_maximum_bit_set(pos + 1, 0);
  304. }
  305. }
  306. }
  307. /**
  308. * Gets the current value of the bitmap.
  309. * This method is designed to be used when performing serialization of a bitmap range.
  310. *
  311. * @param num_bits Upon return, it will contain the number of significant bits in the bitmap.
  312. * @param bitmap Upon return, it will contain the current value of the bitmap.
  313. * @param num_longs_used Upon return, it will contain the number of valid elements on the returned bitmap.
  314. */
  315. void bitmap_get(
  316. uint32_t& num_bits,
  317. bitmap_type& bitmap,
  318. uint32_t& num_longs_used) const noexcept
  319. {
  320. num_bits = num_bits_;
  321. num_longs_used = (num_bits_ + 31UL) / 32UL;
  322. bitmap = bitmap_;
  323. }
  324. /**
  325. * Sets the current value of the bitmap.
  326. * This method is designed to be used when performing deserialization of a bitmap range.
  327. *
  328. * @param num_bits Number of significant bits in the input bitmap.
  329. * @param bitmap Points to the begining of a uint32_t array holding the input bitmap.
  330. */
  331. void bitmap_set(
  332. uint32_t num_bits,
  333. const uint32_t* bitmap) noexcept
  334. {
  335. num_bits_ = std::min(num_bits, NBITS);
  336. uint32_t num_items = ((num_bits_ + 31UL) / 32UL);
  337. uint32_t num_bytes = num_items * sizeof(uint32_t);
  338. bitmap_.fill(0UL);
  339. memcpy(bitmap_.data(), bitmap, num_bytes);
  340. calc_maximum_bit_set(num_items, 0);
  341. }
  342. /**
  343. * Apply a function on every item on the range.
  344. *
  345. * @param f Function to apply on each item.
  346. */
  347. template<class UnaryFunc>
  348. void for_each(
  349. UnaryFunc f) const
  350. {
  351. T item = base_;
  352. // Traverse through the significant items on the bitmap
  353. uint32_t n_longs = (num_bits_ + 31UL) / 32UL;
  354. for (uint32_t i = 0; i < n_longs; i++)
  355. {
  356. // Traverse through the bits set on the item, msb first.
  357. // Loop will stop when there are no bits set.
  358. uint32_t bits = bitmap_[i];
  359. while(bits)
  360. {
  361. // We use an intrinsic to find the index of the highest bit set.
  362. // Most modern CPUs have an instruction to count the leading zeroes of a word.
  363. // The number of leading zeroes will give us the index we need.
  364. #if _MSC_VER
  365. unsigned long bit;
  366. _BitScanReverse(&bit, bits);
  367. uint32_t offset = 31UL ^ bit;
  368. #else
  369. uint32_t offset = __builtin_clz(bits);
  370. uint32_t bit = 31UL ^ offset;
  371. #endif
  372. // Call the function for the corresponding item
  373. f(item + offset);
  374. // Clear the most significant bit
  375. bits &= ~(1UL << bit);
  376. }
  377. // There are 32 items on each bitmap item.
  378. item = item + 32UL;
  379. }
  380. }
  381. protected:
  382. T base_; ///< Holds base value of the range.
  383. T range_max_; ///< Holds maximum allowed value of the range.
  384. bitmap_type bitmap_; ///< Holds the bitmap values.
  385. uint32_t num_bits_; ///< Holds the highest bit set in the bitmap.
  386. private:
  387. void shift_map_left(
  388. uint32_t n_bits)
  389. {
  390. if (n_bits >= num_bits_)
  391. {
  392. // Shifting more than most significant. Clear whole bitmap.
  393. num_bits_ = 0;
  394. bitmap_.fill(0UL);
  395. }
  396. else
  397. {
  398. // Significant bit will move left by n_bits
  399. num_bits_ -= n_bits;
  400. // Div and mod by 32
  401. uint32_t n_items = n_bits >> 5;
  402. n_bits &= 31UL;
  403. if (n_bits == 0)
  404. {
  405. // Shifting a multiple of 32 bits, just move the bitmap integers
  406. std::copy(bitmap_.begin() + n_items, bitmap_.end(), bitmap_.begin());
  407. std::fill_n(bitmap_.rbegin(), n_items, 0);
  408. }
  409. else
  410. {
  411. // Example. Shifting 44 bits. Should shift one complete word and 12 bits.
  412. // Need to iterate forward and take 12 bits from next word (shifting it 20 bits).
  413. // aaaaaaaa bbbbbbbb cccccccc dddddddd
  414. // bbbbbccc bbbbbbbb cccccccc dddddddd
  415. // bbbbbccc cccccddd ddddd000 dddddddd
  416. // bbbbbccc cccccddd ddddd000 00000000
  417. uint32_t overflow_bits = 32UL - n_bits;
  418. size_t last_index = NITEMS - 1u;
  419. for (size_t i = 0, n = n_items; n < last_index; i++, n++)
  420. {
  421. bitmap_[i] = (bitmap_[n] << n_bits) | (bitmap_[n + 1] >> overflow_bits);
  422. }
  423. // Last one does not have next word
  424. bitmap_[last_index - n_items] = bitmap_[last_index] << n_bits;
  425. // Last n_items will become 0
  426. std::fill_n(bitmap_.rbegin(), n_items, 0);
  427. }
  428. }
  429. }
  430. void shift_map_right(
  431. uint32_t n_bits)
  432. {
  433. if (n_bits >= NBITS)
  434. {
  435. // Shifting more than total bitmap size. Clear whole bitmap.
  436. num_bits_ = 0;
  437. bitmap_.fill(0UL);
  438. }
  439. else
  440. {
  441. // Detect if highest bit will be dropped and take note, as we will need
  442. // to find new maximum bit in that case
  443. uint32_t new_num_bits = num_bits_ + n_bits;
  444. bool find_new_max = new_num_bits > NBITS;
  445. // Div and mod by 32
  446. uint32_t n_items = n_bits >> 5;
  447. n_bits &= 31UL;
  448. if (n_bits == 0)
  449. {
  450. // Shifting a multiple of 32 bits, just move the bitmap integers
  451. std::copy(bitmap_.rbegin() + n_items, bitmap_.rend(), bitmap_.rbegin());
  452. std::fill_n(bitmap_.begin(), n_items, 0);
  453. }
  454. else
  455. {
  456. // Example. Shifting 44 bits. Should shift one complete word and 12 bits.
  457. // Need to iterate backwards and take 12 bits from previous word (shifting it 20 bits).
  458. // aaaaaaaa bbbbbbbb cccccccc dddddddd
  459. // aaaaaaaa bbbbbbbb cccccccc bbbccccc
  460. // aaaaaaaa bbbbbbbb aaabbbbb bbbccccc
  461. // aaaaaaaa 000aaaaa aaabbbbb bbbccccc
  462. // 00000000 000aaaaa aaabbbbb bbbccccc
  463. uint32_t overflow_bits = 32UL - n_bits;
  464. size_t last_index = NITEMS - 1u;
  465. for (size_t i = last_index, n = last_index - n_items; n > 0; i--, n--)
  466. {
  467. bitmap_[i] = (bitmap_[n] >> n_bits) | (bitmap_[n - 1] << overflow_bits);
  468. }
  469. // First item does not have previous word
  470. bitmap_[n_items] = bitmap_[0] >> n_bits;
  471. // First n_items will become 0
  472. std::fill_n(bitmap_.begin(), n_items, 0);
  473. }
  474. num_bits_ = new_num_bits;
  475. if (find_new_max)
  476. {
  477. calc_maximum_bit_set(NITEMS, n_items);
  478. }
  479. }
  480. }
  481. void calc_maximum_bit_set(
  482. uint32_t starting_index,
  483. uint32_t min_index)
  484. {
  485. num_bits_ = 0;
  486. for (uint32_t i = starting_index; i > min_index;)
  487. {
  488. --i;
  489. uint32_t bits = bitmap_[i];
  490. if (bits != 0)
  491. {
  492. bits = (bits & ~(bits - 1));
  493. #if _MSC_VER
  494. unsigned long bit;
  495. _BitScanReverse(&bit, bits);
  496. uint32_t offset = (31UL ^ bit) + 1;
  497. #else
  498. uint32_t offset = __builtin_clz(bits) + 1;
  499. #endif
  500. num_bits_ = (i << 5UL) + offset;
  501. break;
  502. }
  503. }
  504. }
  505. };
  506. } // namespace fastrtps
  507. } // namespace eprosima
  508. #endif // DOXYGEN_SHOULD_SKIP_THIS_PUBLIC
  509. #endif // FASTRTPS_UTILS_FIXED_SIZE_BITMAP_HPP_