Repository: RoaringBitmap/CRoaring (C)

Issue:
Bug in 'bitset_next_set_bits'
Using CRoaring REPO as of now the return from the method 'bitset_next_set_bits' is wrong. 

Although the documentation does not make it totally clear, the buffer 'bitset_next_set_bits' updates seems to contain bitset offsets relative to the startfrom index.  Also capacity seems to be number of 'size_t' items not bytes, which makes sense in this context.

This bug demonstrates those offsets are not always correct.

Minimal, complete program to demonstrate bug:

```
#include <roaring/bitset/bitset.h>
u_int32_t MAX = 262144;

using namespace roaring::api;

// Pretty-print up to the first 'oneCount' bits in 'r1' to stdout
void pprint(bitset_t* r1, const size_t oneCount) {
  size_t count, i;
  const size_t max = bitset_maximum(r1);
  for (count=i=0; i<=max && count<oneCount; ++i) {
    const bool on = bitset_get(r1, i);
    count += on;
    printf("%c", on ? '1' : '_');
    if (i&& 0==((i+1)%10)) {
      printf("   %lu\n", i+1);
    }
  }
  printf("\n");
  fflush(stdout);
}

void bug() {
  // create a new empty bitmap
  bitset_t* r1 = bitset_create_with_capacity(MAX);
  bitset_clear(r1);

  // Set 1-bits to match diagram  
  // __________   10
  // __________   20
  // __________   30
  // ______1111   40
  // 1111______   50
  // ________11   60
  // 11111111__   70
  // 1111111111   80
  // 1111111111   90

  bitset_set(r1, 36);
  bitset_set(r1, 37);
  bitset_set(r1, 38);
  bitset_set(r1, 39);
  //-
  bitset_set(r1, 40);
  bitset_set(r1, 41);
  bitset_set(r1, 42);
  bitset_set(r1, 43);
  // -
  bitset_set(r1, 58);
  bitset_set(r1, 59);
  // -
  bitset_set(r1, 60);
  bitset_set(r1, 61);
  bitset_set(r1, 62);
  bitset_set(r1, 63);
  bitset_set(r1, 64);
  bitset_set(r1, 65);
  bitset_set(r1, 66);
  bitset_set(r1, 67);
  // -
  bitset_set(r1, 70);
  bitset_set(r1, 71);
  bitset_set(r1, 72);
  bitset_set(r1, 73);
  bitset_set(r1, 74);
  bitset_set(r1, 75);
  bitset_set(r1, 76);
  bitset_set(r1, 77);
  bitset_set(r1, 78);
  bitset_set(r1, 79);
  // -
  bitset_set(r1, 80);
  bitset_set(r1, 81);
  bitset_set(r1, 82);
  bitset_set(r1, 83);
  bitset_set(r1, 84);
  bitset_set(r1, 85);
  bitset_set(r1, 86);
  bitset_set(r1, 87);
  bitset_set(r1, 88);
  bitset_set(r1, 89);


  // Search for the first 20 1-bits
  bool ok = true;
  size_t buffer[20];
  size_t baseIdx, endIdx, cnt;

  // Not strictly needed but tips hand on what 'bitset_next_set_bits' modifies
  memset(buffer, 0xff, sizeof(buffer));
  // Tell 'bitset_next_set_bits' to start at minimum of bitset
  endIdx = bitset_minimum(r1);
  baseIdx = endIdx;
  // Look for the first 20 1-bits starting at 'bitset_minimum'
  cnt = bitset_next_set_bits(r1, buffer, 20, &endIdx);
  // Tell stdout about search
  printf("searched for first %d 1-bits got howMany %lu starting at baseIdx %lu endIdx %lu\n",
      20, cnt, baseIdx, endIdx);
  // Pretty print the first 150 positions in 'r1' as visual-aid
  pprint(r1, 150);
  // Verify what 'bitset_next_set_bits' wrote in 'buffer'
  // buffer holds offsets relative to baseIdx
  for (size_t idx=0; idx<cnt; ++idx) {
    // Is it really a 1-bit as 'buffer[idx]' claims?
    ok &= bitset_get(r1, baseIdx+buffer[idx]);
    printf("checked baseIdx %lu + offset %lu ok %d\n", baseIdx, buffer[idx], ok);
  }
  if (!ok) {
    printf("bitset_next_set_bits FAIL\n");
  } else {
    printf("bitset_next_set_bits OK\n");
  }

  bitset_free(r1);
}

int main() {
  bug();
  return 0;
}
```

which produces this output:

```
searched for first 20 1-bits got howMany 20 starting at baseIdx 36 endIdx 71
__________   10
__________   20
__________   30
______1111   40
1111______   50
________11   60
11111111__   70
1111111111   80
1111111111   90

checked baseIdx 36 + offset 0 ok 1
checked baseIdx 36 + offset 1 ok 1
checked baseIdx 36 + offset 2 ok 1
checked baseIdx 36 + offset 3 ok 1
checked baseIdx 36 + offset 4 ok 1
checked baseIdx 36 + offset 5 ok 1
checked baseIdx 36 + offset 6 ok 1
checked baseIdx 36 + offset 7 ok 1
checked baseIdx 36 + offset 22 ok 1
checked baseIdx 36 + offset 23 ok 1
checked baseIdx 36 + offset 24 ok 1
checked baseIdx 36 + offset 25 ok 1
checked baseIdx 36 + offset 26 ok 1
checked baseIdx 36 + offset 27 ok 1
checked baseIdx 36 + offset 64 ok 0             <---- why these offsets?
checked baseIdx 36 + offset 65 ok 0
checked baseIdx 36 + offset 66 ok 0
checked baseIdx 36 + offset 67 ok 0
checked baseIdx 36 + offset 70 ok 0
checked baseIdx 36 + offset 71 ok 0
bitset_next_set_bits FAIL
```



Fix (patch):
diff --git a/include/roaring/bitset/bitset.h b/include/roaring/bitset/bitset.h
index 38ed39e8..22062ca4 100644
--- a/include/roaring/bitset/bitset.h
+++ b/include/roaring/bitset/bitset.h
@@ -236,7 +236,8 @@ inline size_t bitset_next_set_bits(const bitset_t *bitset, size_t *buffer,
         return 0;  // nothing more to iterate over
     }
     uint64_t w = bitset->array[x];
-    w >>= (*startfrom & 63);
+    // unset low bits inside the word less than *startfrom
+    w &= ~((UINT64_C(1) << (*startfrom & 63)) - 1);
     size_t howmany = 0;
     size_t base = x << 6;
     while (howmany < capacity) {