Skip to content

Commit

Permalink
Add roaring64_bitmap_move_from_roaring32 function (#649)
Browse files Browse the repository at this point in the history
* Add `roaring64_bitmap_steal_roaring32` function

This allows creating a 64 bitmap cheaply from a roaring_bitmap_t. Ideally, we
should add a `roaring64_bitmap_add_offset` too.

* rename to roaring64_bitmap_move_from_roaring32

* add unit test for roaring64_bitmap_move_from_roaring32
  • Loading branch information
Dr-Emann authored Aug 7, 2024
1 parent 0268464 commit 99ef8b2
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 16 deletions.
9 changes: 9 additions & 0 deletions include/roaring/roaring64.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef ROARING64_H
#define ROARING64_H

#include <roaring.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
Expand Down Expand Up @@ -92,6 +93,14 @@ roaring64_bitmap_t *roaring64_bitmap_of_ptr(size_t n_args,
&((const uint64_t[]){0, __VA_ARGS__})[1])
#endif

/**
* Create a new bitmap by moving containers from a 32 bit roaring bitmap.
*
* After calling this function, the original bitmap will be empty, and the
* returned bitmap will contain all the values from the original bitmap.
*/
roaring64_bitmap_t *roaring64_bitmap_move_from_roaring32(roaring_bitmap_t *r);

/**
* Create a new bitmap containing all the values in [min, max) that are at a
* distance k*step from min.
Expand Down
55 changes: 39 additions & 16 deletions src/roaring64.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,43 @@ roaring64_bitmap_t *roaring64_bitmap_copy(const roaring64_bitmap_t *r) {
return result;
}

/**
* Steal the containers from a 32-bit bitmap and insert them into a 64-bit
* bitmap (with an offset)
*
* After calling this function, the original bitmap will be empty, and the
* returned bitmap will contain all the values from the original bitmap.
*/
static void move_from_roaring32_offset(roaring64_bitmap_t *dst,
roaring_bitmap_t *src,
uint32_t high_bits) {
uint64_t key_base = ((uint64_t)high_bits) << 32;
uint32_t r32_size = ra_get_size(&src->high_low_container);
for (uint32_t i = 0; i < r32_size; ++i) {
uint16_t key = ra_get_key_at_index(&src->high_low_container, i);
uint8_t typecode;
container_t *container = ra_get_container_at_index(
&src->high_low_container, (uint16_t)i, &typecode);

uint8_t high48[ART_KEY_BYTES];
uint64_t high48_bits = key_base | ((uint64_t)key << 16);
split_key(high48_bits, high48);
leaf_t *leaf = create_leaf(container, typecode);
art_insert(&dst->art, high48, (art_val_t *)leaf);
}
// We stole all the containers, so leave behind a size of zero
src->high_low_container.size = 0;
}

roaring64_bitmap_t *roaring64_bitmap_move_from_roaring32(
roaring_bitmap_t *bitmap32) {
roaring64_bitmap_t *result = roaring64_bitmap_create();

move_from_roaring32_offset(result, bitmap32, 0);

return result;
}

roaring64_bitmap_t *roaring64_bitmap_from_range(uint64_t min, uint64_t max,
uint64_t step) {
if (step == 0 || max <= min) {
Expand Down Expand Up @@ -1947,22 +1984,8 @@ roaring64_bitmap_t *roaring64_bitmap_portable_deserialize_safe(
read_bytes += bitmap32_size;

// Insert all containers of the 32-bit bitmap into the 64-bit bitmap.
uint32_t r32_size = ra_get_size(&bitmap32->high_low_container);
for (size_t i = 0; i < r32_size; ++i) {
uint16_t key16 =
ra_get_key_at_index(&bitmap32->high_low_container, (uint16_t)i);
uint8_t typecode;
container_t *container = ra_get_container_at_index(
&bitmap32->high_low_container, (uint16_t)i, &typecode);

uint64_t high48_bits =
(((uint64_t)high32) << 32) | (((uint64_t)key16) << 16);
uint8_t high48[ART_KEY_BYTES];
split_key(high48_bits, high48);
leaf_t *leaf = create_leaf(container, typecode);
art_insert(&r->art, high48, (art_val_t *)leaf);
}
roaring_bitmap_free_without_containers(bitmap32);
move_from_roaring32_offset(r, bitmap32, high32);
roaring_bitmap_free(bitmap32);
}
return r;
}
Expand Down
40 changes: 40 additions & 0 deletions tests/roaring64_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ void assert_vector_equal(const std::vector<uint64_t>& lhs,
}
}

void assert_r32_valid(roaring_bitmap_t* b) {
const char* reason = nullptr;
if (!roaring_bitmap_internal_validate(b, &reason)) {
fail_msg("Roaring64 bitmap is invalid: '%s'\n", reason);
}
}

void assert_r64_valid(roaring64_bitmap_t* b) {
const char* reason = nullptr;
if (!roaring64_bitmap_internal_validate(b, &reason)) {
Expand Down Expand Up @@ -55,6 +62,38 @@ DEFINE_TEST(test_copy) {
roaring64_bitmap_free(r2);
}

DEFINE_TEST(test_move_from_roaring32) {
{
// Empty bitmap
roaring_bitmap_t* r32 = roaring_bitmap_create();
roaring64_bitmap_t* r = roaring64_bitmap_move_from_roaring32(r32);

assert_r32_valid(r32);
assert_true(roaring_bitmap_is_empty(r32));
assert_r64_valid(r);
assert_true(roaring64_bitmap_is_empty(r));

roaring64_bitmap_free(r);
roaring_bitmap_free(r32);
}
{
roaring_bitmap_t* r32 = roaring_bitmap_from(0, 100, UINT32_MAX);
roaring64_bitmap_t* r = roaring64_bitmap_move_from_roaring32(r32);

assert_r32_valid(r32);
assert_true(roaring_bitmap_is_empty(r32));
assert_r64_valid(r);
assert_int_equal(roaring64_bitmap_get_cardinality(r), 3);

assert_true(roaring64_bitmap_contains(r, 0));
assert_true(roaring64_bitmap_contains(r, 100));
assert_true(roaring64_bitmap_contains(r, UINT32_MAX));

roaring_bitmap_free(r32);
roaring64_bitmap_free(r);
}
}

DEFINE_TEST(test_from_range) {
{
// Step greater than 2 ^ 16.
Expand Down Expand Up @@ -1842,6 +1881,7 @@ int main() {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_copy),
cmocka_unit_test(test_from_range),
cmocka_unit_test(test_move_from_roaring32),
cmocka_unit_test(test_of_ptr),
cmocka_unit_test(test_of),
cmocka_unit_test(test_add),
Expand Down

0 comments on commit 99ef8b2

Please sign in to comment.