Repository: RoaringBitmap/CRoaring (C)

Issue:
incorrect Roaring constructed from a COW-Enabled Roaring
i tried to build a COW-enabled Roaring, and then construct other Roarings from it. But the new constructed Roaring seems incorrect.

the problem can be repoduced as following:
```
void test_ctor_from_cow_cpp() {
    Roaring r1;
    r1.setCopyOnWrite(true);
    r1.addRange(1, 100);

    Roaring r2;
    r2 |= r1;
    auto r3 = r2;
    assert_true(r2.isSubset(r3));
}
```
and yields the error msg:
```
CRoaring/include/roaring/containers/containers.h:725: container_is_subset: Assertion `false' failed.
```

i found the error can be walk around by set COW on r2 (`r2.setCopyOnWrite(true);`).


is this a bug or misuse ？

ps. [dest->codetypes/contianers may mismatch in ra_overwrite()](https://github.com/RoaringBitmap/CRoaring/blob/master/src/roaring_array.c#L144)


Discussion:
At a glance, this seems like a bug. I will investigate.
Fix upcoming.



Patch:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9f16dadb9..22886ac27 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -113,7 +113,7 @@ MESSAGE( STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER} ) # important to know w
 MESSAGE( STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS} ) # important to know the flags
 MESSAGE( STATUS "CMAKE_C_FLAGS_DEBUG: " ${CMAKE_C_FLAGS_DEBUG} )
 MESSAGE( STATUS "CMAKE_C_FLAGS_RELEASE: " ${CMAKE_C_FLAGS_RELEASE} )
-
+MESSAGE( STATUS "ROARING_EXCEPTIONS: " ${ROARING_EXCEPTIONS} )
 
 set(CPACK_PACKAGE_VENDOR "Daniel Lemire")
 set(CPACK_PACKAGE_CONTACT "lemire@gmail.com")
diff --git a/cpp/roaring.hh b/cpp/roaring.hh
index cf4d6d27e..d96310613 100644
--- a/cpp/roaring.hh
+++ b/cpp/roaring.hh
@@ -13,6 +13,9 @@ A C++ header for Roaring Bitmaps.
 
 #if !defined(ROARING_EXCEPTIONS)
 // Note that __cpp_exceptions is required by C++98 and we require C++11 and better.
+#ifndef __cpp_exceptions
+#error "__cpp_exceptions should be defined"
+#endif
 # if __cpp_exceptions
 #  define ROARING_EXCEPTIONS 1
 # else
@@ -63,7 +66,7 @@ class Roaring {
      */
     Roaring(const Roaring &r) : Roaring() {
         if (!api::roaring_bitmap_overwrite(&roaring, &r.roaring)) {
-            ROARING_TERMINATE("failed memory alloc in constructor");
+            ROARING_TERMINATE("failed roaring_bitmap_overwrite in constructor");
         }
         api::roaring_bitmap_set_copy_on_write(&roaring,
             api::roaring_bitmap_get_copy_on_write(&r.roaring));
diff --git a/src/containers/containers.c b/src/containers/containers.c
index 03909139d..bb2f3d6c4 100644
--- a/src/containers/containers.c
+++ b/src/containers/containers.c
@@ -165,7 +165,8 @@ container_t *get_copy_of_container(
  * is responsible for deallocation.
  */
 container_t *container_clone(const container_t *c, uint8_t typecode) {
-    c = container_unwrap_shared(c, &typecode);
+    // We do not want to allow cloning of shared containers.
+    // c = container_unwrap_shared(c, &typecode);
     switch (typecode) {
         case BITSET_CONTAINER_TYPE:
             return bitset_container_clone(const_CAST_bitset(c));
@@ -174,8 +175,7 @@ container_t *container_clone(const container_t *c, uint8_t typecode) {
         case RUN_CONTAINER_TYPE:
             return run_container_clone(const_CAST_run(c));
         case SHARED_CONTAINER_TYPE:
-            printf("shared containers are not cloneable\n");
-            assert(false);
+            // Shared containers are not cloneable. Are you mixing COW and non-COW bitmaps?
             return NULL;
         default:
             assert(false);
diff --git a/src/roaring.c b/src/roaring.c
index a24eaa332..918c9328b 100644
--- a/src/roaring.c
+++ b/src/roaring.c
@@ -300,7 +300,6 @@ void roaring_bitmap_printf_describe(const roaring_bitmap_t *r) {
         printf("%d: %s (%d)", ra->keys[i],
                get_full_container_name(ra->containers[i], ra->typecodes[i]),
                container_get_cardinality(ra->containers[i], ra->typecodes[i]));
-
         if (ra->typecodes[i] == SHARED_CONTAINER_TYPE) {
             printf(
                 "(shared count = %" PRIu32 " )",
@@ -402,6 +401,7 @@ roaring_bitmap_t *roaring_bitmap_copy(const roaring_bitmap_t *r) {
 
 bool roaring_bitmap_overwrite(roaring_bitmap_t *dest,
                                      const roaring_bitmap_t *src) {
+    roaring_bitmap_set_copy_on_write(dest, is_cow(src));
     return ra_overwrite(&src->high_low_container, &dest->high_low_container,
                         is_cow(src));
 }
@@ -589,7 +589,7 @@ roaring_bitmap_t *roaring_bitmap_and(const roaring_bitmap_t *x1,
               length2 = x2->high_low_container.size;
     uint32_t neededcap = length1 > length2 ? length2 : length1;
     roaring_bitmap_t *answer = roaring_bitmap_create_with_capacity(neededcap);
-    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2));
+    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2));
 
     int pos1 = 0, pos2 = 0;
 
@@ -737,7 +737,7 @@ roaring_bitmap_t *roaring_bitmap_or(const roaring_bitmap_t *x1,
     }
     roaring_bitmap_t *answer =
         roaring_bitmap_create_with_capacity(length1 + length2);
-    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2));
+    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2));
     int pos1 = 0, pos2 = 0;
     uint8_t type1, type2;
     uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1);
@@ -888,7 +888,7 @@ roaring_bitmap_t *roaring_bitmap_xor(const roaring_bitmap_t *x1,
     }
     roaring_bitmap_t *answer =
         roaring_bitmap_create_with_capacity(length1 + length2);
-    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2));
+    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2));
     int pos1 = 0, pos2 = 0;
     uint8_t type1, type2;
     uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1);
@@ -1049,14 +1049,14 @@ roaring_bitmap_t *roaring_bitmap_andnot(const roaring_bitmap_t *x1,
               length2 = x2->high_low_container.size;
     if (0 == length1) {
         roaring_bitmap_t *empty_bitmap = roaring_bitmap_create();
-        roaring_bitmap_set_copy_on_write(empty_bitmap, is_cow(x1) && is_cow(x2));
+        roaring_bitmap_set_copy_on_write(empty_bitmap, is_cow(x1) || is_cow(x2));
         return empty_bitmap;
     }
     if (0 == length2) {
         return roaring_bitmap_copy(x1);
     }
     roaring_bitmap_t *answer = roaring_bitmap_create_with_capacity(length1);
-    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2));
+    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2));
 
     int pos1 = 0, pos2 = 0;
     uint8_t type1, type2;
@@ -2147,7 +2147,7 @@ roaring_bitmap_t *roaring_bitmap_lazy_or(const roaring_bitmap_t *x1,
     }
     roaring_bitmap_t *answer =
         roaring_bitmap_create_with_capacity(length1 + length2);
-    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2));
+    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2));
     int pos1 = 0, pos2 = 0;
     uint8_t type1, type2;
     uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1);
@@ -2324,7 +2324,7 @@ roaring_bitmap_t *roaring_bitmap_lazy_xor(const roaring_bitmap_t *x1,
     }
     roaring_bitmap_t *answer =
         roaring_bitmap_create_with_capacity(length1 + length2);
-    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2));
+    roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2));
     int pos1 = 0, pos2 = 0;
     uint8_t type1, type2;
     uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1);
diff --git a/tools/cmake/FindCTargets.cmake b/tools/cmake/FindCTargets.cmake
index cccfa2bcc..986294d3d 100644
--- a/tools/cmake/FindCTargets.cmake
+++ b/tools/cmake/FindCTargets.cmake
@@ -16,7 +16,11 @@ endfunction(add_c_test)
 if (CMAKE_VERSION VERSION_GREATER 2.8.10)
   function(add_cpp_test TEST_NAME)
     add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
-    target_compile_definitions(${TEST_NAME} PUBLIC ROARING_EXCEPTIONS=${ROARING_EXCEPTIONS})
+    if(ROARING_EXCEPTIONS)
+      target_compile_definitions(${TEST_NAME} PUBLIC ROARING_EXCEPTIONS=1)
+    else()
+      target_compile_definitions(${TEST_NAME} PUBLIC ROARING_EXCEPTIONS=0)
+    endif()
     target_include_directories(${TEST_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/cpp)
     target_link_libraries(${TEST_NAME} ${ROARING_LIB_NAME} cmocka)
     add_test(${TEST_NAME} ${TEST_NAME})
diff --git a/tools/cmake/FindOptions.cmake b/tools/cmake/FindOptions.cmake
index 97d72d8bd..9810d7d6f 100644
--- a/tools/cmake/FindOptions.cmake
+++ b/tools/cmake/FindOptions.cmake
@@ -3,6 +3,7 @@ macro(append var string)
 endmacro(append)
 
 set(SANITIZE_FLAGS "")
+
 if(ROARING_SANITIZE)
   set(ROARING_SANITIZE_FLAGS "-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all")
   if (CMAKE_COMPILER_IS_GNUCC)


Test patch:
diff --git a/tests/cpp_unit.cpp b/tests/cpp_unit.cpp
index 0b9915b5f..dab91411c 100644
--- a/tests/cpp_unit.cpp
+++ b/tests/cpp_unit.cpp
@@ -159,6 +159,23 @@ void test_issue304(void) {
 
 DEFINE_TEST(test_issue304) { test_issue304(); }
 
+DEFINE_TEST(issue316) {
+    Roaring r1;
+    r1.setCopyOnWrite(true);
+    r1.addRange(1, 100);
+    Roaring r2;
+    r2 |= r1;
+    assert_true(r2.isSubset(r1));
+    assert_true(r1.isSubset(r2));
+    assert_true(r1 == r2);
+
+    Roaring r3 = r2;
+    assert_true(r3.isSubset(r1));
+    assert_true(r1.isSubset(r3));
+    assert_true(r1 == r3);
+    assert_true(r1 == r2);
+}
+
 void test_roaring64_iterate_multi_roaring(void) {
     Roaring64Map roaring;
 
@@ -168,7 +185,7 @@ void test_roaring64_iterate_multi_roaring(void) {
     assert_true(roaring.addChecked(uint64_t(2) << 32));
 
     uint64_t iterate_count = 0;
-    auto iterate_func = [](uint64_t value, void *param) -> bool {
+    auto iterate_func = [](uint64_t , void *param) -> bool {
         auto *count = static_cast<uint64_t *>(param);
         *count += 1;
         return *count < 2;
@@ -628,6 +645,7 @@ DEFINE_TEST(test_cpp_bidirectional_iterator_64) {
 int main() {
     roaring::misc::tellmeall();
     const struct CMUnitTest tests[] = {
+        cmocka_unit_test(issue316),
         cmocka_unit_test(test_issue304),
         cmocka_unit_test(serial_test),
         cmocka_unit_test(test_example_true),
diff --git a/tests/toplevel_unit.c b/tests/toplevel_unit.c
index b87f80c1c..6db0af45f 100644
--- a/tests/toplevel_unit.c
+++ b/tests/toplevel_unit.c
@@ -3429,6 +3429,26 @@ DEFINE_TEST(test_intersect_small_run_bitset) {
     roaring_bitmap_free(rb2);
 }
 
+
+DEFINE_TEST(issue316) {
+    roaring_bitmap_t *rb1 = roaring_bitmap_create();
+    roaring_bitmap_set_copy_on_write(rb1, true);
+    roaring_bitmap_add_range(rb1, 1, 100);
+    roaring_bitmap_t *rb2 = roaring_bitmap_create();
+    roaring_bitmap_or_inplace(rb2, rb1);
+    assert_true(roaring_bitmap_is_subset(rb2, rb1));
+    assert_true(roaring_bitmap_is_subset(rb1, rb2));
+    assert_true(roaring_bitmap_equals(rb1, rb2));
+    roaring_bitmap_t *rb3 = roaring_bitmap_copy(rb2);
+    assert_true(roaring_bitmap_is_subset(rb3, rb1));
+    assert_true(roaring_bitmap_is_subset(rb1, rb3));
+    assert_true(roaring_bitmap_equals(rb1, rb3));
+    assert_true(roaring_bitmap_equals(rb1, rb2));
+    roaring_bitmap_free(rb1);
+    roaring_bitmap_free(rb2);
+    roaring_bitmap_free(rb3);
+}
+
 DEFINE_TEST(test_subset) {
     uint32_t value;
     roaring_bitmap_t *rb1 = roaring_bitmap_create();
@@ -4156,6 +4176,7 @@ int main() {
     tellmeall();
 
     const struct CMUnitTest tests[] = {
+        cmocka_unit_test(issue316),
         cmocka_unit_test(issue288),
         cmocka_unit_test(issue245),
         cmocka_unit_test(issue208),