Skip to content

Commit 8746862

Browse files
committed
roaring64: honor copy-on-write in Bitmap.Or
When two bitmaps share a container key, the in-place Or was calling getContainerAtIndex, which returns the raw container without consulting needCopyOnWrite. If rb's container at that index was shared with another bitmap (e.g. produced by Clone() on a CoW-enabled bitmap), the Or would mutate the shared container in place and silently corrupt the other bitmap. Switch to getWritableContainerAtIndex, which clones the container first when needCopyOnWrite is set. This matches what the sibling AndNot/And methods in the same file already do, and what the 32-bit Bitmap.Or does via getUnionedWritableContainer.
1 parent 6d3d113 commit 8746862

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

roaring64/roaring64.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ main:
700700
}
701701
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
702702
} else {
703-
rb.highlowcontainer.getContainerAtIndex(pos1).Or(x2.highlowcontainer.getContainerAtIndex(pos2))
703+
rb.highlowcontainer.getWritableContainerAtIndex(pos1).Or(x2.highlowcontainer.getContainerAtIndex(pos2))
704704
pos1++
705705
pos2++
706706
if (pos1 == length1) || (pos2 == length2) {

roaring64/roaring64cow_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,3 +1768,32 @@ func TestCloneCOWContainers(t *testing.T) {
17681768

17691769
//assert.EqualValues(t, rb.ToArray(), newRb1.ToArray())
17701770
}
1771+
1772+
// TestOrCOWSharedContainer verifies that in-place Or on a CoW-cloned bitmap
1773+
// does not mutate containers shared with the clone source.
1774+
func TestOrCOWSharedContainer(t *testing.T) {
1775+
rb1 := NewBitmap()
1776+
rb1.SetCopyOnWrite(true)
1777+
// Populate a single high-key container with values in [0, 1000).
1778+
for i := uint64(0); i < 1000; i++ {
1779+
rb1.Add(i)
1780+
}
1781+
1782+
// Clone: rb2 shares rb1's container, with needCopyOnWrite set on both sides.
1783+
rb2 := rb1.Clone()
1784+
1785+
// x has values in the same high-key container that are not already in rb1/rb2.
1786+
x := NewBitmap()
1787+
for i := uint64(1000); i < 2000; i++ {
1788+
x.Add(i)
1789+
}
1790+
1791+
// In-place Or into rb1. Must NOT mutate the container shared with rb2.
1792+
rb1.Or(x)
1793+
1794+
assert.EqualValues(t, 2000, rb1.GetCardinality())
1795+
assert.EqualValues(t, 1000, rb2.GetCardinality(),
1796+
"rb2 was corrupted: Or on rb1 mutated a CoW-shared container")
1797+
assert.False(t, rb2.Contains(1500),
1798+
"rb2 was corrupted: contains a value that was only added to rb1")
1799+
}

0 commit comments

Comments
 (0)