From 96835a5e39f802bf7073e9e081e5342c7e4b2978 Mon Sep 17 00:00:00 2001 From: Soerian Lieve Date: Tue, 9 Jan 2024 22:18:50 +0100 Subject: [PATCH 1/3] Add a roaring64 iterator and functions for it These mirror the 32-bit roaring iterator functions. --- include/roaring/containers/containers.h | 12 ++ include/roaring/roaring64.h | 87 +++++++++ microbenchmarks/bench.cpp | 18 ++ src/containers/containers.c | 89 ++++++++++ src/roaring64.c | 182 ++++++++++++++++++- tests/roaring64_unit.cpp | 223 ++++++++++++++++++++++++ 6 files changed, 606 insertions(+), 5 deletions(-) diff --git a/include/roaring/containers/containers.h b/include/roaring/containers/containers.h index da2c2412b..75fb1a93c 100644 --- a/include/roaring/containers/containers.h +++ b/include/roaring/containers/containers.h @@ -2574,6 +2574,18 @@ bool container_iterator_read_into_uint32(const container_t *c, uint8_t typecode, uint32_t count, uint32_t *consumed, uint16_t *value_out); +/** + * Reads up to `count` entries from the container, and writes them into `buf` + * as `high48 | entry`. Returns true and sets `value_out` if a value is present + * after reading the entries. Sets `consumed` to the number of values read. + * `count` should be greater than zero. + */ +bool container_iterator_read_into_uint64(const container_t *c, uint8_t typecode, + roaring_container_iterator_t *it, + uint64_t high48, uint64_t *buf, + uint64_t count, uint32_t *consumed, + uint16_t *value_out); + #ifdef __cplusplus } } } // extern "C" { namespace roaring { namespace internal { #endif diff --git a/include/roaring/roaring64.h b/include/roaring/roaring64.h index 2f491a470..af7203781 100644 --- a/include/roaring/roaring64.h +++ b/include/roaring/roaring64.h @@ -15,6 +15,7 @@ namespace api { typedef struct roaring64_bitmap_s roaring64_bitmap_t; typedef struct roaring64_leaf_s roaring64_leaf_t; +typedef struct roaring64_iterator_s roaring64_iterator_t; /** * A bit of context usable with `roaring64_bitmap_*_bulk()` functions. @@ -380,6 +381,92 @@ void roaring64_bitmap_andnot_inplace(roaring64_bitmap_t *r1, bool roaring64_bitmap_iterate(const roaring64_bitmap_t *r, roaring_iterator64 iterator, void *ptr); +/** + * Create an iterator object that can be used to iterate through the values. + * Caller is responsible for calling `roaring64_free_iterator()`. + * + * The iterator is initialized. If there is a value, then this iterator points + * to the first value and `roaring64_iterator_has_value()` returns true. The + * value can be retrieved with `roaring64_iterator_value()`. + */ +roaring64_iterator_t *roaring64_create_iterator(const roaring64_bitmap_t *r); + +/** + * Create an iterator object that can be used to iterate through the values. + * Caller is responsible for calling `roaring64_free_iterator()`. + * + * The iterator is initialized. If there is a value, then this iterator points + * to the last value and `roaring64_iterator_has_value()` returns true. The + * value can be retrieved with `roaring64_iterator_value()`. + */ +roaring64_iterator_t *roaring64_create_iterator_last( + const roaring64_bitmap_t *r); + +/** + * Creates a copy of the iterator. Caller is responsible for calling + * `roaring64_free_iterator()` on the resulting iterator. + */ +roaring64_iterator_t *roaring64_copy_iterator(const roaring64_iterator_t *it); + +/** + * Free the iterator. + */ +void roaring64_free_iterator(roaring64_iterator_t *it); + +/** + * Returns true if the iterator currently points to a value. If so, calling + * `roaring64_iterator_value()` returns the value. + */ +bool roaring64_iterator_has_value(const roaring64_iterator_t *it); + +/** + * Returns the value the iterator currently points to. Should only be called if + * `roaring64_iterator_has_value()` returns true. + */ +uint64_t roaring64_iterator_value(const roaring64_iterator_t *it); + +/** + * Advance the iterator. If there is a new value, then + * `roaring64_iterator_has_value()` returns true. Values are traversed in + * increasing order. For convenience, returns the result of + * `roaring64_iterator_has_value()`. + * + * Once this returns false, `roaring64_advance_iterator` should not be called on + * the iterator again. Calling `roaring64_previous_iterator` is allowed. + */ +bool roaring64_advance_iterator(roaring64_iterator_t *it); + +/** + * Decrement the iterator. If there is a new value, then + * `roaring64_iterator_has_value()` returns true. Values are traversed in + * decreasing order. For convenience, returns the result of + * `roaring64_iterator_has_value()`. + * + * Once this returns false, `roaring64_previous_iterator` should not be called + * on the iterator again. Calling `roaring64_advance_iterator` is allowed. + */ +bool roaring64_previous_iterator(roaring64_iterator_t *it); + +/** + * Move the iterator to the first value greater than or equal to `val`, if it + * exists at or after the current position of the iterator. If there is a new + * value, then `roaring64_iterator_has_value()` returns true. Values are + * traversed in increasing order. For convenience, returns the result of + * `roaring64_iterator_has_value()`. + */ +bool roaring64_move_iterator_equalorlarger(roaring64_iterator_t *it, + uint64_t val); + +/** + * Reads up to `count` values from the iterator into the given `buf`. Returns + * the number of elements read. The number of elements read can be smaller than + * `count`, which means that there are no more elements in the bitmap. + * + * This function can be used together with other iterator functions. + */ +uint64_t roaring64_read_iterator(roaring64_iterator_t *it, uint64_t *buf, + uint64_t count); + #ifdef __cplusplus } // extern "C" } // namespace roaring diff --git a/microbenchmarks/bench.cpp b/microbenchmarks/bench.cpp index 31f6657ff..8a134bcce 100644 --- a/microbenchmarks/bench.cpp +++ b/microbenchmarks/bench.cpp @@ -242,6 +242,24 @@ struct iterate_all { auto IterateAll = BasicBench; BENCHMARK(IterateAll); +struct iterate_all64 { + static uint64_t run() { + uint64_t marker = 0; + for (size_t i = 0; i < count; ++i) { + roaring64_bitmap_t *r = bitmaps64[i]; + roaring64_iterator_t *it = roaring64_create_iterator(r); + while (roaring64_iterator_has_value(it)) { + marker++; + roaring64_advance_iterator(it); + } + roaring64_free_iterator(it); + } + return marker; + } +}; +auto IterateAll64 = BasicBench; +BENCHMARK(IterateAll64); + struct compute_cardinality { static uint64_t run() { uint64_t marker = 0; diff --git a/src/containers/containers.c b/src/containers/containers.c index 2dc3b74d1..9259b8229 100644 --- a/src/containers/containers.c +++ b/src/containers/containers.c @@ -622,6 +622,95 @@ bool container_iterator_read_into_uint32(const container_t *c, uint8_t typecode, } } +bool container_iterator_read_into_uint64(const container_t *c, uint8_t typecode, + roaring_container_iterator_t *it, + uint64_t high48, uint64_t *buf, + uint64_t count, uint32_t *consumed, + uint16_t *value_out) { + *consumed = 0; + if (count == 0) { + return false; + } + switch (typecode) { + case BITSET_CONTAINER_TYPE: { + const bitset_container_t *bc = const_CAST_bitset(c); + uint32_t wordindex = it->index / 64; + uint64_t word = + bc->words[wordindex] & (UINT64_MAX << (it->index % 64)); + do { + // Read set bits. + while (word != 0 && *consumed < count) { + *buf = high48 | + (wordindex * 64 + roaring_trailing_zeroes(word)); + word = word & (word - 1); + buf++; + (*consumed)++; + } + // Skip unset bits. + while (word == 0 && + wordindex + 1 < BITSET_CONTAINER_SIZE_IN_WORDS) { + wordindex++; + word = bc->words[wordindex]; + } + } while (word != 0 && *consumed < count); + + if (word != 0) { + it->index = wordindex * 64 + roaring_trailing_zeroes(word); + *value_out = it->index; + return true; + } + return false; + } + case ARRAY_CONTAINER_TYPE: { + const array_container_t *ac = const_CAST_array(c); + uint32_t num_values = + minimum_uint32(ac->cardinality - it->index, count); + for (uint32_t i = 0; i < num_values; i++) { + buf[i] = high48 | ac->array[it->index + i]; + } + *consumed += num_values; + it->index += num_values; + if (it->index < ac->cardinality) { + *value_out = ac->array[it->index]; + return true; + } + return false; + } + case RUN_CONTAINER_TYPE: { + const run_container_t *rc = const_CAST_run(c); + do { + uint32_t largest_run_value = + rc->runs[it->index].value + rc->runs[it->index].length; + uint32_t num_values = minimum_uint32( + largest_run_value - *value_out + 1, count - *consumed); + for (uint32_t i = 0; i < num_values; i++) { + buf[i] = high48 | (*value_out + i); + } + *value_out += num_values; + buf += num_values; + *consumed += num_values; + + // We check for `value == 0` because `it->value += num_values` + // can overflow when `value == UINT16_MAX`, and `count > + // length`. In this case `value` will overflow to 0. + if (*value_out > largest_run_value || *value_out == 0) { + it->index++; + if (it->index < rc->n_runs) { + *value_out = rc->runs[it->index].value; + } else { + return false; + } + } + } while (*consumed < count); + return true; + } + default: + assert(false); + roaring_unreachable; + return 0; + } +} + #ifdef __cplusplus } } } // extern "C" { namespace roaring { namespace internal { #endif diff --git a/src/roaring64.c b/src/roaring64.c index 04b48ab91..a58cca5e2 100644 --- a/src/roaring64.c +++ b/src/roaring64.c @@ -1,8 +1,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -15,10 +15,7 @@ namespace roaring { namespace api { #endif -// TODO: Iteration. -// * Need to create a container iterator which can be used across 32 and 64 bit -// bitmaps. -// * Iteration-based functions like roaring64_bitmap_intersect_with_range. +// TODO: Iteration-based functions like roaring64_bitmap_intersect_with_range. // TODO: Copy on write. // TODO: Serialization. // TODO: Error on failed allocation. @@ -39,6 +36,17 @@ typedef struct roaring64_leaf_s { // anyway. typedef struct roaring64_leaf_s leaf_t; +// Iterator struct to hold iteration state. +typedef struct roaring64_iterator_s { + const roaring64_bitmap_t *parent; + art_iterator_t art_it; + roaring_container_iterator_t container_it; + uint64_t high48; // Key that art_it points to. + + uint64_t value; + bool has_value; +} roaring64_iterator_t; + // Splits the given uint64 key into high 48 bit and low 16 bit components. // Expects high48_out to be of length ART_KEY_BYTES. static inline uint16_t split_key(uint64_t key, uint8_t high48_out[]) { @@ -1331,6 +1339,170 @@ bool roaring64_bitmap_iterate(const roaring64_bitmap_t *r, return true; } +static inline bool roaring64_iterator_init_at_leaf_first( + roaring64_iterator_t *it) { + it->high48 = combine_key(it->art_it.key, 0); + leaf_t *leaf = (leaf_t *)it->art_it.value; + uint16_t low16 = 0; + it->container_it = + container_init_iterator(leaf->container, leaf->typecode, &low16); + it->value = it->high48 | low16; + return (it->has_value = true); +} + +static inline bool roaring64_iterator_init_at_leaf_last( + roaring64_iterator_t *it) { + it->high48 = combine_key(it->art_it.key, 0); + leaf_t *leaf = (leaf_t *)it->art_it.value; + uint16_t low16 = 0; + it->container_it = + container_init_iterator_last(leaf->container, leaf->typecode, &low16); + it->value = it->high48 | low16; + return (it->has_value = true); +} + +static inline roaring64_iterator_t *roaring64_create_iterator_at( + const roaring64_bitmap_t *r, bool first) { + roaring64_iterator_t *it = + (roaring64_iterator_t *)roaring_malloc(sizeof(roaring64_iterator_t)); + it->parent = r; + it->art_it = art_init_iterator(&r->art, first); + it->has_value = it->art_it.value != NULL; + if (it->has_value) { + if (first) { + roaring64_iterator_init_at_leaf_first(it); + } else { + roaring64_iterator_init_at_leaf_last(it); + } + } + return it; +} + +roaring64_iterator_t *roaring64_create_iterator(const roaring64_bitmap_t *r) { + return roaring64_create_iterator_at(r, /*first=*/true); +} + +roaring64_iterator_t *roaring64_create_iterator_last( + const roaring64_bitmap_t *r) { + return roaring64_create_iterator_at(r, /*first=*/false); +} + +roaring64_iterator_t *roaring64_copy_iterator(const roaring64_iterator_t *it) { + roaring64_iterator_t *new_it = + (roaring64_iterator_t *)roaring_malloc(sizeof(roaring64_iterator_t)); + memcpy(new_it, it, sizeof(*it)); + return new_it; +} + +void roaring64_free_iterator(roaring64_iterator_t *it) { roaring_free(it); } + +bool roaring64_iterator_has_value(const roaring64_iterator_t *it) { + return it->has_value; +} + +uint64_t roaring64_iterator_value(const roaring64_iterator_t *it) { + return it->value; +} + +bool roaring64_advance_iterator(roaring64_iterator_t *it) { + if (it->art_it.value == NULL) { + return (it->has_value = false); + } + leaf_t *leaf = (leaf_t *)it->art_it.value; + uint16_t low16 = (uint16_t)it->value; + if (container_iterator_next(leaf->container, leaf->typecode, + &it->container_it, &low16)) { + it->value = it->high48 | low16; + return (it->has_value = true); + } + if (!art_iterator_next(&it->art_it)) { + return (it->has_value = false); + } + return roaring64_iterator_init_at_leaf_first(it); +} + +bool roaring64_previous_iterator(roaring64_iterator_t *it) { + if (it->art_it.value == NULL) { + return (it->has_value = false); + } + leaf_t *leaf = (leaf_t *)it->art_it.value; + uint16_t low16 = (uint16_t)it->value; + if (container_iterator_prev(leaf->container, leaf->typecode, + &it->container_it, &low16)) { + it->value = it->high48 | low16; + return (it->has_value = true); + } + if (!art_iterator_prev(&it->art_it)) { + return (it->has_value = false); + } + return roaring64_iterator_init_at_leaf_last(it); +} + +bool roaring64_move_iterator_equalorlarger(roaring64_iterator_t *it, + uint64_t val) { + if (it->art_it.value == NULL) { + return (it->has_value = false); + } + + uint8_t val_high48[ART_KEY_BYTES]; + uint16_t val_low16 = split_key(val, val_high48); + if (it->high48 < (val & 0xFFFFFFFFFFFF0000)) { + // The ART iterator is before the high48 bits of `val`, so we need to + // move to a leaf with a key equal or greater. + if (!art_iterator_lower_bound(&it->art_it, val_high48)) { + // Only smaller keys found. + return (it->has_value = false); + } + } + + if (it->high48 == (val & 0xFFFFFFFFFFFF0000)) { + // We're at equal high bits, check if a suitable value can be found in + // this container. + leaf_t *leaf = (leaf_t *)it->art_it.value; + uint16_t low16 = (uint16_t)it->value; + if (container_iterator_lower_bound(leaf->container, leaf->typecode, + &it->container_it, &low16, + val_low16)) { + it->value = it->high48 | low16; + return (it->has_value = true); + } + // Only smaller entries in this container, move to the next. + if (!art_iterator_next(&it->art_it)) { + return (it->has_value = false); + } + } + + // We're at a leaf with high bits greater than `val`, so the first entry in + // this container is our result. + return roaring64_iterator_init_at_leaf_first(it); +} + +uint64_t roaring64_read_iterator(roaring64_iterator_t *it, uint64_t *buf, + uint64_t count) { + uint64_t consumed = 0; + while (it->has_value && consumed < count) { + uint32_t container_consumed; + leaf_t *leaf = (leaf_t *)it->art_it.value; + uint16_t low16 = (uint16_t)it->value; + bool has_value = container_iterator_read_into_uint64( + leaf->container, leaf->typecode, &it->container_it, it->high48, buf, + count - consumed, &container_consumed, &low16); + consumed += container_consumed; + buf += container_consumed; + if (has_value) { + it->has_value = true; + it->value = it->high48 | low16; + assert(consumed == count); + return consumed; + } + it->has_value = art_iterator_next(&it->art_it); + if (it->has_value) { + roaring64_iterator_init_at_leaf_first(it); + } + } + return consumed; +} + #ifdef __cplusplus } // extern "C" } // namespace roaring diff --git a/tests/roaring64_unit.cpp b/tests/roaring64_unit.cpp index 9423ab252..08d8a18b5 100644 --- a/tests/roaring64_unit.cpp +++ b/tests/roaring64_unit.cpp @@ -875,6 +875,222 @@ DEFINE_TEST(test_iterate) { roaring64_bitmap_free(r); } +DEFINE_TEST(test_create_iterator) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + { + roaring64_iterator_t* it = roaring64_create_iterator(r); + assert_false(roaring64_iterator_has_value(it)); + roaring64_free_iterator(it); + } + roaring64_bitmap_add(r, 0); + { + roaring64_iterator_t* it = roaring64_create_iterator(r); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + roaring64_free_iterator(it); + } + roaring64_bitmap_add(r, (1ULL << 40) + 1234); + { + roaring64_iterator_t* it = roaring64_create_iterator(r); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + roaring64_free_iterator(it); + } + roaring64_bitmap_remove(r, 0); + { + roaring64_iterator_t* it = roaring64_create_iterator(r); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), ((1ULL << 40) + 1234)); + roaring64_free_iterator(it); + } + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_create_iterator_last) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + { + roaring64_iterator_t* it = roaring64_create_iterator_last(r); + assert_false(roaring64_iterator_has_value(it)); + roaring64_free_iterator(it); + } + roaring64_bitmap_add(r, 0); + { + roaring64_iterator_t* it = roaring64_create_iterator_last(r); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + roaring64_free_iterator(it); + } + roaring64_bitmap_add(r, (1ULL << 40) + 1234); + { + roaring64_iterator_t* it = roaring64_create_iterator_last(r); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), ((1ULL << 40) + 1234)); + roaring64_free_iterator(it); + } + roaring64_bitmap_remove(r, 0); + { + roaring64_iterator_t* it = roaring64_create_iterator_last(r); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), ((1ULL << 40) + 1234)); + roaring64_free_iterator(it); + } + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_copy_iterator) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + + roaring64_bitmap_add(r, 0); + roaring64_bitmap_add(r, 1ULL << 35); + roaring64_bitmap_add(r, (1ULL << 35) + 1); + roaring64_bitmap_add(r, (1ULL << 35) + 2); + roaring64_bitmap_add(r, (1ULL << 36)); + + roaring64_iterator_t* it1 = roaring64_create_iterator(r); + assert_true(roaring64_advance_iterator(it1)); + assert_true(roaring64_advance_iterator(it1)); + assert_true(roaring64_advance_iterator(it1)); + assert_true(roaring64_previous_iterator(it1)); + assert_true(roaring64_iterator_has_value(it1)); + assert_int_equal(roaring64_iterator_value(it1), ((1ULL << 35) + 1)); + + roaring64_iterator_t* it2 = roaring64_copy_iterator(it1); + assert_true(roaring64_iterator_has_value(it2)); + assert_int_equal(roaring64_iterator_value(it2), ((1ULL << 35) + 1)); + assert_true(roaring64_advance_iterator(it2)); + assert_true(roaring64_iterator_has_value(it2)); + assert_int_equal(roaring64_iterator_value(it2), ((1ULL << 35) + 2)); + + roaring64_free_iterator(it1); + roaring64_free_iterator(it2); + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_advance_iterator) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + std::vector values; + values.reserve(1000); + roaring64_bulk_context_t context{}; + for (uint64_t i = 0; i < 1000; ++i) { + uint64_t v = i * 10000; + values.push_back(v); + roaring64_bitmap_add_bulk(r, &context, v); + } + size_t i = 0; + roaring64_iterator_t* it = roaring64_create_iterator(r); + do { + assert_int_equal(roaring64_iterator_value(it), values[i]); + i++; + } while (roaring64_advance_iterator(it)); + assert_int_equal(i, values.size()); + roaring64_free_iterator(it); + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_previous_iterator) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + std::vector values; + values.reserve(1000); + roaring64_bulk_context_t context{}; + for (uint64_t i = 0; i < 1000; ++i) { + uint64_t v = i * 10000; + values.push_back(v); + roaring64_bitmap_add_bulk(r, &context, v); + } + size_t i = values.size(); + roaring64_iterator_t* it = roaring64_create_iterator_last(r); + do { + i--; + assert_int_equal(roaring64_iterator_value(it), values[i]); + } while (roaring64_previous_iterator(it)); + assert_int_equal(i, 0); + roaring64_free_iterator(it); + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_move_iterator_equalorlarger) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + + roaring64_bitmap_add(r, 0); + roaring64_bitmap_add(r, 1ULL << 35); + roaring64_bitmap_add(r, (1ULL << 35) + 1); + roaring64_bitmap_add(r, (1ULL << 35) + 2); + roaring64_bitmap_add(r, (1ULL << 36)); + + roaring64_iterator_t* it = roaring64_create_iterator(r); + assert_true(roaring64_move_iterator_equalorlarger(it, 0)); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + + assert_true(roaring64_move_iterator_equalorlarger(it, 0)); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + + assert_true(roaring64_move_iterator_equalorlarger(it, 1)); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), (1ULL << 35)); + + assert_true(roaring64_move_iterator_equalorlarger(it, (1ULL << 35) + 2)); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), ((1ULL << 35) + 2)); + + assert_true(roaring64_move_iterator_equalorlarger(it, (1ULL << 35) + 3)); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), (1ULL << 36)); + + assert_false(roaring64_move_iterator_equalorlarger(it, (1ULL << 36) + 1)); + assert_false(roaring64_iterator_has_value(it)); + + roaring64_free_iterator(it); + roaring64_bitmap_free(r); +} + +// Reads all elements from the iterator, `step` values at a time, and compares +// the elements with `values`. +void readCompare(const std::vector& values, + const roaring64_bitmap_t* r, uint64_t step) { + roaring64_iterator_t* it = roaring64_create_iterator(r); + std::vector buffer(values.size(), 0); + uint64_t read = 0; + while (read < values.size()) { + assert_true(roaring64_iterator_has_value(it)); + uint64_t step_read = roaring64_read_iterator(it, buffer.data(), step); + assert_int_equal(step_read, std::min(step, values.size() - read)); + for (size_t i = 0; i < step_read; ++i) { + assert_int_equal(values[read + i], buffer[i]); + } + read += step_read; + } + assert_false(roaring64_iterator_has_value(it)); + roaring64_free_iterator(it); +} + +DEFINE_TEST(test_read_iterator) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + std::vector values; + values.reserve(1000); + roaring64_bulk_context_t context{}; + for (uint64_t i = 0; i < 1000; ++i) { + uint64_t v = i * 10000; + values.push_back(v); + roaring64_bitmap_add_bulk(r, &context, v); + } + + // Check that a zero count results in zero elements read. + roaring64_iterator_t* it = roaring64_create_iterator(r); + uint64_t buf[1]; + assert_int_equal(roaring64_read_iterator(it, buf, 0), 0); + roaring64_free_iterator(it); + + readCompare(values, r, 1); + readCompare(values, r, 2); + readCompare(values, r, values.size() - 1); + readCompare(values, r, values.size()); + readCompare(values, r, values.size() + 1); + + roaring64_bitmap_free(r); +} + } // namespace int main() { @@ -920,6 +1136,13 @@ int main() { cmocka_unit_test(test_andnot_cardinality), cmocka_unit_test(test_andnot_inplace), cmocka_unit_test(test_iterate), + cmocka_unit_test(test_create_iterator), + cmocka_unit_test(test_create_iterator_last), + cmocka_unit_test(test_copy_iterator), + cmocka_unit_test(test_advance_iterator), + cmocka_unit_test(test_previous_iterator), + cmocka_unit_test(test_move_iterator_equalorlarger), + cmocka_unit_test(test_read_iterator), }; return cmocka_run_group_tests(tests, NULL, NULL); } From dfb10b74ef02abf6d65703cbecfaee3e777d0ccf Mon Sep 17 00:00:00 2001 From: Soerian Lieve Date: Tue, 16 Jan 2024 22:21:19 +0100 Subject: [PATCH 2/3] Rename roaring64_{verb}_iterator to roaring64_iterator_{verb} This is more consistent with the rest of the codebase. --- include/roaring/roaring64.h | 30 ++++----- microbenchmarks/bench.cpp | 6 +- src/roaring64.c | 22 +++---- tests/roaring64_unit.cpp | 118 ++++++++++++++++++------------------ 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/include/roaring/roaring64.h b/include/roaring/roaring64.h index af7203781..add5a1c64 100644 --- a/include/roaring/roaring64.h +++ b/include/roaring/roaring64.h @@ -383,35 +383,35 @@ bool roaring64_bitmap_iterate(const roaring64_bitmap_t *r, /** * Create an iterator object that can be used to iterate through the values. - * Caller is responsible for calling `roaring64_free_iterator()`. + * Caller is responsible for calling `roaring64_iterator_free()`. * * The iterator is initialized. If there is a value, then this iterator points * to the first value and `roaring64_iterator_has_value()` returns true. The * value can be retrieved with `roaring64_iterator_value()`. */ -roaring64_iterator_t *roaring64_create_iterator(const roaring64_bitmap_t *r); +roaring64_iterator_t *roaring64_iterator_create(const roaring64_bitmap_t *r); /** * Create an iterator object that can be used to iterate through the values. - * Caller is responsible for calling `roaring64_free_iterator()`. + * Caller is responsible for calling `roaring64_iterator_free()`. * * The iterator is initialized. If there is a value, then this iterator points * to the last value and `roaring64_iterator_has_value()` returns true. The * value can be retrieved with `roaring64_iterator_value()`. */ -roaring64_iterator_t *roaring64_create_iterator_last( +roaring64_iterator_t *roaring64_iterator_create_last( const roaring64_bitmap_t *r); /** * Creates a copy of the iterator. Caller is responsible for calling - * `roaring64_free_iterator()` on the resulting iterator. + * `roaring64_iterator_free()` on the resulting iterator. */ -roaring64_iterator_t *roaring64_copy_iterator(const roaring64_iterator_t *it); +roaring64_iterator_t *roaring64_iterator_copy(const roaring64_iterator_t *it); /** * Free the iterator. */ -void roaring64_free_iterator(roaring64_iterator_t *it); +void roaring64_iterator_free(roaring64_iterator_t *it); /** * Returns true if the iterator currently points to a value. If so, calling @@ -431,10 +431,10 @@ uint64_t roaring64_iterator_value(const roaring64_iterator_t *it); * increasing order. For convenience, returns the result of * `roaring64_iterator_has_value()`. * - * Once this returns false, `roaring64_advance_iterator` should not be called on - * the iterator again. Calling `roaring64_previous_iterator` is allowed. + * Once this returns false, `roaring64_iterator_advance` should not be called on + * the iterator again. Calling `roaring64_iterator_previous` is allowed. */ -bool roaring64_advance_iterator(roaring64_iterator_t *it); +bool roaring64_iterator_advance(roaring64_iterator_t *it); /** * Decrement the iterator. If there is a new value, then @@ -442,10 +442,10 @@ bool roaring64_advance_iterator(roaring64_iterator_t *it); * decreasing order. For convenience, returns the result of * `roaring64_iterator_has_value()`. * - * Once this returns false, `roaring64_previous_iterator` should not be called - * on the iterator again. Calling `roaring64_advance_iterator` is allowed. + * Once this returns false, `roaring64_iterator_previous` should not be called + * on the iterator again. Calling `roaring64_iterator_advance` is allowed. */ -bool roaring64_previous_iterator(roaring64_iterator_t *it); +bool roaring64_iterator_previous(roaring64_iterator_t *it); /** * Move the iterator to the first value greater than or equal to `val`, if it @@ -454,7 +454,7 @@ bool roaring64_previous_iterator(roaring64_iterator_t *it); * traversed in increasing order. For convenience, returns the result of * `roaring64_iterator_has_value()`. */ -bool roaring64_move_iterator_equalorlarger(roaring64_iterator_t *it, +bool roaring64_iterator_move_equalorlarger(roaring64_iterator_t *it, uint64_t val); /** @@ -464,7 +464,7 @@ bool roaring64_move_iterator_equalorlarger(roaring64_iterator_t *it, * * This function can be used together with other iterator functions. */ -uint64_t roaring64_read_iterator(roaring64_iterator_t *it, uint64_t *buf, +uint64_t roaring64_iterator_read(roaring64_iterator_t *it, uint64_t *buf, uint64_t count); #ifdef __cplusplus diff --git a/microbenchmarks/bench.cpp b/microbenchmarks/bench.cpp index 8a134bcce..2967bab3c 100644 --- a/microbenchmarks/bench.cpp +++ b/microbenchmarks/bench.cpp @@ -247,12 +247,12 @@ struct iterate_all64 { uint64_t marker = 0; for (size_t i = 0; i < count; ++i) { roaring64_bitmap_t *r = bitmaps64[i]; - roaring64_iterator_t *it = roaring64_create_iterator(r); + roaring64_iterator_t *it = roaring64_iterator_create(r); while (roaring64_iterator_has_value(it)) { marker++; - roaring64_advance_iterator(it); + roaring64_iterator_advance(it); } - roaring64_free_iterator(it); + roaring64_iterator_free(it); } return marker; } diff --git a/src/roaring64.c b/src/roaring64.c index a58cca5e2..5e1932153 100644 --- a/src/roaring64.c +++ b/src/roaring64.c @@ -1361,7 +1361,7 @@ static inline bool roaring64_iterator_init_at_leaf_last( return (it->has_value = true); } -static inline roaring64_iterator_t *roaring64_create_iterator_at( +static inline roaring64_iterator_t *roaring64_iterator_create_at( const roaring64_bitmap_t *r, bool first) { roaring64_iterator_t *it = (roaring64_iterator_t *)roaring_malloc(sizeof(roaring64_iterator_t)); @@ -1378,23 +1378,23 @@ static inline roaring64_iterator_t *roaring64_create_iterator_at( return it; } -roaring64_iterator_t *roaring64_create_iterator(const roaring64_bitmap_t *r) { - return roaring64_create_iterator_at(r, /*first=*/true); +roaring64_iterator_t *roaring64_iterator_create(const roaring64_bitmap_t *r) { + return roaring64_iterator_create_at(r, /*first=*/true); } -roaring64_iterator_t *roaring64_create_iterator_last( +roaring64_iterator_t *roaring64_iterator_create_last( const roaring64_bitmap_t *r) { - return roaring64_create_iterator_at(r, /*first=*/false); + return roaring64_iterator_create_at(r, /*first=*/false); } -roaring64_iterator_t *roaring64_copy_iterator(const roaring64_iterator_t *it) { +roaring64_iterator_t *roaring64_iterator_copy(const roaring64_iterator_t *it) { roaring64_iterator_t *new_it = (roaring64_iterator_t *)roaring_malloc(sizeof(roaring64_iterator_t)); memcpy(new_it, it, sizeof(*it)); return new_it; } -void roaring64_free_iterator(roaring64_iterator_t *it) { roaring_free(it); } +void roaring64_iterator_free(roaring64_iterator_t *it) { roaring_free(it); } bool roaring64_iterator_has_value(const roaring64_iterator_t *it) { return it->has_value; @@ -1404,7 +1404,7 @@ uint64_t roaring64_iterator_value(const roaring64_iterator_t *it) { return it->value; } -bool roaring64_advance_iterator(roaring64_iterator_t *it) { +bool roaring64_iterator_advance(roaring64_iterator_t *it) { if (it->art_it.value == NULL) { return (it->has_value = false); } @@ -1421,7 +1421,7 @@ bool roaring64_advance_iterator(roaring64_iterator_t *it) { return roaring64_iterator_init_at_leaf_first(it); } -bool roaring64_previous_iterator(roaring64_iterator_t *it) { +bool roaring64_iterator_previous(roaring64_iterator_t *it) { if (it->art_it.value == NULL) { return (it->has_value = false); } @@ -1438,7 +1438,7 @@ bool roaring64_previous_iterator(roaring64_iterator_t *it) { return roaring64_iterator_init_at_leaf_last(it); } -bool roaring64_move_iterator_equalorlarger(roaring64_iterator_t *it, +bool roaring64_iterator_move_equalorlarger(roaring64_iterator_t *it, uint64_t val) { if (it->art_it.value == NULL) { return (it->has_value = false); @@ -1477,7 +1477,7 @@ bool roaring64_move_iterator_equalorlarger(roaring64_iterator_t *it, return roaring64_iterator_init_at_leaf_first(it); } -uint64_t roaring64_read_iterator(roaring64_iterator_t *it, uint64_t *buf, +uint64_t roaring64_iterator_read(roaring64_iterator_t *it, uint64_t *buf, uint64_t count) { uint64_t consumed = 0; while (it->has_value && consumed < count) { diff --git a/tests/roaring64_unit.cpp b/tests/roaring64_unit.cpp index 08d8a18b5..61999cfcd 100644 --- a/tests/roaring64_unit.cpp +++ b/tests/roaring64_unit.cpp @@ -875,69 +875,69 @@ DEFINE_TEST(test_iterate) { roaring64_bitmap_free(r); } -DEFINE_TEST(test_create_iterator) { +DEFINE_TEST(test_iterator_create) { roaring64_bitmap_t* r = roaring64_bitmap_create(); { - roaring64_iterator_t* it = roaring64_create_iterator(r); + roaring64_iterator_t* it = roaring64_iterator_create(r); assert_false(roaring64_iterator_has_value(it)); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } roaring64_bitmap_add(r, 0); { - roaring64_iterator_t* it = roaring64_create_iterator(r); + roaring64_iterator_t* it = roaring64_iterator_create(r); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), 0); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } roaring64_bitmap_add(r, (1ULL << 40) + 1234); { - roaring64_iterator_t* it = roaring64_create_iterator(r); + roaring64_iterator_t* it = roaring64_iterator_create(r); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), 0); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } roaring64_bitmap_remove(r, 0); { - roaring64_iterator_t* it = roaring64_create_iterator(r); + roaring64_iterator_t* it = roaring64_iterator_create(r); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), ((1ULL << 40) + 1234)); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } roaring64_bitmap_free(r); } -DEFINE_TEST(test_create_iterator_last) { +DEFINE_TEST(test_iterator_create_last) { roaring64_bitmap_t* r = roaring64_bitmap_create(); { - roaring64_iterator_t* it = roaring64_create_iterator_last(r); + roaring64_iterator_t* it = roaring64_iterator_create_last(r); assert_false(roaring64_iterator_has_value(it)); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } roaring64_bitmap_add(r, 0); { - roaring64_iterator_t* it = roaring64_create_iterator_last(r); + roaring64_iterator_t* it = roaring64_iterator_create_last(r); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), 0); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } roaring64_bitmap_add(r, (1ULL << 40) + 1234); { - roaring64_iterator_t* it = roaring64_create_iterator_last(r); + roaring64_iterator_t* it = roaring64_iterator_create_last(r); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), ((1ULL << 40) + 1234)); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } roaring64_bitmap_remove(r, 0); { - roaring64_iterator_t* it = roaring64_create_iterator_last(r); + roaring64_iterator_t* it = roaring64_iterator_create_last(r); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), ((1ULL << 40) + 1234)); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } roaring64_bitmap_free(r); } -DEFINE_TEST(test_copy_iterator) { +DEFINE_TEST(test_iterator_copy) { roaring64_bitmap_t* r = roaring64_bitmap_create(); roaring64_bitmap_add(r, 0); @@ -946,27 +946,27 @@ DEFINE_TEST(test_copy_iterator) { roaring64_bitmap_add(r, (1ULL << 35) + 2); roaring64_bitmap_add(r, (1ULL << 36)); - roaring64_iterator_t* it1 = roaring64_create_iterator(r); - assert_true(roaring64_advance_iterator(it1)); - assert_true(roaring64_advance_iterator(it1)); - assert_true(roaring64_advance_iterator(it1)); - assert_true(roaring64_previous_iterator(it1)); + roaring64_iterator_t* it1 = roaring64_iterator_create(r); + assert_true(roaring64_iterator_advance(it1)); + assert_true(roaring64_iterator_advance(it1)); + assert_true(roaring64_iterator_advance(it1)); + assert_true(roaring64_iterator_previous(it1)); assert_true(roaring64_iterator_has_value(it1)); assert_int_equal(roaring64_iterator_value(it1), ((1ULL << 35) + 1)); - roaring64_iterator_t* it2 = roaring64_copy_iterator(it1); + roaring64_iterator_t* it2 = roaring64_iterator_copy(it1); assert_true(roaring64_iterator_has_value(it2)); assert_int_equal(roaring64_iterator_value(it2), ((1ULL << 35) + 1)); - assert_true(roaring64_advance_iterator(it2)); + assert_true(roaring64_iterator_advance(it2)); assert_true(roaring64_iterator_has_value(it2)); assert_int_equal(roaring64_iterator_value(it2), ((1ULL << 35) + 2)); - roaring64_free_iterator(it1); - roaring64_free_iterator(it2); + roaring64_iterator_free(it1); + roaring64_iterator_free(it2); roaring64_bitmap_free(r); } -DEFINE_TEST(test_advance_iterator) { +DEFINE_TEST(test_iterator_advance) { roaring64_bitmap_t* r = roaring64_bitmap_create(); std::vector values; values.reserve(1000); @@ -977,17 +977,17 @@ DEFINE_TEST(test_advance_iterator) { roaring64_bitmap_add_bulk(r, &context, v); } size_t i = 0; - roaring64_iterator_t* it = roaring64_create_iterator(r); + roaring64_iterator_t* it = roaring64_iterator_create(r); do { assert_int_equal(roaring64_iterator_value(it), values[i]); i++; - } while (roaring64_advance_iterator(it)); + } while (roaring64_iterator_advance(it)); assert_int_equal(i, values.size()); - roaring64_free_iterator(it); + roaring64_iterator_free(it); roaring64_bitmap_free(r); } -DEFINE_TEST(test_previous_iterator) { +DEFINE_TEST(test_iterator_previous) { roaring64_bitmap_t* r = roaring64_bitmap_create(); std::vector values; values.reserve(1000); @@ -998,17 +998,17 @@ DEFINE_TEST(test_previous_iterator) { roaring64_bitmap_add_bulk(r, &context, v); } size_t i = values.size(); - roaring64_iterator_t* it = roaring64_create_iterator_last(r); + roaring64_iterator_t* it = roaring64_iterator_create_last(r); do { i--; assert_int_equal(roaring64_iterator_value(it), values[i]); - } while (roaring64_previous_iterator(it)); + } while (roaring64_iterator_previous(it)); assert_int_equal(i, 0); - roaring64_free_iterator(it); + roaring64_iterator_free(it); roaring64_bitmap_free(r); } -DEFINE_TEST(test_move_iterator_equalorlarger) { +DEFINE_TEST(test_iterator_move_equalorlarger) { roaring64_bitmap_t* r = roaring64_bitmap_create(); roaring64_bitmap_add(r, 0); @@ -1017,31 +1017,31 @@ DEFINE_TEST(test_move_iterator_equalorlarger) { roaring64_bitmap_add(r, (1ULL << 35) + 2); roaring64_bitmap_add(r, (1ULL << 36)); - roaring64_iterator_t* it = roaring64_create_iterator(r); - assert_true(roaring64_move_iterator_equalorlarger(it, 0)); + roaring64_iterator_t* it = roaring64_iterator_create(r); + assert_true(roaring64_iterator_move_equalorlarger(it, 0)); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), 0); - assert_true(roaring64_move_iterator_equalorlarger(it, 0)); + assert_true(roaring64_iterator_move_equalorlarger(it, 0)); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), 0); - assert_true(roaring64_move_iterator_equalorlarger(it, 1)); + assert_true(roaring64_iterator_move_equalorlarger(it, 1)); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), (1ULL << 35)); - assert_true(roaring64_move_iterator_equalorlarger(it, (1ULL << 35) + 2)); + assert_true(roaring64_iterator_move_equalorlarger(it, (1ULL << 35) + 2)); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), ((1ULL << 35) + 2)); - assert_true(roaring64_move_iterator_equalorlarger(it, (1ULL << 35) + 3)); + assert_true(roaring64_iterator_move_equalorlarger(it, (1ULL << 35) + 3)); assert_true(roaring64_iterator_has_value(it)); assert_int_equal(roaring64_iterator_value(it), (1ULL << 36)); - assert_false(roaring64_move_iterator_equalorlarger(it, (1ULL << 36) + 1)); + assert_false(roaring64_iterator_move_equalorlarger(it, (1ULL << 36) + 1)); assert_false(roaring64_iterator_has_value(it)); - roaring64_free_iterator(it); + roaring64_iterator_free(it); roaring64_bitmap_free(r); } @@ -1049,12 +1049,12 @@ DEFINE_TEST(test_move_iterator_equalorlarger) { // the elements with `values`. void readCompare(const std::vector& values, const roaring64_bitmap_t* r, uint64_t step) { - roaring64_iterator_t* it = roaring64_create_iterator(r); + roaring64_iterator_t* it = roaring64_iterator_create(r); std::vector buffer(values.size(), 0); uint64_t read = 0; while (read < values.size()) { assert_true(roaring64_iterator_has_value(it)); - uint64_t step_read = roaring64_read_iterator(it, buffer.data(), step); + uint64_t step_read = roaring64_iterator_read(it, buffer.data(), step); assert_int_equal(step_read, std::min(step, values.size() - read)); for (size_t i = 0; i < step_read; ++i) { assert_int_equal(values[read + i], buffer[i]); @@ -1062,10 +1062,10 @@ void readCompare(const std::vector& values, read += step_read; } assert_false(roaring64_iterator_has_value(it)); - roaring64_free_iterator(it); + roaring64_iterator_free(it); } -DEFINE_TEST(test_read_iterator) { +DEFINE_TEST(test_iterator_read) { roaring64_bitmap_t* r = roaring64_bitmap_create(); std::vector values; values.reserve(1000); @@ -1077,10 +1077,10 @@ DEFINE_TEST(test_read_iterator) { } // Check that a zero count results in zero elements read. - roaring64_iterator_t* it = roaring64_create_iterator(r); + roaring64_iterator_t* it = roaring64_iterator_create(r); uint64_t buf[1]; - assert_int_equal(roaring64_read_iterator(it, buf, 0), 0); - roaring64_free_iterator(it); + assert_int_equal(roaring64_iterator_read(it, buf, 0), 0); + roaring64_iterator_free(it); readCompare(values, r, 1); readCompare(values, r, 2); @@ -1136,13 +1136,13 @@ int main() { cmocka_unit_test(test_andnot_cardinality), cmocka_unit_test(test_andnot_inplace), cmocka_unit_test(test_iterate), - cmocka_unit_test(test_create_iterator), - cmocka_unit_test(test_create_iterator_last), - cmocka_unit_test(test_copy_iterator), - cmocka_unit_test(test_advance_iterator), - cmocka_unit_test(test_previous_iterator), - cmocka_unit_test(test_move_iterator_equalorlarger), - cmocka_unit_test(test_read_iterator), + cmocka_unit_test(test_iterator_create), + cmocka_unit_test(test_iterator_create_last), + cmocka_unit_test(test_iterator_copy), + cmocka_unit_test(test_iterator_advance), + cmocka_unit_test(test_iterator_previous), + cmocka_unit_test(test_iterator_move_equalorlarger), + cmocka_unit_test(test_iterator_read), }; return cmocka_run_group_tests(tests, NULL, NULL); } From f18a415c3a83dc8bc2e9ede08a63cbba2c281de3 Mon Sep 17 00:00:00 2001 From: Soerian Lieve Date: Tue, 16 Jan 2024 22:37:58 +0100 Subject: [PATCH 3/3] Add roaring64_iterator_{reinit, reinit_last} These allow reusing an existing iterator. --- include/roaring/roaring64.h | 14 ++++++++++ src/roaring64.c | 24 ++++++++++++----- tests/roaring64_unit.cpp | 52 +++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/include/roaring/roaring64.h b/include/roaring/roaring64.h index add5a1c64..ce9216fc6 100644 --- a/include/roaring/roaring64.h +++ b/include/roaring/roaring64.h @@ -402,6 +402,20 @@ roaring64_iterator_t *roaring64_iterator_create(const roaring64_bitmap_t *r); roaring64_iterator_t *roaring64_iterator_create_last( const roaring64_bitmap_t *r); +/** + * Re-initializes an existing iterator. Functionally the same as + * `roaring64_iterator_create` without a allocation. + */ +void roaring64_iterator_reinit(const roaring64_bitmap_t *r, + roaring64_iterator_t *it); + +/** + * Re-initializes an existing iterator. Functionally the same as + * `roaring64_iterator_create_last` without a allocation. + */ +void roaring64_iterator_reinit_last(const roaring64_bitmap_t *r, + roaring64_iterator_t *it); + /** * Creates a copy of the iterator. Caller is responsible for calling * `roaring64_iterator_free()` on the resulting iterator. diff --git a/src/roaring64.c b/src/roaring64.c index 5e1932153..8069eeb14 100644 --- a/src/roaring64.c +++ b/src/roaring64.c @@ -1361,10 +1361,8 @@ static inline bool roaring64_iterator_init_at_leaf_last( return (it->has_value = true); } -static inline roaring64_iterator_t *roaring64_iterator_create_at( - const roaring64_bitmap_t *r, bool first) { - roaring64_iterator_t *it = - (roaring64_iterator_t *)roaring_malloc(sizeof(roaring64_iterator_t)); +static inline roaring64_iterator_t *roaring64_iterator_init_at( + const roaring64_bitmap_t *r, roaring64_iterator_t *it, bool first) { it->parent = r; it->art_it = art_init_iterator(&r->art, first); it->has_value = it->art_it.value != NULL; @@ -1379,12 +1377,26 @@ static inline roaring64_iterator_t *roaring64_iterator_create_at( } roaring64_iterator_t *roaring64_iterator_create(const roaring64_bitmap_t *r) { - return roaring64_iterator_create_at(r, /*first=*/true); + roaring64_iterator_t *it = + (roaring64_iterator_t *)roaring_malloc(sizeof(roaring64_iterator_t)); + return roaring64_iterator_init_at(r, it, /*first=*/true); } roaring64_iterator_t *roaring64_iterator_create_last( const roaring64_bitmap_t *r) { - return roaring64_iterator_create_at(r, /*first=*/false); + roaring64_iterator_t *it = + (roaring64_iterator_t *)roaring_malloc(sizeof(roaring64_iterator_t)); + return roaring64_iterator_init_at(r, it, /*first=*/false); +} + +void roaring64_iterator_reinit(const roaring64_bitmap_t *r, + roaring64_iterator_t *it) { + roaring64_iterator_init_at(r, it, /*first=*/true); +} + +void roaring64_iterator_reinit_last(const roaring64_bitmap_t *r, + roaring64_iterator_t *it) { + roaring64_iterator_init_at(r, it, /*first=*/false); } roaring64_iterator_t *roaring64_iterator_copy(const roaring64_iterator_t *it) { diff --git a/tests/roaring64_unit.cpp b/tests/roaring64_unit.cpp index 61999cfcd..d9577d75d 100644 --- a/tests/roaring64_unit.cpp +++ b/tests/roaring64_unit.cpp @@ -937,6 +937,56 @@ DEFINE_TEST(test_iterator_create_last) { roaring64_bitmap_free(r); } +DEFINE_TEST(test_iterator_reinit) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + + roaring64_bitmap_add(r, 0); + roaring64_bitmap_add(r, 1ULL << 35); + roaring64_bitmap_add(r, (1ULL << 35) + 1); + roaring64_bitmap_add(r, (1ULL << 35) + 2); + roaring64_bitmap_add(r, (1ULL << 36)); + + roaring64_iterator_t* it = roaring64_iterator_create(r); + assert_true(roaring64_iterator_advance(it)); + assert_true(roaring64_iterator_advance(it)); + assert_true(roaring64_iterator_advance(it)); + assert_true(roaring64_iterator_previous(it)); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), ((1ULL << 35) + 1)); + + roaring64_iterator_reinit(r, it); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + + roaring64_iterator_free(it); + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_iterator_reinit_last) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + + roaring64_bitmap_add(r, 0); + roaring64_bitmap_add(r, 1ULL << 35); + roaring64_bitmap_add(r, (1ULL << 35) + 1); + roaring64_bitmap_add(r, (1ULL << 35) + 2); + roaring64_bitmap_add(r, (1ULL << 36)); + + roaring64_iterator_t* it = roaring64_iterator_create(r); + assert_true(roaring64_iterator_advance(it)); + assert_true(roaring64_iterator_advance(it)); + assert_true(roaring64_iterator_advance(it)); + assert_true(roaring64_iterator_previous(it)); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), ((1ULL << 35) + 1)); + + roaring64_iterator_reinit_last(r, it); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), (1ULL << 36)); + + roaring64_iterator_free(it); + roaring64_bitmap_free(r); +} + DEFINE_TEST(test_iterator_copy) { roaring64_bitmap_t* r = roaring64_bitmap_create(); @@ -1138,6 +1188,8 @@ int main() { cmocka_unit_test(test_iterate), cmocka_unit_test(test_iterator_create), cmocka_unit_test(test_iterator_create_last), + cmocka_unit_test(test_iterator_reinit), + cmocka_unit_test(test_iterator_reinit_last), cmocka_unit_test(test_iterator_copy), cmocka_unit_test(test_iterator_advance), cmocka_unit_test(test_iterator_previous),