Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add roaring_bitmap_deserialize_safe #486

Merged
merged 3 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/roaring/roaring.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,20 @@ size_t roaring_bitmap_serialize(const roaring_bitmap_t *r, char *buf);
*/
roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf);

/**
* Use with `roaring_bitmap_serialize()`.
*
* (See `roaring_bitmap_portable_deserialize_safe()` if you want a format that's
* compatible with Java and Go implementations).
*
* 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.
*/
roaring_bitmap_t *roaring_bitmap_deserialize_safe(const void *buf, size_t maxbytes);

/**
* How many bytes are required to serialize this bitmap (NOT compatible
* with Java and Go versions)
Expand Down
46 changes: 46 additions & 0 deletions src/roaring.c
Original file line number Diff line number Diff line change
Expand Up @@ -1463,9 +1463,12 @@ roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf) {
if (bufaschar[0] == CROARING_SERIALIZATION_ARRAY_UINT32) {
/* This looks like a compressed set of uint32_t elements */
uint32_t card;

memcpy(&card, bufaschar + 1, sizeof(uint32_t));

const uint32_t *elems =
(const uint32_t *)(bufaschar + 1 + sizeof(uint32_t));

roaring_bitmap_t *bitmap = roaring_bitmap_create();
if (bitmap == NULL) {
return NULL;
Expand All @@ -1478,12 +1481,55 @@ roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf) {
roaring_bitmap_add_bulk(bitmap, &context, elem);
}
return bitmap;

} else if (bufaschar[0] == CROARING_SERIALIZATION_CONTAINER) {
return roaring_bitmap_portable_deserialize(bufaschar + 1);
} else
return (NULL);
}

roaring_bitmap_t* roaring_bitmap_deserialize_safe(const void *buf, size_t maxbytes) {
if (maxbytes < 1) {
return NULL;
}

const char *bufaschar = (const char *)buf;
if (bufaschar[0] == CROARING_SERIALIZATION_ARRAY_UINT32) {
if (maxbytes < 1 + sizeof(uint32_t)) {
return NULL;
}

/* This looks like a compressed set of uint32_t elements */
uint32_t card;
memcpy(&card, bufaschar + 1, sizeof(uint32_t));

// Check the buffer is big enough to contain card uint32_t elements
if (maxbytes < 1 + sizeof(uint32_t) + card * sizeof(uint32_t)) {
return NULL;
}

const uint32_t *elems =
(const uint32_t *)(bufaschar + 1 + sizeof(uint32_t));

roaring_bitmap_t *bitmap = roaring_bitmap_create();
if (bitmap == NULL) {
return NULL;
}
roaring_bulk_context_t context = {0};
for (uint32_t i = 0; i < card; i++) {
// elems may not be aligned, read with memcpy
uint32_t elem;
memcpy(&elem, elems + i, sizeof(elem));
roaring_bitmap_add_bulk(bitmap, &context, elem);
}
return bitmap;

} else if (bufaschar[0] == CROARING_SERIALIZATION_CONTAINER) {
return roaring_bitmap_portable_deserialize_safe(bufaschar + 1, maxbytes - 1);
} else
return (NULL);
}

bool roaring_iterate(const roaring_bitmap_t *r, roaring_iterator iterator,
void *ptr) {
const roaring_array_t *ra = &r->high_low_container;
Expand Down
26 changes: 26 additions & 0 deletions tests/toplevel_unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,23 @@ DEFINE_TEST(test_serialize) {
r2 = roaring_bitmap_deserialize(serialized);
assert_true(roaring_bitmap_equals(r1, r2));

// Check that roaring_bitmap_deserialize_safe fails on invalid length

assert_null(roaring_bitmap_deserialize_safe(serialized, 0));
assert_null(roaring_bitmap_deserialize_safe(serialized, serialize_len - 1));

// Check that roaring_bitmap_deserialize_safe succeed with valid length

roaring_bitmap_t *t_safe = roaring_bitmap_deserialize_safe(serialized, serialize_len);
assert_true(roaring_bitmap_equals(r1, t_safe));
roaring_bitmap_free(t_safe);

// Check that roaring_bitmap_deserialize_safe succeed with larger length

t_safe = roaring_bitmap_deserialize_safe(serialized, serialize_len + 10);
assert_true(roaring_bitmap_equals(r1, t_safe));
roaring_bitmap_free(t_safe);

free(serialized);
roaring_bitmap_free(r1);
roaring_bitmap_free(r2);
Expand Down Expand Up @@ -1460,6 +1477,7 @@ DEFINE_TEST(test_serialize) {

assert_true(array_equals(arr1, card1, arr2, card2));
assert_true(roaring_bitmap_equals(r1, r2));

free(arr1);
free(arr2);
free(serialized);
Expand All @@ -1473,6 +1491,14 @@ DEFINE_TEST(test_serialize) {
uint32_t size = roaring_bitmap_serialize(old_bm, buff);
assert_int_equal(size, roaring_bitmap_size_in_bytes(old_bm));
roaring_bitmap_t *new_bm = roaring_bitmap_deserialize(buff);

// Check that roaring_bitmap_deserialize_safe fails on invalid length
assert_null(roaring_bitmap_deserialize_safe(buff, size - 1));
// Check that roaring_bitmap_deserialize_safe succeed with valid length
t_safe = roaring_bitmap_deserialize_safe(buff, size);
assert_true(roaring_bitmap_equals(new_bm, t_safe));
roaring_bitmap_free(t_safe);

free(buff);
assert_true((unsigned int)roaring_bitmap_get_cardinality(old_bm) ==
(unsigned int)roaring_bitmap_get_cardinality(new_bm));
Expand Down