diff --git a/include/roaring/containers/containers.h b/include/roaring/containers/containers.h index ac2aba447..d81ff530d 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..ce9216fc6 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,106 @@ 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_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_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_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_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. + */ +roaring64_iterator_t *roaring64_iterator_copy(const roaring64_iterator_t *it); + +/** + * Free the iterator. + */ +void roaring64_iterator_free(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_iterator_advance` should not be called on + * the iterator again. Calling `roaring64_iterator_previous` is allowed. + */ +bool roaring64_iterator_advance(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_iterator_previous` should not be called + * on the iterator again. Calling `roaring64_iterator_advance` is allowed. + */ +bool roaring64_iterator_previous(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_iterator_move_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_iterator_read(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..2967bab3c 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_iterator_create(r); + while (roaring64_iterator_has_value(it)) { + marker++; + roaring64_iterator_advance(it); + } + roaring64_iterator_free(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 d4994efbb..53914653b 100644 --- a/src/roaring64.c +++ b/src/roaring64.c @@ -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[]) { @@ -1337,6 +1345,182 @@ 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_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; + 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_iterator_create(const roaring64_bitmap_t *r) { + 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) { + 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) { + 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_iterator_free(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_iterator_advance(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_iterator_previous(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_iterator_move_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_iterator_read(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 ddd305190..511084cba 100644 --- a/tests/roaring64_unit.cpp +++ b/tests/roaring64_unit.cpp @@ -950,6 +950,272 @@ DEFINE_TEST(test_iterate) { roaring64_bitmap_free(r); } +DEFINE_TEST(test_iterator_create) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + { + roaring64_iterator_t* it = roaring64_iterator_create(r); + assert_false(roaring64_iterator_has_value(it)); + roaring64_iterator_free(it); + } + roaring64_bitmap_add(r, 0); + { + roaring64_iterator_t* it = roaring64_iterator_create(r); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + roaring64_iterator_free(it); + } + roaring64_bitmap_add(r, (1ULL << 40) + 1234); + { + roaring64_iterator_t* it = roaring64_iterator_create(r); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + roaring64_iterator_free(it); + } + roaring64_bitmap_remove(r, 0); + { + 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_iterator_free(it); + } + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_iterator_create_last) { + roaring64_bitmap_t* r = roaring64_bitmap_create(); + { + roaring64_iterator_t* it = roaring64_iterator_create_last(r); + assert_false(roaring64_iterator_has_value(it)); + roaring64_iterator_free(it); + } + roaring64_bitmap_add(r, 0); + { + 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_iterator_free(it); + } + roaring64_bitmap_add(r, (1ULL << 40) + 1234); + { + 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_iterator_free(it); + } + roaring64_bitmap_remove(r, 0); + { + 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_iterator_free(it); + } + 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(); + + 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_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_iterator_copy(it1); + assert_true(roaring64_iterator_has_value(it2)); + assert_int_equal(roaring64_iterator_value(it2), ((1ULL << 35) + 1)); + assert_true(roaring64_iterator_advance(it2)); + assert_true(roaring64_iterator_has_value(it2)); + assert_int_equal(roaring64_iterator_value(it2), ((1ULL << 35) + 2)); + + roaring64_iterator_free(it1); + roaring64_iterator_free(it2); + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_iterator_advance) { + 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_iterator_create(r); + do { + assert_int_equal(roaring64_iterator_value(it), values[i]); + i++; + } while (roaring64_iterator_advance(it)); + assert_int_equal(i, values.size()); + roaring64_iterator_free(it); + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_iterator_previous) { + 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_iterator_create_last(r); + do { + i--; + assert_int_equal(roaring64_iterator_value(it), values[i]); + } while (roaring64_iterator_previous(it)); + assert_int_equal(i, 0); + roaring64_iterator_free(it); + roaring64_bitmap_free(r); +} + +DEFINE_TEST(test_iterator_move_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_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_iterator_move_equalorlarger(it, 0)); + assert_true(roaring64_iterator_has_value(it)); + assert_int_equal(roaring64_iterator_value(it), 0); + + 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_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_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_iterator_move_equalorlarger(it, (1ULL << 36) + 1)); + assert_false(roaring64_iterator_has_value(it)); + + roaring64_iterator_free(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_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_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]); + } + read += step_read; + } + assert_false(roaring64_iterator_has_value(it)); + roaring64_iterator_free(it); +} + +DEFINE_TEST(test_iterator_read) { + 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_iterator_create(r); + uint64_t buf[1]; + assert_int_equal(roaring64_iterator_read(it, buf, 0), 0); + roaring64_iterator_free(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() { @@ -995,6 +1261,15 @@ int main() { cmocka_unit_test(test_andnot_cardinality), cmocka_unit_test(test_andnot_inplace), 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), + cmocka_unit_test(test_iterator_move_equalorlarger), + cmocka_unit_test(test_iterator_read), }; return cmocka_run_group_tests(tests, NULL, NULL); }