CRoaring 4.3.1
Roaring bitmaps in C (and C++)
Loading...
Searching...
No Matches
roaring64map.hh
Go to the documentation of this file.
1
9#ifndef INCLUDE_ROARING_64_MAP_HH_
10#define INCLUDE_ROARING_64_MAP_HH_
11
12#include <algorithm>
13#include <cinttypes> // PRIu64 macro
14#include <cstdarg> // for va_list handling in bitmapOf()
15#include <cstdio> // for std::printf() in the printf() method
16#include <cstring> // for std::memcpy()
17#include <functional>
18#include <initializer_list>
19#include <limits>
20#include <map>
21#include <new>
22#include <numeric>
23#include <queue>
24#include <stdexcept>
25#include <string>
26#include <utility>
27
28#include "roaring.hh"
29
30namespace roaring {
31
33
34class Roaring64MapSetBitBiDirectionalIterator;
35
36// For backwards compatibility; there used to be two kinds of iterators
37// (forward and bidirectional) and now there's only one.
38typedef Roaring64MapSetBitBiDirectionalIterator
40
42 typedef api::roaring_bitmap_t roaring_bitmap_t;
43
44 public:
48 Roaring64Map() = default;
49
53 Roaring64Map(size_t n, const uint32_t *data) { addMany(n, data); }
54
58 Roaring64Map(size_t n, const uint64_t *data) { addMany(n, data); }
59
63 Roaring64Map(std::initializer_list<uint64_t> l) {
64 addMany(l.size(), l.begin());
65 }
66
70 explicit Roaring64Map(const Roaring &r) { emplaceOrInsert(0, r); }
71
75 explicit Roaring64Map(Roaring &&r) { emplaceOrInsert(0, std::move(r)); }
76
82 explicit Roaring64Map(roaring_bitmap_t *s) {
83 emplaceOrInsert(0, Roaring(s));
84 }
85
86 Roaring64Map(const Roaring64Map &r) = default;
87
88 Roaring64Map(Roaring64Map &&r) noexcept = default;
89
93 Roaring64Map &operator=(const Roaring64Map &r) = default;
94
98 Roaring64Map &operator=(Roaring64Map &&r) noexcept = default;
99
103 Roaring64Map &operator=(std::initializer_list<uint64_t> l) {
104 // Delegate to move assignment operator
105 *this = Roaring64Map(l);
106 return *this;
107 }
108
112 static Roaring64Map bitmapOf(size_t n...) {
113 Roaring64Map ans;
114 va_list vl;
115 va_start(vl, n);
116 for (size_t i = 0; i < n; i++) {
117 ans.add(va_arg(vl, uint64_t));
118 }
119 va_end(vl);
120 return ans;
121 }
122
127 static Roaring64Map bitmapOfList(std::initializer_list<uint64_t> l) {
128 Roaring64Map ans;
129 ans.addMany(l.size(), l.begin());
130 return ans;
131 }
132
136 void add(uint32_t x) { lookupOrCreateInner(0).add(x); }
137
141 void add(uint64_t x) { lookupOrCreateInner(highBytes(x)).add(lowBytes(x)); }
142
148 bool addChecked(uint32_t x) { return lookupOrCreateInner(0).addChecked(x); }
149
155 bool addChecked(uint64_t x) {
156 return lookupOrCreateInner(highBytes(x)).addChecked(lowBytes(x));
157 }
158
162 void addRange(uint64_t min, uint64_t max) {
163 if (min >= max) {
164 return;
165 }
166 addRangeClosed(min, max - 1);
167 }
168
172 void addRangeClosed(uint32_t min, uint32_t max) {
173 lookupOrCreateInner(0).addRangeClosed(min, max);
174 }
175
179 void addRangeClosed(uint64_t min, uint64_t max) {
180 if (min > max) {
181 return;
182 }
183 uint32_t start_high = highBytes(min);
184 uint32_t start_low = lowBytes(min);
185 uint32_t end_high = highBytes(max);
186 uint32_t end_low = lowBytes(max);
187
188 // We put std::numeric_limits<>::max in parentheses to avoid a
189 // clash with the Windows.h header under Windows.
190 const uint32_t uint32_max = (std::numeric_limits<uint32_t>::max)();
191
192 // Fill in any nonexistent slots with empty Roarings. This simplifies
193 // the logic below, allowing it to simply iterate over the map between
194 // 'start_high' and 'end_high' in a linear fashion.
195 auto current_iter = ensureRangePopulated(start_high, end_high);
196
197 // If start and end land on the same inner bitmap, then we can do the
198 // whole operation in one call.
199 if (start_high == end_high) {
200 auto &bitmap = current_iter->second;
201 bitmap.addRangeClosed(start_low, end_low);
202 return;
203 }
204
205 // Because start and end don't land on the same inner bitmap,
206 // we need to do this in multiple steps:
207 // 1. Partially fill the first bitmap with values from the closed
208 // interval [start_low, uint32_max]
209 // 2. Fill intermediate bitmaps completely: [0, uint32_max]
210 // 3. Partially fill the last bitmap with values from the closed
211 // interval [0, end_low]
212 auto num_intermediate_bitmaps = end_high - start_high - 1;
213
214 // Step 1: Partially fill the first bitmap.
215 {
216 auto &bitmap = current_iter->second;
217 bitmap.addRangeClosed(start_low, uint32_max);
218 ++current_iter;
219 }
220
221 // Step 2. Fill intermediate bitmaps completely.
222 if (num_intermediate_bitmaps != 0) {
223 auto &first_intermediate = current_iter->second;
224 first_intermediate.addRangeClosed(0, uint32_max);
225 ++current_iter;
226
227 // Now make (num_intermediate_bitmaps - 1) copies of this.
228 for (uint32_t i = 1; i != num_intermediate_bitmaps; ++i) {
229 auto &next_intermediate = current_iter->second;
230 next_intermediate = first_intermediate;
231 ++current_iter;
232 }
233 }
234
235 // Step 3: Partially fill the last bitmap.
236 auto &bitmap = current_iter->second;
237 bitmap.addRangeClosed(0, end_low);
238 }
239
243 void addMany(size_t n_args, const uint32_t *vals) {
244 lookupOrCreateInner(0).addMany(n_args, vals);
245 }
246
250 void addMany(size_t n_args, const uint64_t *vals) {
251 // Potentially reduce outer map lookups by optimistically
252 // assuming that adjacent values will belong to the same inner bitmap.
253 Roaring *last_inner_bitmap = nullptr;
254 uint32_t last_value_high = 0;
255 BulkContext last_bulk_context;
256 for (size_t lcv = 0; lcv < n_args; lcv++) {
257 auto value = vals[lcv];
258 auto value_high = highBytes(value);
259 auto value_low = lowBytes(value);
260 if (last_inner_bitmap == nullptr || value_high != last_value_high) {
261 last_inner_bitmap = &lookupOrCreateInner(value_high);
262 last_value_high = value_high;
263 last_bulk_context = BulkContext{};
264 }
265 last_inner_bitmap->addBulk(last_bulk_context, value_low);
266 }
267 }
268
272 void remove(uint32_t x) {
273 auto iter = roarings.begin();
274 // Since x is a uint32_t, highbytes(x) == 0. The inner bitmap we are
275 // looking for, if it exists, will be at the first slot of 'roarings'.
276 if (iter == roarings.end() || iter->first != 0) {
277 return;
278 }
279 auto &bitmap = iter->second;
280 bitmap.remove(x);
281 eraseIfEmpty(iter);
282 }
283
287 void remove(uint64_t x) {
288 auto iter = roarings.find(highBytes(x));
289 if (iter == roarings.end()) {
290 return;
291 }
292 auto &bitmap = iter->second;
293 bitmap.remove(lowBytes(x));
294 eraseIfEmpty(iter);
295 }
296
302 bool removeChecked(uint32_t x) {
303 auto iter = roarings.begin();
304 // Since x is a uint32_t, highbytes(x) == 0. The inner bitmap we are
305 // looking for, if it exists, will be at the first slot of 'roarings'.
306 if (iter == roarings.end() || iter->first != 0) {
307 return false;
308 }
309 auto &bitmap = iter->second;
310 if (!bitmap.removeChecked(x)) {
311 return false;
312 }
313 eraseIfEmpty(iter);
314 return true;
315 }
316
322 bool removeChecked(uint64_t x) {
323 auto iter = roarings.find(highBytes(x));
324 if (iter == roarings.end()) {
325 return false;
326 }
327 auto &bitmap = iter->second;
328 if (!bitmap.removeChecked(lowBytes(x))) {
329 return false;
330 }
331 eraseIfEmpty(iter);
332 return true;
333 }
334
338 void removeRange(uint64_t min, uint64_t max) {
339 if (min >= max) {
340 return;
341 }
342 return removeRangeClosed(min, max - 1);
343 }
344
348 void removeRangeClosed(uint32_t min, uint32_t max) {
349 auto iter = roarings.begin();
350 // Since min and max are uint32_t, highbytes(min or max) == 0. The inner
351 // bitmap we are looking for, if it exists, will be at the first slot of
352 // 'roarings'.
353 if (iter == roarings.end() || iter->first != 0) {
354 return;
355 }
356 auto &bitmap = iter->second;
357 bitmap.removeRangeClosed(min, max);
358 eraseIfEmpty(iter);
359 }
360
364 void removeRangeClosed(uint64_t min, uint64_t max) {
365 if (min > max) {
366 return;
367 }
368 uint32_t start_high = highBytes(min);
369 uint32_t start_low = lowBytes(min);
370 uint32_t end_high = highBytes(max);
371 uint32_t end_low = lowBytes(max);
372
373 // We put std::numeric_limits<>::max in parentheses to avoid a
374 // clash with the Windows.h header under Windows.
375 const uint32_t uint32_max = (std::numeric_limits<uint32_t>::max)();
376
377 // If the outer map is empty, end_high is less than the first key,
378 // or start_high is greater than the last key, then exit now because
379 // there is no work to do.
380 if (roarings.empty() || end_high < roarings.cbegin()->first ||
381 start_high > (roarings.crbegin())->first) {
382 return;
383 }
384
385 // If we get here, start_iter points to the first entry in the outer map
386 // with key >= start_high. Such an entry is known to exist (i.e. the
387 // iterator will not be equal to end()) because start_high <= the last
388 // key in the map (thanks to the above if statement).
389 auto start_iter = roarings.lower_bound(start_high);
390 // end_iter points to the first entry in the outer map with
391 // key >= end_high, if such a key exists. Otherwise, it equals end().
392 auto end_iter = roarings.lower_bound(end_high);
393
394 // Note that the 'lower_bound' method will find the start and end slots,
395 // if they exist; otherwise it will find the next-higher slots.
396 // In the case where 'start' landed on an existing slot, we need to do a
397 // partial erase of that slot, and likewise for 'end'. But all the slots
398 // in between can be fully erased. More precisely:
399 //
400 // 1. If the start point falls on an existing entry, there are two
401 // subcases:
402 // a. if the end point falls on that same entry, remove the closed
403 // interval [start_low, end_low] from that entry and we are done.
404 // b. Otherwise, remove the closed interval [start_low, uint32_max]
405 // from that entry, advance start_iter, and fall through to
406 // step 2.
407 // 2. Completely erase all slots in the half-open interval
408 // [start_iter, end_iter)
409 // 3. If the end point falls on an existing entry, remove the closed
410 // interval [0, end_high] from it.
411
412 // Step 1. If the start point falls on an existing entry...
413 if (start_iter->first == start_high) {
414 auto &start_inner = start_iter->second;
415 // 1a. if the end point falls on that same entry...
416 if (start_iter == end_iter) {
417 start_inner.removeRangeClosed(start_low, end_low);
418 eraseIfEmpty(start_iter);
419 return;
420 }
421
422 // 1b. Otherwise, remove the closed range [start_low, uint32_max]...
423 start_inner.removeRangeClosed(start_low, uint32_max);
424 // Advance start_iter, but keep the old value so we can check the
425 // bitmap we just modified for emptiness and erase if it necessary.
426 auto temp = start_iter++;
427 eraseIfEmpty(temp);
428 }
429
430 // 2. Completely erase all slots in the half-open interval...
431 roarings.erase(start_iter, end_iter);
432
433 // 3. If the end point falls on an existing entry...
434 if (end_iter != roarings.end() && end_iter->first == end_high) {
435 auto &end_inner = end_iter->second;
436 end_inner.removeRangeClosed(0, end_low);
437 eraseIfEmpty(end_iter);
438 }
439 }
440
444 void clear() { roarings.clear(); }
445
449 uint64_t maximum() const {
450 for (auto roaring_iter = roarings.crbegin();
451 roaring_iter != roarings.crend(); ++roaring_iter) {
452 if (!roaring_iter->second.isEmpty()) {
453 return uniteBytes(roaring_iter->first,
454 roaring_iter->second.maximum());
455 }
456 }
457 // we put std::numeric_limits<>::max/min in parentheses
458 // to avoid a clash with the Windows.h header under Windows
459 return (std::numeric_limits<uint64_t>::min)();
460 }
461
465 uint64_t minimum() const {
466 for (auto roaring_iter = roarings.cbegin();
467 roaring_iter != roarings.cend(); ++roaring_iter) {
468 if (!roaring_iter->second.isEmpty()) {
469 return uniteBytes(roaring_iter->first,
470 roaring_iter->second.minimum());
471 }
472 }
473 // we put std::numeric_limits<>::max/min in parentheses
474 // to avoid a clash with the Windows.h header under Windows
475 return (std::numeric_limits<uint64_t>::max)();
476 }
477
481 bool contains(uint32_t x) const {
482 auto iter = roarings.find(0);
483 if (iter == roarings.end()) {
484 return false;
485 }
486 return iter->second.contains(x);
487 }
488 bool contains(uint64_t x) const {
489 auto iter = roarings.find(highBytes(x));
490 if (iter == roarings.end()) {
491 return false;
492 }
493 return iter->second.contains(lowBytes(x));
494 }
495
496 // TODO: implement `containsRange`
497
507 if (this == &other) {
508 // ANDing *this with itself is a no-op.
509 return *this;
510 }
511
512 // Logic table summarizing what to do when a given outer key is
513 // present vs. absent from self and other.
514 //
515 // self other (self & other) work to do
516 // --------------------------------------------
517 // absent absent empty None
518 // absent present empty None
519 // present absent empty Erase self
520 // present present empty or not Intersect self with other, but
521 // erase self if result is empty.
522 //
523 // Because there is only work to do when a key is present in 'self', the
524 // main for loop iterates over entries in 'self'.
525
526 decltype(roarings.begin()) self_next;
527 for (auto self_iter = roarings.begin(); self_iter != roarings.end();
528 self_iter = self_next) {
529 // Do the 'next' operation now, so we don't have to worry about
530 // invalidation of self_iter down below with the 'erase' operation.
531 self_next = std::next(self_iter);
532
533 auto self_key = self_iter->first;
534 auto &self_bitmap = self_iter->second;
535
536 auto other_iter = other.roarings.find(self_key);
537 if (other_iter == other.roarings.end()) {
538 // 'other' doesn't have self_key. In the logic table above,
539 // this reflects the case (self.present & other.absent).
540 // So, erase self.
541 roarings.erase(self_iter);
542 continue;
543 }
544
545 // Both sides have self_key. In the logic table above, this reflects
546 // the case (self.present & other.present). So, intersect self with
547 // other.
548 const auto &other_bitmap = other_iter->second;
549 self_bitmap &= other_bitmap;
550 if (self_bitmap.isEmpty()) {
551 // ...but if intersection is empty, remove it altogether.
552 roarings.erase(self_iter);
553 }
554 }
555 return *this;
556 }
557
564 if (this == &other) {
565 // Subtracting *this from itself results in the empty map.
566 roarings.clear();
567 return *this;
568 }
569
570 // Logic table summarizing what to do when a given outer key is
571 // present vs. absent from self and other.
572 //
573 // self other (self - other) work to do
574 // --------------------------------------------
575 // absent absent empty None
576 // absent present empty None
577 // present absent unchanged None
578 // present present empty or not Subtract other from self, but
579 // erase self if result is empty
580 //
581 // Because there is only work to do when a key is present in both 'self'
582 // and 'other', the main while loop ping-pongs back and forth until it
583 // finds the next key that is the same on both sides.
584
585 auto self_iter = roarings.begin();
586 auto other_iter = other.roarings.cbegin();
587
588 while (self_iter != roarings.end() &&
589 other_iter != other.roarings.cend()) {
590 auto self_key = self_iter->first;
591 auto other_key = other_iter->first;
592 if (self_key < other_key) {
593 // Because self_key is < other_key, advance self_iter to the
594 // first point where self_key >= other_key (or end).
595 self_iter = roarings.lower_bound(other_key);
596 continue;
597 }
598
599 if (self_key > other_key) {
600 // Because self_key is > other_key, advance other_iter to the
601 // first point where other_key >= self_key (or end).
602 other_iter = other.roarings.lower_bound(self_key);
603 continue;
604 }
605
606 // Both sides have self_key. In the logic table above, this reflects
607 // the case (self.present & other.present). So subtract other from
608 // self.
609 auto &self_bitmap = self_iter->second;
610 const auto &other_bitmap = other_iter->second;
611 self_bitmap -= other_bitmap;
612
613 if (self_bitmap.isEmpty()) {
614 // ...but if subtraction is empty, remove it altogether.
615 self_iter = roarings.erase(self_iter);
616 } else {
617 ++self_iter;
618 }
619 ++other_iter;
620 }
621 return *this;
622 }
623
632 if (this == &other) {
633 // ORing *this with itself is a no-op.
634 return *this;
635 }
636
637 // Logic table summarizing what to do when a given outer key is
638 // present vs. absent from self and other.
639 //
640 // self other (self | other) work to do
641 // --------------------------------------------
642 // absent absent empty None
643 // absent present not empty Copy other to self and set flags
644 // present absent unchanged None
645 // present present not empty self |= other
646 //
647 // Because there is only work to do when a key is present in 'other',
648 // the main for loop iterates over entries in 'other'.
649
650 for (const auto &other_entry : other.roarings) {
651 const auto &other_bitmap = other_entry.second;
652
653 // Try to insert other_bitmap into self at other_key. We take
654 // advantage of the fact that std::map::insert will not overwrite an
655 // existing entry.
656 auto insert_result = roarings.insert(other_entry);
657 auto self_iter = insert_result.first;
658 auto insert_happened = insert_result.second;
659 auto &self_bitmap = self_iter->second;
660
661 if (insert_happened) {
662 // Key was not present in self, so insert was performed above.
663 // In the logic table above, this reflects the case
664 // (self.absent | other.present). Because the copy has already
665 // happened, thanks to the 'insert' operation above, we just
666 // need to set the copyOnWrite flag.
667 self_bitmap.setCopyOnWrite(copyOnWrite);
668 continue;
669 }
670
671 // Both sides have self_key, and the insert was not performed. In
672 // the logic table above, this reflects the case
673 // (self.present & other.present). So OR other into self.
674 self_bitmap |= other_bitmap;
675 }
676 return *this;
677 }
678
684 if (this == &other) {
685 // XORing *this with itself results in the empty map.
686 roarings.clear();
687 return *this;
688 }
689
690 // Logic table summarizing what to do when a given outer key is
691 // present vs. absent from self and other.
692 //
693 // self other (self ^ other) work to do
694 // --------------------------------------------
695 // absent absent empty None
696 // absent present non-empty Copy other to self and set flags
697 // present absent unchanged None
698 // present present empty or not XOR other into self, but erase self
699 // if result is empty.
700 //
701 // Because there is only work to do when a key is present in 'other',
702 // the main for loop iterates over entries in 'other'.
703
704 for (const auto &other_entry : other.roarings) {
705 const auto &other_bitmap = other_entry.second;
706
707 // Try to insert other_bitmap into self at other_key. We take
708 // advantage of the fact that std::map::insert will not overwrite an
709 // existing entry.
710 auto insert_result = roarings.insert(other_entry);
711 auto self_iter = insert_result.first;
712 auto insert_happened = insert_result.second;
713 auto &self_bitmap = self_iter->second;
714
715 if (insert_happened) {
716 // Key was not present in self, so insert was performed above.
717 // In the logic table above, this reflects the case
718 // (self.absent ^ other.present). Because the copy has already
719 // happened, thanks to the 'insert' operation above, we just
720 // need to set the copyOnWrite flag.
721 self_bitmap.setCopyOnWrite(copyOnWrite);
722 continue;
723 }
724
725 // Both sides have self_key, and the insert was not performed. In
726 // the logic table above, this reflects the case
727 // (self.present ^ other.present). So XOR other into self.
728 self_bitmap ^= other_bitmap;
729
730 if (self_bitmap.isEmpty()) {
731 // ...but if intersection is empty, remove it altogether.
732 roarings.erase(self_iter);
733 }
734 }
735 return *this;
736 }
737
741 void swap(Roaring64Map &r) { roarings.swap(r.roarings); }
742
749 uint64_t cardinality() const {
750 if (isFull()) {
751#if ROARING_EXCEPTIONS
752 throw std::length_error(
753 "bitmap is full, cardinality is 2^64, "
754 "unable to represent in a 64-bit integer");
755#else
757 "bitmap is full, cardinality is 2^64, "
758 "unable to represent in a 64-bit integer");
759#endif
760 }
761 return std::accumulate(
762 roarings.cbegin(), roarings.cend(), (uint64_t)0,
763 [](uint64_t previous,
764 const std::pair<const uint32_t, Roaring> &map_entry) {
765 return previous + map_entry.second.cardinality();
766 });
767 }
768
772 bool isEmpty() const {
773 return std::all_of(
774 roarings.cbegin(), roarings.cend(),
775 [](const std::pair<const uint32_t, Roaring> &map_entry) {
776 return map_entry.second.isEmpty();
777 });
778 }
779
783 bool isFull() const {
784 // only bother to check if map is fully saturated
785 //
786 // we put std::numeric_limits<>::max/min in parentheses
787 // to avoid a clash with the Windows.h header under Windows
788 return roarings.size() ==
789 ((uint64_t)(std::numeric_limits<uint32_t>::max)()) + 1
790 ? std::all_of(roarings.cbegin(), roarings.cend(),
791 [](const std::pair<const uint32_t, Roaring>
792 &roaring_map_entry) {
793 return roaring_map_entry.second.isFull();
794 })
795 : false;
796 }
797
801 bool isSubset(const Roaring64Map &r) const {
802 for (const auto &map_entry : roarings) {
803 if (map_entry.second.isEmpty()) {
804 continue;
805 }
806 auto roaring_iter = r.roarings.find(map_entry.first);
807 if (roaring_iter == r.roarings.cend())
808 return false;
809 else if (!map_entry.second.isSubset(roaring_iter->second))
810 return false;
811 }
812 return true;
813 }
814
821 bool isStrictSubset(const Roaring64Map &r) const {
822 return isSubset(r) && cardinality() != r.cardinality();
823 }
824
831 void toUint64Array(uint64_t *ans) const {
832 // Annoyingly, VS 2017 marks std::accumulate() as [[nodiscard]]
833 (void)std::accumulate(
834 roarings.cbegin(), roarings.cend(), ans,
835 [](uint64_t *previous,
836 const std::pair<const uint32_t, Roaring> &map_entry) {
837 for (uint32_t low_bits : map_entry.second)
838 *previous++ = uniteBytes(map_entry.first, low_bits);
839 return previous;
840 });
841 }
842
846 bool operator==(const Roaring64Map &r) const {
847 // we cannot use operator == on the map because either side may contain
848 // empty Roaring Bitmaps
849 auto lhs_iter = roarings.cbegin();
850 auto lhs_cend = roarings.cend();
851 auto rhs_iter = r.roarings.cbegin();
852 auto rhs_cend = r.roarings.cend();
853 while (lhs_iter != lhs_cend && rhs_iter != rhs_cend) {
854 auto lhs_key = lhs_iter->first, rhs_key = rhs_iter->first;
855 const auto &lhs_map = lhs_iter->second, &rhs_map = rhs_iter->second;
856 if (lhs_map.isEmpty()) {
857 ++lhs_iter;
858 continue;
859 }
860 if (rhs_map.isEmpty()) {
861 ++rhs_iter;
862 continue;
863 }
864 if (!(lhs_key == rhs_key)) {
865 return false;
866 }
867 if (!(lhs_map == rhs_map)) {
868 return false;
869 }
870 ++lhs_iter;
871 ++rhs_iter;
872 }
873 while (lhs_iter != lhs_cend) {
874 if (!lhs_iter->second.isEmpty()) {
875 return false;
876 }
877 ++lhs_iter;
878 }
879 while (rhs_iter != rhs_cend) {
880 if (!rhs_iter->second.isEmpty()) {
881 return false;
882 }
883 ++rhs_iter;
884 }
885 return true;
886 }
887
892 void flip(uint64_t min, uint64_t max) {
893 if (min >= max) {
894 return;
895 }
896 flipClosed(min, max - 1);
897 }
898
903 void flipClosed(uint32_t min, uint32_t max) {
904 auto iter = roarings.begin();
905 // Since min and max are uint32_t, highbytes(min or max) == 0. The inner
906 // bitmap we are looking for, if it exists, will be at the first slot of
907 // 'roarings'. If it does not exist, we have to create it.
908 if (iter == roarings.end() || iter->first != 0) {
909 iter = roarings.emplace_hint(iter, std::piecewise_construct,
910 std::forward_as_tuple(0),
911 std::forward_as_tuple());
912 auto &bitmap = iter->second;
913 bitmap.setCopyOnWrite(copyOnWrite);
914 }
915 auto &bitmap = iter->second;
916 bitmap.flipClosed(min, max);
917 eraseIfEmpty(iter);
918 }
919
924 void flipClosed(uint64_t min, uint64_t max) {
925 if (min > max) {
926 return;
927 }
928 uint32_t start_high = highBytes(min);
929 uint32_t start_low = lowBytes(min);
930 uint32_t end_high = highBytes(max);
931 uint32_t end_low = lowBytes(max);
932
933 // We put std::numeric_limits<>::max in parentheses to avoid a
934 // clash with the Windows.h header under Windows.
935 const uint32_t uint32_max = (std::numeric_limits<uint32_t>::max)();
936
937 // Fill in any nonexistent slots with empty Roarings. This simplifies
938 // the logic below, allowing it to simply iterate over the map between
939 // 'start_high' and 'end_high' in a linear fashion.
940 auto current_iter = ensureRangePopulated(start_high, end_high);
941
942 // If start and end land on the same inner bitmap, then we can do the
943 // whole operation in one call.
944 if (start_high == end_high) {
945 auto &bitmap = current_iter->second;
946 bitmap.flipClosed(start_low, end_low);
947 eraseIfEmpty(current_iter);
948 return;
949 }
950
951 // Because start and end don't land on the same inner bitmap,
952 // we need to do this in multiple steps:
953 // 1. Partially flip the first bitmap in the closed interval
954 // [start_low, uint32_max]
955 // 2. Flip intermediate bitmaps completely: [0, uint32_max]
956 // 3. Partially flip the last bitmap in the closed interval
957 // [0, end_low]
958
959 auto num_intermediate_bitmaps = end_high - start_high - 1;
960
961 // 1. Partially flip the first bitmap.
962 {
963 auto &bitmap = current_iter->second;
964 bitmap.flipClosed(start_low, uint32_max);
965 auto temp = current_iter++;
966 eraseIfEmpty(temp);
967 }
968
969 // 2. Flip intermediate bitmaps completely.
970 for (uint32_t i = 0; i != num_intermediate_bitmaps; ++i) {
971 auto &bitmap = current_iter->second;
972 bitmap.flipClosed(0, uint32_max);
973 auto temp = current_iter++;
974 eraseIfEmpty(temp);
975 }
976
977 // 3. Partially flip the last bitmap.
978 auto &bitmap = current_iter->second;
979 bitmap.flipClosed(0, end_low);
980 eraseIfEmpty(current_iter);
981 }
982
988 return std::accumulate(
989 roarings.begin(), roarings.end(), true,
990 [](bool previous, std::pair<const uint32_t, Roaring> &map_entry) {
991 return map_entry.second.removeRunCompression() && previous;
992 });
993 }
994
1002 return std::accumulate(
1003 roarings.begin(), roarings.end(), true,
1004 [](bool previous, std::pair<const uint32_t, Roaring> &map_entry) {
1005 return map_entry.second.runOptimize() && previous;
1006 });
1007 }
1008
1013 size_t shrinkToFit() {
1014 size_t savedBytes = 0;
1015 auto iter = roarings.begin();
1016 while (iter != roarings.cend()) {
1017 if (iter->second.isEmpty()) {
1018 // empty Roarings are 84 bytes
1019 savedBytes += 88;
1020 roarings.erase(iter++);
1021 } else {
1022 savedBytes += iter->second.shrinkToFit();
1023 iter++;
1024 }
1025 }
1026 return savedBytes;
1027 }
1028
1040 void iterate(api::roaring_iterator64 iterator, void *ptr) const {
1041 for (const auto &map_entry : roarings) {
1042 bool should_continue =
1043 roaring_iterate64(&map_entry.second.roaring, iterator,
1044 uint64_t(map_entry.first) << 32, ptr);
1045 if (!should_continue) {
1046 break;
1047 }
1048 }
1049 }
1050
1057 bool select(uint64_t rank, uint64_t *element) const {
1058 for (const auto &map_entry : roarings) {
1059 auto key = map_entry.first;
1060 const auto &bitmap = map_entry.second;
1061
1062 uint64_t sub_cardinality = bitmap.cardinality();
1063 if (rank < sub_cardinality) {
1064 uint32_t low_bytes;
1065 // Casting rank to uint32_t is safe because
1066 // rank < sub_cardinality and sub_cardinality <= 2^32.
1067 if (!bitmap.select((uint32_t)rank, &low_bytes)) {
1069 "Logic error: bitmap.select() "
1070 "returned false despite rank < cardinality()");
1071 }
1072 *element = uniteBytes(key, low_bytes);
1073 return true;
1074 }
1075 rank -= sub_cardinality;
1076 }
1077 return false;
1078 }
1079
1083 uint64_t rank(uint64_t x) const {
1084 uint64_t result = 0;
1085 // Find the first bitmap >= x's bucket. If that is the bucket x would be
1086 // in, find it's rank in that bucket. Either way, we're left with a
1087 // range of all buckets strictly smaller than x's bucket, add all their
1088 // cardinalities together.
1089 auto end = roarings.lower_bound(highBytes(x));
1090 if (end != roarings.cend() && end->first == highBytes(x)) {
1091 result += end->second.rank(lowBytes(x));
1092 }
1093 for (auto iter = roarings.cbegin(); iter != end; ++iter) {
1094 result += iter->second.cardinality();
1095 }
1096 return result;
1097 }
1098
1106 int64_t getIndex(uint64_t x) const {
1107 int64_t index = 0;
1108 auto roaring_destination = roarings.find(highBytes(x));
1109 if (roaring_destination != roarings.cend()) {
1110 for (auto roaring_iter = roarings.cbegin();
1111 roaring_iter != roaring_destination; ++roaring_iter) {
1112 index += roaring_iter->second.cardinality();
1113 }
1114 auto low_idx = roaring_destination->second.getIndex(lowBytes(x));
1115 if (low_idx < 0) return -1;
1116 index += low_idx;
1117 return index;
1118 }
1119 return -1;
1120 }
1121
1131 size_t write(char *buf, bool portable = true) const {
1132 const char *orig = buf;
1133 // push map size
1134 uint64_t map_size = roarings.size();
1135 std::memcpy(buf, &map_size, sizeof(uint64_t));
1136 buf += sizeof(uint64_t);
1137 std::for_each(roarings.cbegin(), roarings.cend(),
1138 [&buf, portable](
1139 const std::pair<const uint32_t, Roaring> &map_entry) {
1140 // push map key
1141 std::memcpy(buf, &map_entry.first, sizeof(uint32_t));
1142 // ^-- Note: `*((uint32_t*)buf) = map_entry.first;` is
1143 // undefined
1144
1145 buf += sizeof(uint32_t);
1146 // push map value Roaring
1147 buf += map_entry.second.write(buf, portable);
1148 });
1149 return buf - orig;
1150 }
1151
1164 static Roaring64Map read(const char *buf, bool portable = true) {
1165 Roaring64Map result;
1166 // get map size
1167 uint64_t map_size;
1168 std::memcpy(&map_size, buf, sizeof(uint64_t));
1169 buf += sizeof(uint64_t);
1170 for (uint64_t lcv = 0; lcv < map_size; lcv++) {
1171 // get map key
1172 uint32_t key;
1173 std::memcpy(&key, buf, sizeof(uint32_t));
1174 // ^-- Note: `uint32_t key = *((uint32_t*)buf);` is undefined
1175
1176 buf += sizeof(uint32_t);
1177 // read map value Roaring
1178 Roaring read_var = Roaring::read(buf, portable);
1179 // forward buffer past the last Roaring Bitmap
1180 buf += read_var.getSizeInBytes(portable);
1181 result.emplaceOrInsert(key, std::move(read_var));
1182 }
1183 return result;
1184 }
1185
1193 static Roaring64Map readSafe(const char *buf, size_t maxbytes) {
1194 if (maxbytes < sizeof(uint64_t)) {
1195 ROARING_TERMINATE("ran out of bytes");
1196 }
1197 Roaring64Map result;
1198 uint64_t map_size;
1199 std::memcpy(&map_size, buf, sizeof(uint64_t));
1200 buf += sizeof(uint64_t);
1201 maxbytes -= sizeof(uint64_t);
1202 for (uint64_t lcv = 0; lcv < map_size; lcv++) {
1203 if (maxbytes < sizeof(uint32_t)) {
1204 ROARING_TERMINATE("ran out of bytes");
1205 }
1206 uint32_t key;
1207 std::memcpy(&key, buf, sizeof(uint32_t));
1208 // ^-- Note: `uint32_t key = *((uint32_t*)buf);` is undefined
1209
1210 buf += sizeof(uint32_t);
1211 maxbytes -= sizeof(uint32_t);
1212 // read map value Roaring
1213 Roaring read_var = Roaring::readSafe(buf, maxbytes);
1214 // forward buffer past the last Roaring Bitmap
1215 size_t tz = read_var.getSizeInBytes(true);
1216 buf += tz;
1217 maxbytes -= tz;
1218 result.emplaceOrInsert(key, std::move(read_var));
1219 }
1220 return result;
1221 }
1222
1230 size_t getSizeInBytes(bool portable = true) const {
1231 // start with, respectively, map size and size of keys for each map
1232 // entry
1233 return std::accumulate(
1234 roarings.cbegin(), roarings.cend(),
1235 sizeof(uint64_t) + roarings.size() * sizeof(uint32_t),
1236 [=](size_t previous,
1237 const std::pair<const uint32_t, Roaring> &map_entry) {
1238 // add in bytes used by each Roaring
1239 return previous + map_entry.second.getSizeInBytes(portable);
1240 });
1241 }
1242
1243 static const Roaring64Map frozenView(const char *buf) {
1244 // size of bitmap buffer and key
1245 const size_t metadata_size = sizeof(size_t) + sizeof(uint32_t);
1246
1247 Roaring64Map result;
1248
1249 // get map size
1250 uint64_t map_size;
1251 memcpy(&map_size, buf, sizeof(uint64_t));
1252 buf += sizeof(uint64_t);
1253
1254 for (uint64_t lcv = 0; lcv < map_size; lcv++) {
1255 // pad to 32 bytes minus the metadata size
1256 while (((uintptr_t)buf + metadata_size) % 32 != 0) buf++;
1257
1258 // get bitmap size
1259 size_t len;
1260 memcpy(&len, buf, sizeof(size_t));
1261 buf += sizeof(size_t);
1262
1263 // get map key
1264 uint32_t key;
1265 memcpy(&key, buf, sizeof(uint32_t));
1266 buf += sizeof(uint32_t);
1267
1268 // read map value Roaring
1269 const Roaring read = Roaring::frozenView(buf, len);
1270 result.emplaceOrInsert(key, read);
1271
1272 // forward buffer past the last Roaring Bitmap
1273 buf += len;
1274 }
1275 return result;
1276 }
1277
1278 static const Roaring64Map portableDeserializeFrozen(const char *buf) {
1279 Roaring64Map result;
1280 // get map size
1281 uint64_t map_size;
1282 std::memcpy(&map_size, buf, sizeof(uint64_t));
1283 buf += sizeof(uint64_t);
1284 for (uint64_t lcv = 0; lcv < map_size; lcv++) {
1285 // get map key
1286 uint32_t key;
1287 std::memcpy(&key, buf, sizeof(uint32_t));
1288 buf += sizeof(uint32_t);
1289 // read map value Roaring
1291 // forward buffer past the last Roaring bitmap
1292 buf += read_var.getSizeInBytes(true);
1293 result.emplaceOrInsert(key, std::move(read_var));
1294 }
1295 return result;
1296 }
1297
1298 // As with serialized 64-bit bitmaps, 64-bit frozen bitmaps are serialized
1299 // by concatenating one or more Roaring::write output buffers with the
1300 // preceeding map key. Unlike standard bitmap serialization, frozen bitmaps
1301 // must be 32-byte aligned and requires a buffer length to parse. As a
1302 // result, each concatenated output of Roaring::writeFrozen is preceeded by
1303 // padding, the buffer size (size_t), and the map key (uint32_t). The
1304 // padding is used to ensure 32-byte alignment, but since it is followed by
1305 // the buffer size and map key, it actually pads to `(x - sizeof(size_t) +
1306 // sizeof(uint32_t)) mod 32` to leave room for the metadata.
1307 void writeFrozen(char *buf) const {
1308 // size of bitmap buffer and key
1309 const size_t metadata_size = sizeof(size_t) + sizeof(uint32_t);
1310
1311 // push map size
1312 uint64_t map_size = roarings.size();
1313 memcpy(buf, &map_size, sizeof(uint64_t));
1314 buf += sizeof(uint64_t);
1315
1316 for (auto &map_entry : roarings) {
1317 size_t frozenSizeInBytes = map_entry.second.getFrozenSizeInBytes();
1318
1319 // pad to 32 bytes minus the metadata size
1320 while (((uintptr_t)buf + metadata_size) % 32 != 0) buf++;
1321
1322 // push bitmap size
1323 memcpy(buf, &frozenSizeInBytes, sizeof(size_t));
1324 buf += sizeof(size_t);
1325
1326 // push map key
1327 memcpy(buf, &map_entry.first, sizeof(uint32_t));
1328 buf += sizeof(uint32_t);
1329
1330 // push map value Roaring
1331 map_entry.second.writeFrozen(buf);
1332 buf += map_entry.second.getFrozenSizeInBytes();
1333 }
1334 }
1335
1336 size_t getFrozenSizeInBytes() const {
1337 // size of bitmap size and map key
1338 const size_t metadata_size = sizeof(size_t) + sizeof(uint32_t);
1339 size_t ret = 0;
1340
1341 // map size
1342 ret += sizeof(uint64_t);
1343
1344 for (auto &map_entry : roarings) {
1345 // pad to 32 bytes minus the metadata size
1346 while ((ret + metadata_size) % 32 != 0) ret++;
1347 ret += metadata_size;
1348
1349 // frozen bitmaps must be 32-byte aligned
1350 ret += map_entry.second.getFrozenSizeInBytes();
1351 }
1352 return ret;
1353 }
1354
1365 return Roaring64Map(*this) &= o;
1366 }
1367
1373 return Roaring64Map(*this) -= o;
1374 }
1375
1381 return Roaring64Map(*this) |= o;
1382 }
1383
1389 return Roaring64Map(*this) ^= o;
1390 }
1391
1395 void setCopyOnWrite(bool val) {
1396 if (copyOnWrite == val) return;
1397 copyOnWrite = val;
1398 std::for_each(roarings.begin(), roarings.end(),
1399 [=](std::pair<const uint32_t, Roaring> &map_entry) {
1400 map_entry.second.setCopyOnWrite(val);
1401 });
1402 }
1403
1408 void printf() const {
1409 auto sink = [](const std::string &s) { fputs(s.c_str(), stdout); };
1410 printToSink(sink);
1411 sink("\n");
1412 }
1413
1417 std::string toString() const {
1418 std::string result;
1419 auto sink = [&result](const std::string &s) { result += s; };
1420 printToSink(sink);
1421 return result;
1422 }
1423
1427 bool getCopyOnWrite() const { return copyOnWrite; }
1428
1433 static Roaring64Map fastunion(size_t n, const Roaring64Map **inputs) {
1434 // The strategy here is to basically do a "group by" operation.
1435 // We group the input roarings by key, do a 32-bit
1436 // roaring_bitmap_or_many on each group, and collect the results.
1437 // We accomplish the "group by" operation using a priority queue, which
1438 // tracks the next key for each of our input maps. At each step, our
1439 // algorithm takes the next subset of maps that share the same next key,
1440 // runs roaring_bitmap_or_many on those bitmaps, and then advances the
1441 // current_iter on all the affected entries and then repeats.
1442
1443 // There is an entry in our priority queue for each of the 'n' inputs.
1444 // For a given Roaring64Map, we look at its underlying 'roarings'
1445 // std::map, and take its begin() and end(). This forms our half-open
1446 // interval [current_iter, end_iter), which we keep in the priority
1447 // queue as a pq_entry. These entries are updated (removed and then
1448 // reinserted with the pq_entry.iterator field advanced by one step) as
1449 // our algorithm progresses. But when a given interval becomes empty
1450 // (i.e. pq_entry.iterator == pq_entry.end) it is not returned to the
1451 // priority queue.
1452 struct pq_entry {
1453 roarings_t::const_iterator iterator;
1454 roarings_t::const_iterator end;
1455 };
1456
1457 // Custom comparator for the priority queue.
1458 auto pq_comp = [](const pq_entry &lhs, const pq_entry &rhs) {
1459 auto left_key = lhs.iterator->first;
1460 auto right_key = rhs.iterator->first;
1461
1462 // We compare in the opposite direction than normal because priority
1463 // queues normally order from largest to smallest, but we want
1464 // smallest to largest.
1465 return left_key > right_key;
1466 };
1467
1468 // Create and populate the priority queue.
1469 std::priority_queue<pq_entry, std::vector<pq_entry>, decltype(pq_comp)>
1470 pq(pq_comp);
1471 for (size_t i = 0; i < n; ++i) {
1472 const auto &roarings = inputs[i]->roarings;
1473 if (roarings.begin() != roarings.end()) {
1474 pq.push({roarings.begin(), roarings.end()});
1475 }
1476 }
1477
1478 // A reusable vector that holds the pointers to the inner bitmaps that
1479 // we pass to the underlying 32-bit fastunion operation.
1480 std::vector<const roaring_bitmap_t *> group_bitmaps;
1481
1482 // Summary of the algorithm:
1483 // 1. While the priority queue is not empty:
1484 // A. Get its lowest key. Call this group_key
1485 // B. While the lowest entry in the priority queue has a key equal to
1486 // group_key:
1487 // 1. Remove this entry (the pair {current_iter, end_iter}) from
1488 // the priority queue.
1489 // 2. Add the bitmap pointed to by current_iter to a list of
1490 // 32-bit bitmaps to process.
1491 // 3. Advance current_iter. Now it will point to a bitmap entry
1492 // with some key greater than group_key (or it will point to
1493 // end()).
1494 // 4. If current_iter != end_iter, reinsert the pair into the
1495 // priority queue.
1496 // C. Invoke the 32-bit roaring_bitmap_or_many() and add to result
1497 Roaring64Map result;
1498 while (!pq.empty()) {
1499 // Find the next key (the lowest key) in the priority queue.
1500 auto group_key = pq.top().iterator->first;
1501
1502 // The purpose of the inner loop is to gather all the inner bitmaps
1503 // that share "group_key" into "group_bitmaps" so that they can be
1504 // fed to roaring_bitmap_or_many(). While we are doing this, we
1505 // advance those iterators to their next value and reinsert them
1506 // into the priority queue (unless they reach their end).
1507 group_bitmaps.clear();
1508 while (!pq.empty()) {
1509 auto candidate_current_iter = pq.top().iterator;
1510 auto candidate_end_iter = pq.top().end;
1511
1512 auto candidate_key = candidate_current_iter->first;
1513 const auto &candidate_bitmap = candidate_current_iter->second;
1514
1515 // This element will either be in the group (having
1516 // key == group_key) or it will not be in the group (having
1517 // key > group_key). (Note it cannot have key < group_key
1518 // because of the ordered nature of the priority queue itself
1519 // and the ordered nature of all the underlying roaring maps).
1520 if (candidate_key != group_key) {
1521 // This entry, and (thanks to the nature of the priority
1522 // queue) all other entries as well, are all greater than
1523 // group_key, so we're done collecting elements for the
1524 // current group. Because of the way this loop was written,
1525 // the group will will always contain at least one element.
1526 break;
1527 }
1528
1529 group_bitmaps.push_back(&candidate_bitmap.roaring);
1530 // Remove this entry from the priority queue. Note this
1531 // invalidates pq.top() so make sure you don't have any dangling
1532 // references to it.
1533 pq.pop();
1534
1535 // Advance 'candidate_current_iter' and insert a new entry
1536 // {candidate_current_iter, candidate_end_iter} into the
1537 // priority queue (unless it has reached its end).
1538 ++candidate_current_iter;
1539 if (candidate_current_iter != candidate_end_iter) {
1540 pq.push({candidate_current_iter, candidate_end_iter});
1541 }
1542 }
1543
1544 // Use the fast inner union to combine these.
1545 auto *inner_result = roaring_bitmap_or_many(group_bitmaps.size(),
1546 group_bitmaps.data());
1547 // Insert the 32-bit result at end of the 'roarings' map of the
1548 // result we are building.
1549 result.roarings.insert(
1550 result.roarings.end(),
1551 std::make_pair(group_key, Roaring(inner_result)));
1552 }
1553 return result;
1554 }
1555
1560
1570 const_iterator begin() const;
1571
1576 const_iterator end() const;
1577
1578 private:
1579 typedef std::map<uint32_t, Roaring> roarings_t;
1580 roarings_t roarings{}; // The empty constructor silences warnings from
1581 // pedantic static analyzers.
1582 bool copyOnWrite{false};
1583 static constexpr uint32_t highBytes(const uint64_t in) {
1584 return uint32_t(in >> 32);
1585 }
1586 static constexpr uint32_t lowBytes(const uint64_t in) {
1587 return uint32_t(in);
1588 }
1589 static constexpr uint64_t uniteBytes(const uint32_t highBytes,
1590 const uint32_t lowBytes) {
1591 return (uint64_t(highBytes) << 32) | uint64_t(lowBytes);
1592 }
1593 // this is needed to tolerate gcc's C++11 libstdc++ lacking emplace
1594 // prior to version 4.8
1595 void emplaceOrInsert(const uint32_t key, const Roaring &value) {
1596#if defined(__GLIBCXX__) && __GLIBCXX__ < 20130322
1597 roarings.insert(std::make_pair(key, value));
1598#else
1599 roarings.emplace(std::make_pair(key, value));
1600#endif
1601 }
1602
1603 void emplaceOrInsert(const uint32_t key, Roaring &&value) {
1604#if defined(__GLIBCXX__) && __GLIBCXX__ < 20130322
1605 roarings.insert(std::make_pair(key, std::move(value)));
1606#else
1607 roarings.emplace(key, std::move(value));
1608#endif
1609 }
1610
1611 /*
1612 * Look up 'key' in the 'roarings' map. If it does not exist, create it.
1613 * Also, set its copyOnWrite flag to 'copyOnWrite'. Then return a reference
1614 * to the (already existing or newly created) inner bitmap.
1615 */
1616 Roaring &lookupOrCreateInner(uint32_t key) {
1617 auto &bitmap = roarings[key];
1618 bitmap.setCopyOnWrite(copyOnWrite);
1619 return bitmap;
1620 }
1621
1625 void printToSink(
1626 const std::function<void(const std::string &)> &sink) const {
1627 sink("{");
1628
1629 // Storage for snprintf. Big enough to store the decimal representation
1630 // of the largest uint64_t value and trailing \0.
1631 char buffer[32];
1632 const char *separator = "";
1633 // Reusable, and therefore avoids many repeated heap allocations.
1634 std::string callback_string;
1635 for (const auto &entry : roarings) {
1636 auto high_bits = entry.first;
1637 const auto &bitmap = entry.second;
1638 for (const auto low_bits : bitmap) {
1639 auto value = uniteBytes(high_bits, low_bits);
1640 snprintf(buffer, sizeof(buffer), "%" PRIu64, value);
1641 callback_string = separator;
1642 callback_string.append(buffer);
1643 sink(callback_string);
1644 separator = ",";
1645 }
1646 }
1647 sink("}");
1648 }
1649
1656 roarings_t::iterator ensureRangePopulated(uint32_t start_high,
1657 uint32_t end_high) {
1658 if (start_high > end_high) {
1659 ROARING_TERMINATE("Logic error: start_high > end_high");
1660 }
1661 // next_populated_iter points to the first entry in the outer map with
1662 // key >= start_high, or end().
1663 auto next_populated_iter = roarings.lower_bound(start_high);
1664
1665 // Use uint64_t to avoid an infinite loop when end_high == uint32_max.
1666 roarings_t::iterator start_iter{}; // Definitely assigned in loop.
1667 for (uint64_t slot = start_high; slot <= end_high; ++slot) {
1668 roarings_t::iterator slot_iter;
1669 if (next_populated_iter != roarings.end() &&
1670 next_populated_iter->first == slot) {
1671 // 'slot' index has caught up to next_populated_iter.
1672 // Note it here and advance next_populated_iter.
1673 slot_iter = next_populated_iter++;
1674 } else {
1675 // 'slot' index has not yet caught up to next_populated_iter.
1676 // Make a fresh entry {key = 'slot', value = Roaring()}, insert
1677 // it just prior to next_populated_iter, and set its copy
1678 // on write flag. We take pains to use emplace_hint and
1679 // piecewise_construct to minimize effort.
1680 slot_iter = roarings.emplace_hint(
1681 next_populated_iter, std::piecewise_construct,
1682 std::forward_as_tuple(uint32_t(slot)),
1683 std::forward_as_tuple());
1684 auto &bitmap = slot_iter->second;
1685 bitmap.setCopyOnWrite(copyOnWrite);
1686 }
1687
1688 // Make a note of the iterator of the starting slot. It will be
1689 // needed for the return value.
1690 if (slot == start_high) {
1691 start_iter = slot_iter;
1692 }
1693 }
1694 return start_iter;
1695 }
1696
1701 void eraseIfEmpty(roarings_t::iterator iter) {
1702 const auto &bitmap = iter->second;
1703 if (bitmap.isEmpty()) {
1704 roarings.erase(iter);
1705 }
1706 }
1707};
1708
1715 public:
1716 typedef std::bidirectional_iterator_tag iterator_category;
1717 typedef uint64_t *pointer;
1718 typedef uint64_t &reference;
1719 typedef uint64_t value_type;
1720 typedef int64_t difference_type;
1722
1724 bool exhausted = false)
1725 : p(&parent.roarings) {
1726 if (exhausted || parent.roarings.empty()) {
1727 map_iter = p->cend();
1728 } else {
1729 map_iter = parent.roarings.cbegin();
1730 roaring_iterator_init(&map_iter->second.roaring, &i);
1731 while (!i.has_value) {
1732 map_iter++;
1733 if (map_iter == p->cend()) return;
1734 roaring_iterator_init(&map_iter->second.roaring, &i);
1735 }
1736 }
1737 }
1738
1743 return Roaring64Map::uniteBytes(map_iter->first, i.current_value);
1744 }
1745
1746 bool operator<(const type_of_iterator &o) const {
1747 if (map_iter == p->cend()) return false;
1748 if (o.map_iter == o.p->cend()) return true;
1749 return **this < *o;
1750 }
1751
1752 bool operator<=(const type_of_iterator &o) const {
1753 if (o.map_iter == o.p->cend()) return true;
1754 if (map_iter == p->cend()) return false;
1755 return **this <= *o;
1756 }
1757
1758 bool operator>(const type_of_iterator &o) const {
1759 if (o.map_iter == o.p->cend()) return false;
1760 if (map_iter == p->cend()) return true;
1761 return **this > *o;
1762 }
1763
1764 bool operator>=(const type_of_iterator &o) const {
1765 if (map_iter == p->cend()) return true;
1766 if (o.map_iter == o.p->cend()) return false;
1767 return **this >= *o;
1768 }
1769
1770 type_of_iterator &operator++() { // ++i, must returned inc. value
1771 if (i.has_value == true) roaring_uint32_iterator_advance(&i);
1772 while (!i.has_value) {
1773 ++map_iter;
1774 if (map_iter == p->cend()) return *this;
1775 roaring_iterator_init(&map_iter->second.roaring, &i);
1776 }
1777 return *this;
1778 }
1779
1780 type_of_iterator operator++(int) { // i++, must return orig. value
1783 while (!i.has_value) {
1784 ++map_iter;
1785 if (map_iter == p->cend()) return orig;
1786 roaring_iterator_init(&map_iter->second.roaring, &i);
1787 }
1788 return orig;
1789 }
1790
1796 map_iter = p->lower_bound(Roaring64Map::highBytes(x));
1797 if (map_iter != p->cend()) {
1798 roaring_iterator_init(&map_iter->second.roaring, &i);
1799 if (map_iter->first == Roaring64Map::highBytes(x)) {
1801 &i, Roaring64Map::lowBytes(x)))
1802 return true;
1803 ++map_iter;
1804 if (map_iter == p->cend()) return false;
1805 roaring_iterator_init(&map_iter->second.roaring, &i);
1806 }
1807 return true;
1808 }
1809 return false;
1810 }
1811
1813 CROARING_DEPRECATED bool move(const value_type &x) {
1814 return move_equalorlarger(x);
1815 }
1816
1817 type_of_iterator &operator--() { // --i, must return dec.value
1818 if (map_iter == p->cend()) {
1819 --map_iter;
1820 roaring_iterator_init_last(&map_iter->second.roaring, &i);
1821 if (i.has_value) return *this;
1822 }
1823
1825 while (!i.has_value) {
1826 if (map_iter == p->cbegin()) return *this;
1827 map_iter--;
1828 roaring_iterator_init_last(&map_iter->second.roaring, &i);
1829 }
1830 return *this;
1831 }
1832
1833 type_of_iterator operator--(int) { // i--, must return orig. value
1835 if (map_iter == p->cend()) {
1836 --map_iter;
1837 roaring_iterator_init_last(&map_iter->second.roaring, &i);
1838 return orig;
1839 }
1840
1842 while (!i.has_value) {
1843 if (map_iter == p->cbegin()) return orig;
1844 map_iter--;
1845 roaring_iterator_init_last(&map_iter->second.roaring, &i);
1846 }
1847 return orig;
1848 }
1849
1851 if (map_iter == p->cend() && o.map_iter == o.p->cend()) return true;
1852 if (o.map_iter == o.p->cend()) return false;
1853 return **this == *o;
1854 }
1855
1857 if (map_iter == p->cend() && o.map_iter == o.p->cend()) return false;
1858 if (o.map_iter == o.p->cend()) return true;
1859 return **this != *o;
1860 }
1861
1862 private:
1863 const std::map<uint32_t, Roaring> *p{nullptr};
1864 std::map<uint32_t, Roaring>::const_iterator
1865 map_iter{}; // The empty constructor silences warnings from pedantic
1866 // static analyzers.
1867 api::roaring_uint32_iterator_t
1868 i{}; // The empty constructor silences warnings from pedantic static
1869 // analyzers.
1870};
1871
1875
1879
1880} // namespace roaring
1881
1882#endif /* INCLUDE_ROARING_64_MAP_HH_ */
bool operator<(const type_of_iterator &o) const
bool operator<=(const type_of_iterator &o) const
bool operator!=(const Roaring64MapSetBitBiDirectionalIterator &o) const
Roaring64MapSetBitBiDirectionalIterator(const Roaring64Map &parent, bool exhausted=false)
bool operator>(const type_of_iterator &o) const
CROARING_DEPRECATED bool move(const value_type &x)
std::bidirectional_iterator_tag iterator_category
bool operator==(const Roaring64MapSetBitBiDirectionalIterator &o) const
Roaring64MapSetBitBiDirectionalIterator type_of_iterator
bool operator>=(const type_of_iterator &o) const
void addMany(size_t n_args, const uint32_t *vals)
void flip(uint64_t min, uint64_t max)
friend class Roaring64MapSetBitBiDirectionalIterator
Roaring64Map(size_t n, const uint64_t *data)
uint64_t rank(uint64_t x) const
size_t getFrozenSizeInBytes() const
Roaring64Map operator-(const Roaring64Map &o) const
void flipClosed(uint32_t min, uint32_t max)
bool contains(uint32_t x) const
Roaring64Map & operator^=(const Roaring64Map &other)
uint64_t cardinality() const
const_iterator end() const
void remove(uint64_t x)
Roaring64Map & operator|=(const Roaring64Map &other)
bool contains(uint64_t x) const
Roaring64Map(size_t n, const uint32_t *data)
std::string toString() const
Roaring64Map(const Roaring &r)
Roaring64MapSetBitBiDirectionalIterator const_iterator
bool operator==(const Roaring64Map &r) const
uint64_t maximum() const
bool isSubset(const Roaring64Map &r) const
const_iterator begin() const
size_t write(char *buf, bool portable=true) const
void removeRangeClosed(uint64_t min, uint64_t max)
Roaring64Map & operator-=(const Roaring64Map &other)
bool removeChecked(uint32_t x)
Roaring64Map operator|(const Roaring64Map &o) const
bool addChecked(uint64_t x)
void addRangeClosed(uint64_t min, uint64_t max)
bool getCopyOnWrite() const
Roaring64Map(Roaring &&r)
static Roaring64Map readSafe(const char *buf, size_t maxbytes)
void add(uint32_t x)
static Roaring64Map fastunion(size_t n, const Roaring64Map **inputs)
Roaring64Map & operator=(Roaring64Map &&r) noexcept=default
Roaring64Map & operator=(std::initializer_list< uint64_t > l)
Roaring64Map(const Roaring64Map &r)=default
Roaring64Map operator^(const Roaring64Map &o) const
static const Roaring64Map frozenView(const char *buf)
static Roaring64Map read(const char *buf, bool portable=true)
void addMany(size_t n_args, const uint64_t *vals)
static Roaring64Map bitmapOfList(std::initializer_list< uint64_t > l)
void addRange(uint64_t min, uint64_t max)
Roaring64Map operator&(const Roaring64Map &o) const
static Roaring64Map bitmapOf(size_t n...)
bool removeChecked(uint64_t x)
Roaring64Map(roaring_bitmap_t *s)
uint64_t minimum() const
Roaring64MapSetBitBiDirectionalIterator const_bidirectional_iterator
void setCopyOnWrite(bool val)
Roaring64Map(Roaring64Map &&r) noexcept=default
void flipClosed(uint64_t min, uint64_t max)
void toUint64Array(uint64_t *ans) const
static const Roaring64Map portableDeserializeFrozen(const char *buf)
size_t getSizeInBytes(bool portable=true) const
Roaring64Map & operator&=(const Roaring64Map &other)
void remove(uint32_t x)
void removeRange(uint64_t min, uint64_t max)
void iterate(api::roaring_iterator64 iterator, void *ptr) const
void removeRangeClosed(uint32_t min, uint32_t max)
void swap(Roaring64Map &r)
void addRangeClosed(uint32_t min, uint32_t max)
void add(uint64_t x)
bool select(uint64_t rank, uint64_t *element) const
void writeFrozen(char *buf) const
Roaring64Map & operator=(const Roaring64Map &r)=default
Roaring64Map(std::initializer_list< uint64_t > l)
bool isStrictSubset(const Roaring64Map &r) const
bool addChecked(uint32_t x)
int64_t getIndex(uint64_t x) const
static Roaring readSafe(const char *buf, size_t maxbytes)
Definition roaring.hh:680
bool addChecked(uint32_t x) noexcept
Definition roaring.hh:207
void addRangeClosed(const uint32_t min, const uint32_t max) noexcept
Definition roaring.hh:221
static Roaring read(const char *buf, bool portable=true)
Definition roaring.hh:647
void add(uint32_t x) noexcept
Definition roaring.hh:200
void addBulk(BulkContext &context, uint32_t x) noexcept
Definition roaring.hh:240
void addMany(size_t n_args, const uint32_t *vals) noexcept
Definition roaring.hh:228
static const Roaring frozenView(const char *buf, size_t length)
Definition roaring.hh:709
static const Roaring portableDeserializeFrozen(const char *buf)
Definition roaring.hh:724
size_t getSizeInBytes(bool portable=true) const noexcept
Definition roaring.hh:697
Roaring64MapSetBitBiDirectionalIterator Roaring64MapSetBitForwardIterator
void roaring_iterator_init_last(const roaring_bitmap_t *r, roaring_uint32_iterator_t *newit)
bool roaring_iterate64(const roaring_bitmap_t *r, roaring_iterator64 iterator, uint64_t high_bits, void *ptr)
void roaring_iterator_init(const roaring_bitmap_t *r, roaring_uint32_iterator_t *newit)
roaring_bitmap_t * roaring_bitmap_or_many(size_t number, const roaring_bitmap_t **rs)
bool roaring_uint32_iterator_move_equalorlarger(roaring_uint32_iterator_t *it, uint32_t val)
bool roaring_uint32_iterator_previous(roaring_uint32_iterator_t *it)
bool roaring_uint32_iterator_advance(roaring_uint32_iterator_t *it)
#define ROARING_TERMINATE(_s)
Definition roaring.hh:31