diff --git a/cpp/roaring.hh b/cpp/roaring.hh index 231dc5bd4..8a3b8b689 100644 --- a/cpp/roaring.hh +++ b/cpp/roaring.hh @@ -7,6 +7,7 @@ A C++ header for Roaring Bitmaps. #include #include #include +#include #include #include #include @@ -39,7 +40,10 @@ A C++ header for Roaring Bitmaps. namespace roaring { -class RoaringSetBitForwardIterator; +class RoaringSetBitBiDirectionalIterator; + +/** DEPRECATED, use `RoaringSetBitBiDirectionalIterator`. */ +using RoaringSetBitForwardIterator = RoaringSetBitBiDirectionalIterator; /** * A bit of context usable with `*Bulk()` functions. @@ -91,6 +95,16 @@ class Roaring { addMany(l.size(), l.begin()); } + /** + * Construct a roaring object by taking control of a malloc()'d C struct. + * + * Passing a NULL pointer is unsafe. + * The pointer to the C struct will be invalid after the call. + */ + explicit Roaring(roaring_bitmap_t *s) noexcept : roaring(*s) { + roaring_free(s); // deallocate the passed-in pointer + } + /** * Copy constructor. * It may throw std::runtime_error if there is insufficient memory. @@ -118,16 +132,6 @@ class Roaring { api::roaring_bitmap_init_cleared(&r.roaring); } - /** - * Construct a roaring object by taking control of a malloc()'d C struct. - * - * Passing a NULL pointer is unsafe. - * The pointer to the C struct will be invalid after the call. - */ - explicit Roaring(roaring_bitmap_t *s) noexcept : roaring(*s) { - roaring_free(s); // deallocate the passed-in pointer - } - /** * Construct a bitmap from a list of uint32_t values. */ @@ -142,6 +146,44 @@ class Roaring { return ans; } + /** + * Copies the content of the provided bitmap, and + * discard the current content. + * It may throw std::runtime_error if there is insufficient memory. + */ + Roaring &operator=(const Roaring &r) { + if (!api::roaring_bitmap_overwrite(&roaring, &r.roaring)) { + ROARING_TERMINATE("failed memory alloc in assignment"); + } + api::roaring_bitmap_set_copy_on_write( + &roaring, api::roaring_bitmap_get_copy_on_write(&r.roaring)); + return *this; + } + + /** + * Moves the content of the provided bitmap, and + * discard the current content. + */ + Roaring &operator=(Roaring &&r) noexcept { + api::roaring_bitmap_clear(&roaring); // free this class's allocations + + // !!! See notes in the Move Constructor regarding roaring_bitmap_move() + // + roaring = r.roaring; + api::roaring_bitmap_init_cleared(&r.roaring); + + return *this; + } + + /** + * Assignment from an initializer list. + */ + Roaring &operator=(std::initializer_list l) { + // Delegate to move assignment operator + *this = Roaring(l); + return *this; + } + /** * Construct a bitmap from a list of uint32_t values. * E.g., bitmapOfList({1,2,3}). @@ -242,6 +284,11 @@ class Roaring { return api::roaring_bitmap_remove_range_closed(&roaring, min, max); } + /** + * Clears the bitmap. + */ + void clear() { api::roaring_bitmap_clear(&roaring); } + /** * Return the largest value (if not empty) */ @@ -270,63 +317,9 @@ class Roaring { return api::roaring_bitmap_contains_range(&roaring, x, y); } - /** - * Destructor. By contract, calling roaring_bitmap_clear() is enough to - * release all auxiliary memory used by the structure. - */ - ~Roaring() { - if (!(roaring.high_low_container.flags & ROARING_FLAG_FROZEN)) { - api::roaring_bitmap_clear(&roaring); - } else { - // The roaring member variable copies the `roaring_bitmap_t` and - // nested `roaring_array_t` structures by value and is freed in the - // constructor, however the underlying memory arena used for the - // container data is not freed with it. Here we derive the arena - // pointer from the second arena allocation in - // `roaring_bitmap_frozen_view` and free it as well. - roaring_bitmap_free( - (roaring_bitmap_t *)((char *) - roaring.high_low_container.containers - - sizeof(roaring_bitmap_t))); - } - } - - /** - * Copies the content of the provided bitmap, and - * discard the current content. - * It may throw std::runtime_error if there is insufficient memory. - */ - Roaring &operator=(const Roaring &r) { - if (!api::roaring_bitmap_overwrite(&roaring, &r.roaring)) { - ROARING_TERMINATE("failed memory alloc in assignment"); - } - api::roaring_bitmap_set_copy_on_write( - &roaring, api::roaring_bitmap_get_copy_on_write(&r.roaring)); - return *this; - } - - /** - * Moves the content of the provided bitmap, and - * discard the current content. - */ - Roaring &operator=(Roaring &&r) noexcept { - api::roaring_bitmap_clear(&roaring); // free this class's allocations - - // !!! See notes in the Move Constructor regarding roaring_bitmap_move() - // - roaring = r.roaring; - api::roaring_bitmap_init_cleared(&r.roaring); - - return *this; - } - - /** - * Assignment from an initializer list. - */ - Roaring &operator=(std::initializer_list l) { - // Delegate to move assignment operator - *this = Roaring(l); - return *this; + bool containsRangeClosed(const uint32_t x, + const uint32_t y) const noexcept { + return api::roaring_bitmap_contains_range_closed(&roaring, x, y); } /** @@ -393,6 +386,16 @@ class Roaring { return api::roaring_bitmap_is_empty(&roaring); } + /** + * Returns true if the bitmap is full (cardinality is uint32_t max + 1). + * we put std::numeric_limits<>::max/min in parentheses + * to avoid a clash with the Windows.h header under Windows. + */ + bool isFull() const noexcept { + return api::roaring_bitmap_get_cardinality(&roaring) == + ((uint64_t)(std::numeric_limits::max)()) + 1; + } + /** * Returns true if the bitmap is subset of the other. */ @@ -443,8 +446,8 @@ class Roaring { * [range_start, range_end]. Areas outside the interval are unchanged. */ void flipClosed(uint32_t range_start, uint32_t range_end) noexcept { - api::roaring_bitmap_flip_inplace(&roaring, range_start, - uint64_t(range_end) + 1); + api::roaring_bitmap_flip_inplace_closed(&roaring, range_start, + range_end); } /** @@ -868,7 +871,30 @@ class Roaring { return ans; } - typedef RoaringSetBitForwardIterator const_iterator; + /** + * Destructor. By contract, calling roaring_bitmap_clear() is enough to + * release all auxiliary memory used by the structure. + */ + ~Roaring() { + if (!(roaring.high_low_container.flags & ROARING_FLAG_FROZEN)) { + api::roaring_bitmap_clear(&roaring); + } else { + // The roaring member variable copies the `roaring_bitmap_t` and + // nested `roaring_array_t` structures by value and is freed in the + // constructor, however the underlying memory arena used for the + // container data is not freed with it. Here we derive the arena + // pointer from the second arena allocation in + // `roaring_bitmap_frozen_view` and free it as well. + roaring_bitmap_free( + (roaring_bitmap_t *)((char *) + roaring.high_low_container.containers - + sizeof(roaring_bitmap_t))); + } + } + + friend class RoaringSetBitBiDirectionalIterator; + typedef RoaringSetBitBiDirectionalIterator const_iterator; + typedef RoaringSetBitBiDirectionalIterator const_bidirectional_iterator; /** * Returns an iterator that can be used to access the position of the set @@ -893,14 +919,26 @@ class Roaring { /** * Used to go through the set bits. Not optimally fast, but convenient. */ -class RoaringSetBitForwardIterator final { +class RoaringSetBitBiDirectionalIterator final { public: - typedef std::forward_iterator_tag iterator_category; + typedef std::bidirectional_iterator_tag iterator_category; typedef uint32_t *pointer; typedef uint32_t &reference_type; typedef uint32_t value_type; typedef int32_t difference_type; - typedef RoaringSetBitForwardIterator type_of_iterator; + typedef RoaringSetBitBiDirectionalIterator type_of_iterator; + + explicit RoaringSetBitBiDirectionalIterator(const Roaring &parent, + bool exhausted = false) { + if (exhausted) { + i.parent = &parent.roaring; + i.container_index = INT32_MAX; + i.has_value = false; + i.current_value = UINT32_MAX; + } else { + api::roaring_iterator_init(&parent.roaring, &i); + } + } /** * Provides the location of the set bit. @@ -931,66 +969,60 @@ class RoaringSetBitForwardIterator final { return i.current_value >= *o; } - /** - * Move the iterator to the first value >= val. - */ - void equalorlarger(uint32_t val) { - api::roaring_uint32_iterator_move_equalorlarger(&i, val); - } - type_of_iterator &operator++() { // ++i, must returned inc. value api::roaring_uint32_iterator_advance(&i); return *this; } type_of_iterator operator++(int) { // i++, must return orig. value - RoaringSetBitForwardIterator orig(*this); + RoaringSetBitBiDirectionalIterator orig(*this); api::roaring_uint32_iterator_advance(&i); return orig; } + /** + * Move the iterator to the first value >= val. + * Return true if there is such a value. + */ + bool move_equalorlarger(value_type val) { + return api::roaring_uint32_iterator_move_equalorlarger(&i, val); + } + + /** DEPRECATED, use `move_equalorlarger`.*/ + CROARING_DEPRECATED void equalorlarger(uint32_t val) { + api::roaring_uint32_iterator_move_equalorlarger(&i, val); + } + type_of_iterator &operator--() { // prefix -- api::roaring_uint32_iterator_previous(&i); return *this; } type_of_iterator operator--(int) { // postfix -- - RoaringSetBitForwardIterator orig(*this); + RoaringSetBitBiDirectionalIterator orig(*this); api::roaring_uint32_iterator_previous(&i); return orig; } - bool operator==(const RoaringSetBitForwardIterator &o) const { + bool operator==(const RoaringSetBitBiDirectionalIterator &o) const { return i.current_value == *o && i.has_value == o.i.has_value; } - bool operator!=(const RoaringSetBitForwardIterator &o) const { + bool operator!=(const RoaringSetBitBiDirectionalIterator &o) const { return i.current_value != *o || i.has_value != o.i.has_value; } - explicit RoaringSetBitForwardIterator(const Roaring &parent, - bool exhausted = false) { - if (exhausted) { - i.parent = &parent.roaring; - i.container_index = INT32_MAX; - i.has_value = false; - i.current_value = UINT32_MAX; - } else { - api::roaring_iterator_init(&parent.roaring, &i); - } - } - api::roaring_uint32_iterator_t i{}; // The empty constructor silences warnings from pedantic static // analyzers. }; -inline RoaringSetBitForwardIterator Roaring::begin() const { - return RoaringSetBitForwardIterator(*this); +inline RoaringSetBitBiDirectionalIterator Roaring::begin() const { + return RoaringSetBitBiDirectionalIterator(*this); } -inline RoaringSetBitForwardIterator &Roaring::end() const { - static RoaringSetBitForwardIterator e(*this, true); +inline RoaringSetBitBiDirectionalIterator &Roaring::end() const { + static RoaringSetBitBiDirectionalIterator e(*this, true); return e; } diff --git a/cpp/roaring64map.hh b/cpp/roaring64map.hh index 46e726a2d..d45bb2272 100644 --- a/cpp/roaring64map.hh +++ b/cpp/roaring64map.hh @@ -493,6 +493,8 @@ class Roaring64Map { return iter->second.contains(lowBytes(x)); } + // TODO: implement `containsRange` + /** * Compute the intersection of the current bitmap and the provided bitmap, * writing the result in the current bitmap. The provided bitmap is not @@ -785,17 +787,11 @@ class Roaring64Map { // to avoid a clash with the Windows.h header under Windows return roarings.size() == ((uint64_t)(std::numeric_limits::max)()) + 1 - ? std::all_of( - roarings.cbegin(), roarings.cend(), - [](const std::pair - &roaring_map_entry) { - // roarings within map are saturated if cardinality - // is uint32_t max + 1 - return roaring_map_entry.second.cardinality() == - ((uint64_t)(std::numeric_limits< - uint32_t>::max)()) + - 1; - }) + ? std::all_of(roarings.cbegin(), roarings.cend(), + [](const std::pair + &roaring_map_entry) { + return roaring_map_entry.second.isFull(); + }) : false; } @@ -844,6 +840,8 @@ class Roaring64Map { }); } + // TODO: implement `rangeUint64Array` + /** * Return true if the two bitmaps contain the same elements. */ @@ -1722,8 +1720,8 @@ class Roaring64MapSetBitBiDirectionalIterator { typedef int64_t difference_type; typedef Roaring64MapSetBitBiDirectionalIterator type_of_iterator; - Roaring64MapSetBitBiDirectionalIterator(const Roaring64Map &parent, - bool exhausted = false) + explicit Roaring64MapSetBitBiDirectionalIterator(const Roaring64Map &parent, + bool exhausted = false) : p(&parent.roarings) { if (exhausted || parent.roarings.empty()) { map_iter = p->cend(); @@ -1790,7 +1788,11 @@ class Roaring64MapSetBitBiDirectionalIterator { return orig; } - bool move(const value_type &x) { + /** + * Move the iterator to the first value >= val. + * Return true if there is such a value. + */ + bool move_equalorlarger(const value_type &x) { map_iter = p->lower_bound(Roaring64Map::highBytes(x)); if (map_iter != p->cend()) { roaring_iterator_init(&map_iter->second.roaring, &i); @@ -1807,6 +1809,11 @@ class Roaring64MapSetBitBiDirectionalIterator { return false; } + /** DEPRECATED, use `move_equalorlarger`. */ + CROARING_DEPRECATED bool move(const value_type &x) { + return move_equalorlarger(x); + } + type_of_iterator &operator--() { // --i, must return dec.value if (map_iter == p->cend()) { --map_iter; diff --git a/include/roaring/roaring.h b/include/roaring/roaring.h index 295bc3cb3..4665abbfc 100644 --- a/include/roaring/roaring.h +++ b/include/roaring/roaring.h @@ -435,6 +435,14 @@ bool roaring_bitmap_contains(const roaring_bitmap_t *r, uint32_t val); bool roaring_bitmap_contains_range(const roaring_bitmap_t *r, uint64_t range_start, uint64_t range_end); +/** + * Check whether a range of values from range_start (included) + * to range_end (included) is present + */ +bool roaring_bitmap_contains_range_closed(const roaring_bitmap_t *r, + uint32_t range_start, + uint32_t range_end); + /** * Check if an items is present, using context from a previous insert or search * for speed optimization. @@ -850,6 +858,14 @@ void roaring_bitmap_lazy_xor_inplace(roaring_bitmap_t *r1, roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *r1, uint64_t range_start, uint64_t range_end); +/** + * Compute the negation of the bitmap in the interval [range_start, range_end]. + * The number of negated values is range_end - range_start + 1. + * Areas outside the range are passed through unchanged. + */ +roaring_bitmap_t *roaring_bitmap_flip_closed(const roaring_bitmap_t *x1, + uint32_t range_start, + uint32_t range_end); /** * compute (in place) the negation of the roaring bitmap within a specified * interval: [range_start, range_end). The number of negated values is @@ -859,6 +875,16 @@ roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *r1, void roaring_bitmap_flip_inplace(roaring_bitmap_t *r1, uint64_t range_start, uint64_t range_end); +/** + * compute (in place) the negation of the roaring bitmap within a specified + * interval: [range_start, range_end]. The number of negated values is + * range_end - range_start + 1. + * Areas outside the range are passed through unchanged. + */ +void roaring_bitmap_flip_inplace_closed(roaring_bitmap_t *r1, + uint32_t range_start, + uint32_t range_end); + /** * Selects the element at index 'rank' where the smallest element is at index 0. * If the size of the roaring bitmap is strictly greater than rank, then this diff --git a/src/roaring.c b/src/roaring.c index e3847bae9..ca238adb5 100644 --- a/src/roaring.c +++ b/src/roaring.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -2005,11 +2006,17 @@ static void inplace_fully_flip_container(roaring_array_t *x1_arr, uint16_t hb) { roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *x1, uint64_t range_start, uint64_t range_end) { - if (range_start >= range_end) { + if (range_start >= range_end || range_start > (uint64_t)UINT_MAX + 1) + return roaring_bitmap_copy(x1); + return roaring_bitmap_flip_closed(x1, (uint32_t)range_start, + (uint32_t)(range_end - 1)); +} + +roaring_bitmap_t *roaring_bitmap_flip_closed(const roaring_bitmap_t *x1, + uint32_t range_start, + uint32_t range_end) { + if (range_start > range_end) { return roaring_bitmap_copy(x1); - } - if (range_end >= UINT64_C(0x100000000)) { - range_end = UINT64_C(0x100000000); } roaring_bitmap_t *ans = roaring_bitmap_create(); @@ -2017,8 +2024,8 @@ roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *x1, uint16_t hb_start = (uint16_t)(range_start >> 16); const uint16_t lb_start = (uint16_t)range_start; // & 0xFFFF; - uint16_t hb_end = (uint16_t)((range_end - 1) >> 16); - const uint16_t lb_end = (uint16_t)(range_end - 1); // & 0xFFFF; + uint16_t hb_end = (uint16_t)(range_end >> 16); + const uint16_t lb_end = (uint16_t)range_end; // & 0xFFFF; ra_append_copies_until(&ans->high_low_container, &x1->high_low_container, hb_start, is_cow(x1)); @@ -2059,17 +2066,23 @@ roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *x1, void roaring_bitmap_flip_inplace(roaring_bitmap_t *x1, uint64_t range_start, uint64_t range_end) { - if (range_start >= range_end) { + if (range_start >= range_end || range_start > (uint64_t)UINT_MAX + 1) + return; + roaring_bitmap_flip_inplace_closed(x1, (uint32_t)range_start, + (uint32_t)(range_end - 1)); +} + +void roaring_bitmap_flip_inplace_closed(roaring_bitmap_t *x1, + uint32_t range_start, + uint32_t range_end) { + if (range_start > range_end) { return; // empty range } - if (range_end >= UINT64_C(0x100000000)) { - range_end = UINT64_C(0x100000000); - } uint16_t hb_start = (uint16_t)(range_start >> 16); const uint16_t lb_start = (uint16_t)range_start; - uint16_t hb_end = (uint16_t)((range_end - 1) >> 16); - const uint16_t lb_end = (uint16_t)(range_end - 1); + uint16_t hb_end = (uint16_t)(range_end >> 16); + const uint16_t lb_end = (uint16_t)range_end; if (hb_start == hb_end) { inplace_flip_container(&x1->high_low_container, hb_start, lb_start, @@ -2827,15 +2840,25 @@ bool roaring_bitmap_contains(const roaring_bitmap_t *r, uint32_t val) { */ bool roaring_bitmap_contains_range(const roaring_bitmap_t *r, uint64_t range_start, uint64_t range_end) { - if (range_end >= UINT64_C(0x100000000)) { - range_end = UINT64_C(0x100000000); - } - if (range_start >= range_end) + if (range_start >= range_end || range_start > (uint64_t)UINT_MAX + 1) + return true; + return roaring_bitmap_contains_range_closed(r, (uint32_t)range_start, + (uint32_t)(range_end - 1)); +} + +/** + * Check whether a range of values from range_start (included) to range_end + * (included) is present + */ +bool roaring_bitmap_contains_range_closed(const roaring_bitmap_t *r, + uint32_t range_start, + uint32_t range_end) { + if (range_start > range_end) return true; // empty range are always contained! - if (range_end - range_start == 1) + if (range_end == range_start) return roaring_bitmap_contains(r, (uint32_t)range_start); uint16_t hb_rs = (uint16_t)(range_start >> 16); - uint16_t hb_re = (uint16_t)((range_end - 1) >> 16); + uint16_t hb_re = (uint16_t)(range_end >> 16); const int32_t span = hb_re - hb_rs; const int32_t hlc_sz = ra_get_size(&r->high_low_container); if (hlc_sz < span + 1) { @@ -2847,7 +2870,7 @@ bool roaring_bitmap_contains_range(const roaring_bitmap_t *r, return false; } const uint32_t lb_rs = range_start & 0xFFFF; - const uint32_t lb_re = ((range_end - 1) & 0xFFFF) + 1; + const uint32_t lb_re = (range_end & 0xFFFF) + 1; uint8_t type; container_t *c = ra_get_container_at_index(&r->high_low_container, (uint16_t)is, &type); diff --git a/tests/cpp_example2.cpp b/tests/cpp_example2.cpp index 31b3b1959..fd49f70b7 100644 --- a/tests/cpp_example2.cpp +++ b/tests/cpp_example2.cpp @@ -102,7 +102,7 @@ int main() { const uint32_t manyvalues[] = {2, 3, 4, 7, 8}; Roaring rogue(5, manyvalues); Roaring::const_iterator j = rogue.begin(); - j.equalorlarger(4); // *j == 4 + j.move_equalorlarger(4); // *j == 4 return EXIT_SUCCESS; } diff --git a/tests/cpp_unit.cpp b/tests/cpp_unit.cpp index c6d466f29..03fc287a3 100644 --- a/tests/cpp_unit.cpp +++ b/tests/cpp_unit.cpp @@ -422,7 +422,7 @@ void test_example_cpp(bool copy_on_write) { const uint32_t manyvalues[] = {2, 3, 4, 7, 8}; Roaring rogue(5, manyvalues); Roaring::const_iterator j = rogue.begin(); - j.equalorlarger(4); + j.move_equalorlarger(4); assert_true(*j == 4); // test move constructor @@ -1295,11 +1295,11 @@ DEFINE_TEST(test_cpp_move_64) { } Roaring64Map::const_iterator i(roaring); - i.move(123ULL); + i.move_equalorlarger(123ULL); assert_true(*i == 123ULL); - i.move(0xAFFFFFFF8ULL); + i.move_equalorlarger(0xAFFFFFFF8ULL); assert_true(*i == 0xFFFFFFFFFULL); - assert_false(i.move(0xFFFFFFFFFFULL)); + assert_false(i.move_equalorlarger(0xFFFFFFFFFFULL)); } DEFINE_TEST(test_cpp_bidirectional_iterator_64) { @@ -2153,7 +2153,7 @@ DEFINE_TEST(test_cpp_copy_map_iterator_to_different_map) { Roaring64Map m2{10, 20, 30, 40}; auto it = m1.begin(); it = m2.begin(); - it.move(21); + it.move_equalorlarger(21); int n = 0; for (; it != m2.end(); ++it, ++n) { } diff --git a/tests/roaring_checked.hh b/tests/roaring_checked.hh index c09edd3e2..7d06cad4f 100644 --- a/tests/roaring_checked.hh +++ b/tests/roaring_checked.hh @@ -670,14 +670,14 @@ class Roaring { return ans; } - typedef roaring::RoaringSetBitForwardIterator const_iterator; + typedef roaring::RoaringSetBitBiDirectionalIterator const_iterator; const_iterator begin() const { - return roaring::RoaringSetBitForwardIterator(plain); + return roaring::RoaringSetBitBiDirectionalIterator(plain); } const_iterator &end() const { - static roaring::RoaringSetBitForwardIterator e(plain, true); + static roaring::RoaringSetBitBiDirectionalIterator e(plain, true); return e; } };