Skip to content

Fix NextUnsetBit returning set bits in bitmap containers - #593

Merged
lemire merged 1 commit into
masterfrom
fix-unset-bitmap-container
Nov 12, 2026
Merged

lemire merged 1 commit into
masterfrom
fix-unset-bitmap-container

Conversation

@lemire

@lemire lemire commented Nov 12, 2026

Copy link
Copy Markdown
Member

Fixes #592.

bitmapContainer.NextUnsetBit shifted the word right before inverting it, so the zeros shifted in at the top turned into ones and were read as unset bits. Whenever the rest of the word was all ones, it returned the first bit of the next word, even if that bit was set. The bitmap container's unset iterator (Unset, UnsetIterator, including AdvanceIfNeeded) uses this function, so it yielded values that are in the bitmap:

d := roaring.NewBitmap()
d.AddMany([]uint32{0})
d.AddRange(2, 4098)
for v := range roaring.Unset(d, 2, 4099) { fmt.Println(v) } // 64 128 192 ... instead of 4098 4099

The fix inverts first and then shifts, as nextAbsentValue already does (79ebb60).

The NextAbsentValue/PreviousAbsentValue part of #592 was already fixed in v2.30.0. The report's output matches v2.29.0.

Tests:

Both tests fail without the fix. go test ./... passes.

NextUnsetBit shifted the word before inverting it, so the zeros shifted
in at the top became ones and were taken for unset bits. When the rest
of the word was all ones, it returned the first bit of the next word,
which made the Unset iterator yield values present in the bitmap.

Invert first, then shift, as nextAbsentValue already does.

Fixes #592
@lemire
lemire merged commit da64f7d into master Nov 12, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NextAbsentValue and PreviousAbsentValue answer with a value that is present in a bitmap container

1 participant