From 413230b38f52bddc41b15e1b993877b2537063e6 Mon Sep 17 00:00:00 2001 From: Zachary Dremann Date: Wed, 24 Jan 2024 16:36:09 -0500 Subject: [PATCH] Replace `roaring{,64}_bitmap_of` with `roaring{,64}_bitmap_from` macro (#570) Using a macro allows us to count the number of items passed, and avoid having to explicitly pass the count as the first parameter. --- README.md | 2 +- include/roaring/roaring.h | 50 ++++++++++++++++++++++++++++++++++--- include/roaring/roaring64.h | 42 ++++++++++++++++++++++++++++--- tests/c_example1.c | 2 +- tests/cpp_unit.cpp | 2 +- tests/roaring64_unit.cpp | 24 +++++++++--------- tests/threads_unit.cpp | 2 +- tests/toplevel_unit.c | 26 +++++++++---------- 8 files changed, 114 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index fcd25a2d9..b767d833b 100644 --- a/README.md +++ b/README.md @@ -331,7 +331,7 @@ int main() { expectedsizebasic, expectedsizerun); // create a new bitmap containing the values {1,2,3,5,6} - roaring_bitmap_t *r2 = roaring_bitmap_of(5, 1, 2, 3, 5, 6); + roaring_bitmap_t *r2 = roaring_bitmap_from(1, 2, 3, 5, 6); roaring_bitmap_printf(r2); // print it // we can also create a bitmap from a pointer to 32-bit integers diff --git a/include/roaring/roaring.h b/include/roaring/roaring.h index c283a0e7a..d609bdf3a 100644 --- a/include/roaring/roaring.h +++ b/include/roaring/roaring.h @@ -9,6 +9,7 @@ #include #include // for `size_t` +#include #include #include #include @@ -94,8 +95,51 @@ void roaring_bitmap_printf_describe(const roaring_bitmap_t *r); /** * Creates a new bitmap from a list of uint32_t integers + * + * This function is deprecated, use `roaring_bitmap_from` instead, which + * doesn't require the number of elements to be passed in. + * + * @see roaring_bitmap_from */ -roaring_bitmap_t *roaring_bitmap_of(size_t n, ...); +CROARING_DEPRECATED roaring_bitmap_t *roaring_bitmap_of(size_t n, ...); + +#ifdef __cplusplus +/** + * Creates a new bitmap which contains all values passed in as arguments. + * + * To create a bitmap from a variable number of arguments, use the + * `roaring_bitmap_of_ptr` function instead. + */ +// Use an immediately invoked closure, capturing by reference +// (in case __VA_ARGS__ refers to context outside the closure) +// Include a 0 at the beginning of the array to make the array length > 0 +// (zero sized arrays are not valid in standard c/c++) +#define roaring_bitmap_from(...) \ + [&]() { \ + const uint32_t roaring_bitmap_from_array[] = {0, __VA_ARGS__}; \ + return roaring_bitmap_of_ptr((sizeof(roaring_bitmap_from_array) / \ + sizeof(roaring_bitmap_from_array[0])) - \ + 1, \ + &roaring_bitmap_from_array[1]); \ + }() +#else +/** + * Creates a new bitmap which contains all values passed in as arguments. + * + * To create a bitmap from a variable number of arguments, use the + * `roaring_bitmap_of_ptr` function instead. + */ +// While __VA_ARGS__ occurs twice in expansion, one of the times is in a sizeof +// expression, which is an unevaluated context, so it's even safe in the case +// where expressions passed have side effects (roaring64_bitmap_from(my_func(), +// ++i)) +// Include a 0 at the beginning of the array to make the array length > 0 +// (zero sized arrays are not valid in standard c/c++) +#define roaring_bitmap_from(...) \ + roaring_bitmap_of_ptr( \ + (sizeof((const uint32_t[]){0, __VA_ARGS__}) / sizeof(uint32_t)) - 1, \ + &((const uint32_t[]){0, __VA_ARGS__})[1]) +#endif /** * Copies a bitmap (this does memory allocation). @@ -446,7 +490,7 @@ void roaring_bitmap_to_uint32_array(const roaring_bitmap_t *r, uint32_t *ans); * * bitset_t * out = bitset_create(); * // if the bitset has content in it, call "bitset_clear(out)" - * bool success = roaring_bitmap_to_bitset(mybitmap, out); + * bool success = roaring_bitmap_to_bitset(mybitmap, out); * // on failure, success will be false. * // You can then query the bitset: * bool is_present = bitset_get(out, 10011 ); @@ -524,7 +568,7 @@ roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf); * * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), * the data format is going to be big-endian and not compatible with little-endian systems. - * + * * The difference with `roaring_bitmap_deserialize()` is that this function checks that the input buffer * is a valid bitmap. If the buffer is too small, NULL is returned. */ diff --git a/include/roaring/roaring64.h b/include/roaring/roaring64.h index c77a4fbf2..8e082d7bb 100644 --- a/include/roaring/roaring64.h +++ b/include/roaring/roaring64.h @@ -2,6 +2,7 @@ #define ROARING64_H #include +#include #include #include #include @@ -51,10 +52,44 @@ roaring64_bitmap_t *roaring64_bitmap_copy(const roaring64_bitmap_t *r); roaring64_bitmap_t *roaring64_bitmap_of_ptr(size_t n_args, const uint64_t *vals); +#ifdef __cplusplus /** - * Creates a new bitmap of a pointer to N 64-bit integers. - */ -roaring64_bitmap_t *roaring64_bitmap_of(size_t n_args, ...); + * Creates a new bitmap which contains all values passed in as arguments. + * + * To create a bitmap from a variable number of arguments, use the + * `roaring64_bitmap_of_ptr` function instead. + */ +// Use an immediately invoked closure, capturing by reference +// (in case __VA_ARGS__ refers to context outside the closure) +// Include a 0 at the beginning of the array to make the array length > 0 +// (zero sized arrays are not valid in standard c/c++) +#define roaring64_bitmap_from(...) \ + [&]() { \ + const uint64_t roaring64_bitmap_from_array[] = {0, __VA_ARGS__}; \ + return roaring64_bitmap_of_ptr( \ + (sizeof(roaring64_bitmap_from_array) / \ + sizeof(roaring64_bitmap_from_array[0])) - \ + 1, \ + &roaring64_bitmap_from_array[1]); \ + }() +#else +/** + * Creates a new bitmap which contains all values passed in as arguments. + * + * To create a bitmap from a variable number of arguments, use the + * `roaring64_bitmap_of_ptr` function instead. + */ +// While __VA_ARGS__ occurs twice in expansion, one of the times is in a sizeof +// expression, which is an unevaluated context, so it's even safe in the case +// where expressions passed have side effects (roaring64_bitmap_from(my_func(), +// ++i)) +// Include a 0 at the beginning of the array to make the array length > 0 +// (zero sized arrays are not valid in standard c/c++) +#define roaring64_bitmap_from(...) \ + roaring64_bitmap_of_ptr( \ + (sizeof((const uint64_t[]){0, __VA_ARGS__}) / sizeof(uint64_t)) - 1, \ + &((const uint64_t[]){0, __VA_ARGS__})[1]) +#endif /** * Create a new bitmap containing all the values in [min, max) that are at a @@ -543,4 +578,3 @@ uint64_t roaring64_iterator_read(roaring64_iterator_t *it, uint64_t *buf, #endif #endif /* ROARING64_H */ - diff --git a/tests/c_example1.c b/tests/c_example1.c index 7e1dff23b..fc4d36daa 100644 --- a/tests/c_example1.c +++ b/tests/c_example1.c @@ -30,7 +30,7 @@ int main() { printf("size before run optimize %d bytes, and after %d bytes\n", expectedsizebasic, expectedsizerun); // create a new bitmap containing the values {1,2,3,5,6} - roaring_bitmap_t *r2 = roaring_bitmap_of(5, 1, 2, 3, 5, 6); + roaring_bitmap_t *r2 = roaring_bitmap_from(1, 2, 3, 5, 6); roaring_bitmap_printf(r2); // print it // we can also create a bitmap from a pointer to 32-bit integers diff --git a/tests/cpp_unit.cpp b/tests/cpp_unit.cpp index 94f100f78..14bcb05c3 100644 --- a/tests/cpp_unit.cpp +++ b/tests/cpp_unit.cpp @@ -101,7 +101,7 @@ void test_example(bool copy_on_write) { printf("size before run optimize %zu bytes, and after %zu bytes\n", size, compact_size); // create a new bitmap with varargs - roaring_bitmap_t *r2 = roaring_bitmap_of(5, 1, 2, 3, 5, 6); + roaring_bitmap_t *r2 = roaring_bitmap_from(1, 2, 3, 5, 6); assert_ptr_not_equal(r2, NULL); roaring_bitmap_printf(r2); printf("\n"); diff --git a/tests/roaring64_unit.cpp b/tests/roaring64_unit.cpp index 4a47ef8df..a31d19745 100644 --- a/tests/roaring64_unit.cpp +++ b/tests/roaring64_unit.cpp @@ -93,7 +93,7 @@ DEFINE_TEST(test_of_ptr) { } DEFINE_TEST(test_of) { - roaring64_bitmap_t* r = roaring64_bitmap_of(3, 1ULL, 20000ULL, 500000ULL); + roaring64_bitmap_t* r = roaring64_bitmap_from(1, 20000, 500000); assert_true(roaring64_bitmap_contains(r, 1)); assert_true(roaring64_bitmap_contains(r, 20000)); assert_true(roaring64_bitmap_contains(r, 500000)); @@ -1038,9 +1038,9 @@ DEFINE_TEST(test_flip) { } { // Only the specified range should be flipped. - roaring64_bitmap_t* r1 = roaring64_bitmap_of(3, 1ULL, 3ULL, 6ULL); + roaring64_bitmap_t* r1 = roaring64_bitmap_from(1, 3, 6); roaring64_bitmap_t* r2 = roaring64_bitmap_flip(r1, 2, 5); - roaring64_bitmap_t* r3 = roaring64_bitmap_of(4, 1ULL, 2ULL, 4ULL, 6ULL); + roaring64_bitmap_t* r3 = roaring64_bitmap_from(1, 2, 4, 6); assert_true(roaring64_bitmap_equals(r2, r3)); roaring64_bitmap_free(r1); @@ -1049,7 +1049,7 @@ DEFINE_TEST(test_flip) { } { // An empty range does nothing. - roaring64_bitmap_t* r1 = roaring64_bitmap_of(3, 1ULL, 3ULL, 6); + roaring64_bitmap_t* r1 = roaring64_bitmap_from(1, 3, 6); roaring64_bitmap_t* r2 = roaring64_bitmap_flip(r1, 3, 3); assert_true(roaring64_bitmap_equals(r2, r1)); @@ -1058,8 +1058,8 @@ DEFINE_TEST(test_flip) { } { // A bitmap with values in all affected containers. - roaring64_bitmap_t* r1 = roaring64_bitmap_of( - 3, (2ULL << 16), (3ULL << 16) + 1, (4ULL << 16) + 3); + roaring64_bitmap_t* r1 = roaring64_bitmap_from( + (2 << 16), (3 << 16) + 1, (4 << 16) + 3); roaring64_bitmap_t* r2 = roaring64_bitmap_flip(r1, (2 << 16), (4 << 16) + 4); roaring64_bitmap_t* r3 = @@ -1084,9 +1084,9 @@ DEFINE_TEST(test_flip_inplace) { } { // Only the specified range should be flipped. - roaring64_bitmap_t* r1 = roaring64_bitmap_of(3, 1ULL, 3ULL, 6ULL); + roaring64_bitmap_t* r1 = roaring64_bitmap_from(1, 3, 6); roaring64_bitmap_flip_inplace(r1, 2, 5); - roaring64_bitmap_t* r2 = roaring64_bitmap_of(4, 1ULL, 2ULL, 4ULL, 6ULL); + roaring64_bitmap_t* r2 = roaring64_bitmap_from(1, 2, 4, 6); assert_true(roaring64_bitmap_equals(r1, r2)); roaring64_bitmap_free(r1); @@ -1094,9 +1094,9 @@ DEFINE_TEST(test_flip_inplace) { } { // An empty range does nothing. - roaring64_bitmap_t* r1 = roaring64_bitmap_of(3, 1ULL, 3ULL, 6ULL); + roaring64_bitmap_t* r1 = roaring64_bitmap_from(1, 3, 6); roaring64_bitmap_flip_inplace(r1, 3, 3); - roaring64_bitmap_t* r2 = roaring64_bitmap_of(3, 1ULL, 3ULL, 6ULL); + roaring64_bitmap_t* r2 = roaring64_bitmap_from(1, 3, 6); assert_true(roaring64_bitmap_equals(r1, r2)); roaring64_bitmap_free(r1); @@ -1104,8 +1104,8 @@ DEFINE_TEST(test_flip_inplace) { } { // A bitmap with values in all affected containers. - roaring64_bitmap_t* r1 = roaring64_bitmap_of( - 3, (2ULL << 16), (3ULL << 16) + 1, (4ULL << 16) + 3); + roaring64_bitmap_t* r1 = roaring64_bitmap_from( + (2 << 16), (3 << 16) + 1, (4 << 16) + 3); roaring64_bitmap_flip_inplace(r1, (2 << 16), (4 << 16) + 4); roaring64_bitmap_t* r2 = roaring64_bitmap_from_range((2 << 16) + 1, (4 << 16) + 3, 1); diff --git a/tests/threads_unit.cpp b/tests/threads_unit.cpp index 2d913889c..3774fbb9a 100644 --- a/tests/threads_unit.cpp +++ b/tests/threads_unit.cpp @@ -39,7 +39,7 @@ bool run_threads_unit_tests() { roaring_bitmap_set_copy_on_write(r1, true); roaring_bitmap_run_optimize(r1); - roaring_bitmap_t *r2 = roaring_bitmap_of(5, 10010,10020,10030,10040,10050); + roaring_bitmap_t *r2 = roaring_bitmap_from(10010,10020,10030,10040,10050); roaring_bitmap_set_copy_on_write(r2, true); roaring_bitmap_t *r3 = roaring_bitmap_copy(r1); roaring_bitmap_set_copy_on_write(r3, true); diff --git a/tests/toplevel_unit.c b/tests/toplevel_unit.c index e67161606..2cfcbe352 100644 --- a/tests/toplevel_unit.c +++ b/tests/toplevel_unit.c @@ -575,7 +575,7 @@ DEFINE_TEST(leaks_with_empty_false) { leaks_with_empty(false); } DEFINE_TEST(check_interval) { // create a new bitmap with varargs - roaring_bitmap_t *r = roaring_bitmap_of(4, 1, 2, 3, 1000); + roaring_bitmap_t *r = roaring_bitmap_from(1, 2, 3, 1000); assert_non_null(r); roaring_bitmap_printf(r); @@ -728,7 +728,7 @@ void test_example(bool copy_on_write) { compact_size); // create a new bitmap with varargs - roaring_bitmap_t *r2 = roaring_bitmap_of(5, 1, 2, 3, 5, 6); + roaring_bitmap_t *r2 = roaring_bitmap_from(1, 2, 3, 5, 6); assert_bitmap_validate(r2); assert_non_null(r2); @@ -1186,7 +1186,7 @@ DEFINE_TEST(test_bitmap_from_range) { DEFINE_TEST(test_printf) { roaring_bitmap_t *r1 = - roaring_bitmap_of(8, 1, 2, 3, 100, 1000, 10000, 1000000, 20000000); + roaring_bitmap_from(1, 2, 3, 100, 1000, 10000, 1000000, 20000000); assert_bitmap_validate(r1); assert_non_null(r1); roaring_bitmap_printf(r1); @@ -1230,7 +1230,7 @@ bool dummy_iterator(uint32_t value, void *param) { DEFINE_TEST(test_iterate) { roaring_bitmap_t *r1 = - roaring_bitmap_of(8, 1, 2, 3, 100, 1000, 10000, 1000000, 20000000); + roaring_bitmap_from(1, 2, 3, 100, 1000, 10000, 1000000, 20000000); assert_non_null(r1); uint32_t num = 0; @@ -1302,7 +1302,7 @@ DEFINE_TEST(test_remove_withrun) { DEFINE_TEST(test_portable_serialize) { roaring_bitmap_t *r1 = - roaring_bitmap_of(8, 1, 2, 3, 100, 1000, 10000, 1000000, 20000000); + roaring_bitmap_from(1, 2, 3, 100, 1000, 10000, 1000000, 20000000); assert_non_null(r1); uint32_t serialize_len; @@ -1335,8 +1335,8 @@ DEFINE_TEST(test_portable_serialize) { roaring_bitmap_free(r1); roaring_bitmap_free(r2); - r1 = roaring_bitmap_of(6, 2946000, 2997491, 10478289, 10490227, 10502444, - 19866827); + r1 = roaring_bitmap_from(2946000, 2997491, 10478289, 10490227, 10502444, + 19866827); expectedsize = roaring_bitmap_portable_size_in_bytes(r1); serialized = (char *)malloc(expectedsize); serialize_len = roaring_bitmap_portable_serialize(r1, serialized); @@ -1397,7 +1397,7 @@ DEFINE_TEST(test_portable_serialize) { DEFINE_TEST(test_serialize) { roaring_bitmap_t *r1 = - roaring_bitmap_of(8, 1, 2, 3, 100, 1000, 10000, 1000000, 20000000); + roaring_bitmap_from(1, 2, 3, 100, 1000, 10000, 1000000, 20000000); assert_non_null(r1); uint32_t serialize_len; @@ -1466,8 +1466,8 @@ DEFINE_TEST(test_serialize) { roaring_bitmap_free(r1); roaring_bitmap_free(r2); - r1 = roaring_bitmap_of(6, 2946000, 2997491, 10478289, 10490227, 10502444, - 19866827); + r1 = roaring_bitmap_from(2946000, 2997491, 10478289, 10490227, 10502444, + 19866827); serialized = (char *)malloc(roaring_bitmap_size_in_bytes(r1)); serialize_len = roaring_bitmap_serialize(r1, serialized); @@ -4531,7 +4531,7 @@ DEFINE_TEST(test_frozen_serialization_max_containers) { DEFINE_TEST(test_portable_deserialize_frozen) { roaring_bitmap_t *r1 = - roaring_bitmap_of(8, 1, 2, 3, 100, 1000, 10000, 1000000, 20000000); + roaring_bitmap_from(1, 2, 3, 100, 1000, 10000, 1000000, 20000000); assert_non_null(r1); uint32_t serialize_len; @@ -4563,8 +4563,8 @@ DEFINE_TEST(test_portable_deserialize_frozen) { roaring_bitmap_free(r1); roaring_bitmap_free(r2); - r1 = roaring_bitmap_of(6, 2946000, 2997491, 10478289, 10490227, 10502444, - 19866827); + r1 = roaring_bitmap_from(2946000, 2997491, 10478289, 10490227, 10502444, + 19866827); expectedsize = roaring_bitmap_portable_size_in_bytes(r1); serialized = (char *)malloc(expectedsize); serialize_len = roaring_bitmap_portable_serialize(r1, serialized);