diff --git a/roaring64/roaring64.go b/roaring64/roaring64.go index 8589925e..adb508fe 100644 --- a/roaring64/roaring64.go +++ b/roaring64/roaring64.go @@ -700,7 +700,7 @@ main: } s2 = x2.highlowcontainer.getKeyAtIndex(pos2) } else { - rb.highlowcontainer.getContainerAtIndex(pos1).Or(x2.highlowcontainer.getContainerAtIndex(pos2)) + rb.highlowcontainer.getWritableContainerAtIndex(pos1).Or(x2.highlowcontainer.getContainerAtIndex(pos2)) pos1++ pos2++ if (pos1 == length1) || (pos2 == length2) { diff --git a/roaring64/roaring64cow_test.go b/roaring64/roaring64cow_test.go index fb9eb38d..ac7010cd 100644 --- a/roaring64/roaring64cow_test.go +++ b/roaring64/roaring64cow_test.go @@ -1768,3 +1768,32 @@ func TestCloneCOWContainers(t *testing.T) { //assert.EqualValues(t, rb.ToArray(), newRb1.ToArray()) } + +// TestOrCOWSharedContainer verifies that in-place Or on a CoW-cloned bitmap +// does not mutate containers shared with the clone source. +func TestOrCOWSharedContainer(t *testing.T) { + rb1 := NewBitmap() + rb1.SetCopyOnWrite(true) + // Populate a single high-key container with values in [0, 1000). + for i := uint64(0); i < 1000; i++ { + rb1.Add(i) + } + + // Clone: rb2 shares rb1's container, with needCopyOnWrite set on both sides. + rb2 := rb1.Clone() + + // x has values in the same high-key container that are not already in rb1/rb2. + x := NewBitmap() + for i := uint64(1000); i < 2000; i++ { + x.Add(i) + } + + // In-place Or into rb1. Must NOT mutate the container shared with rb2. + rb1.Or(x) + + assert.EqualValues(t, 2000, rb1.GetCardinality()) + assert.EqualValues(t, 1000, rb2.GetCardinality(), + "rb2 was corrupted: Or on rb1 mutated a CoW-shared container") + assert.False(t, rb2.Contains(1500), + "rb2 was corrupted: contains a value that was only added to rb1") +}