roaring: fix intersectionAnyRunBitmap when processing single-word runs

When a run started and ended within a single word, the entirety of the word would be checked.
This would cause small runs to be processed incorrectly, and caused Distinct-on-sets to select rows that did not match the specified filter.
This commit is contained in:
Nia Weiss 2021-01-21 11:11:20 -05:00
parent b273f3ba60
commit 0d9179f10c
No known key found for this signature in database
GPG key ID: 895E83409BFDA1BB
2 changed files with 25 additions and 13 deletions

View file

@ -4144,26 +4144,26 @@ func intersectionAnyRunBitmap(a, b *Container) bool {
bb := b.bitmap()[:1024]
runs := a.runs()
for _, r := range runs {
loWord, loBit := r.Start/64, r.Start%64
hiWord, hiBit := r.Last/64, r.Last%64
if loBit != 0 {
w := bb[loWord]
mask := (uint64(1) << loBit) - 1
if w&^mask != 0 {
if r.Start/64 == r.Last/64 {
mask := (^uint64(0) << (r.Start % 64)) &^
(^uint64(0) << ((r.Last % 64) + 1))
if mask&bb[r.Start/64] != 0 {
return true
}
continue
}
for i := loWord; i < hiWord; i++ {
firstWord, lastWord := r.Start/64, r.Last/64
for i := firstWord + 1; i < lastWord; i++ {
if bb[i] != 0 {
return true
}
}
if hiBit != 0 {
w := bb[hiWord]
mask := (uint64(1) << hiBit) - 1
if w&mask != 0 {
return true
}
firstMask := ^uint64(0) << (r.Start % 64)
lastMask := ^(^uint64(0) << ((r.Last % 64) + 1))
if (firstMask&bb[firstWord])|(lastMask&bb[lastWord]) != 0 {
return true
}
}
return false

View file

@ -97,3 +97,15 @@ func TestIntersectVariants(t *testing.T) {
}
}
}
func TestIntersectionAnyRunBitmapSingleWordRegression(t *testing.T) {
// In a previous version, single-word runs would match any bit within the word.
// Verify that this no longer happens.
any := intersectionAnyRunBitmap(
NewContainerRun([]Interval16{{1, 2}}),
NewContainerBitmapN([]uint64{0b1001}, 2),
)
if any {
t.Errorf("matched an exclusive single-word run")
}
}