diff --git a/include/roaring/roaring64.h b/include/roaring/roaring64.h index c71bccb91..fd89feb5e 100644 --- a/include/roaring/roaring64.h +++ b/include/roaring/roaring64.h @@ -1,6 +1,7 @@ #ifndef ROARING64_H #define ROARING64_H +#include #include #include #include @@ -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. diff --git a/src/roaring64.c b/src/roaring64.c index d41507b3c..e63d3d965 100644 --- a/src/roaring64.c +++ b/src/roaring64.c @@ -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) { @@ -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; } diff --git a/tests/roaring64_unit.cpp b/tests/roaring64_unit.cpp index f6129abc3..6d8f2c9a1 100644 --- a/tests/roaring64_unit.cpp +++ b/tests/roaring64_unit.cpp @@ -23,6 +23,13 @@ void assert_vector_equal(const std::vector& 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)) { @@ -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. @@ -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),