diff --git a/cow_edgecases_test.go b/cow_edgecases_test.go index 3f30ac3f..3ab79421 100644 --- a/cow_edgecases_test.go +++ b/cow_edgecases_test.go @@ -398,3 +398,28 @@ func TestCOWAndNotResultReusedByXor(t *testing.T) { require.True(t, c.Equals(BitmapOf(939524096, 939524097, 939524098, 939524099))) require.NoError(t, c.Validate()) } + +// Clear resets the bitmap to a new one, so copy-on-write is turned off. +// Xor and AndNot of a bitmap with itself also empty it, but keep the setting. +func TestClearResetsCopyOnWrite(t *testing.T) { + rb := BitmapOf(1, 2, 3) + rb.SetCopyOnWrite(true) + rb.Clear() + require.False(t, rb.GetCopyOnWrite()) + rb.Add(4) + require.NoError(t, rb.Validate()) + + for _, op := range []func(*Bitmap){ + func(b *Bitmap) { b.Xor(b) }, + func(b *Bitmap) { b.AndNot(b) }, + } { + b := BitmapOf(1, 2, 3, 100000) + b.SetCopyOnWrite(true) + op(b) + require.True(t, b.IsEmpty()) + require.True(t, b.GetCopyOnWrite()) + b.Add(5) + require.NoError(t, b.Validate()) + require.True(t, b.Contains(5)) + } +} diff --git a/roaring.go b/roaring.go index 0e35660a..83c5cc97 100644 --- a/roaring.go +++ b/roaring.go @@ -441,7 +441,12 @@ func New() *Bitmap { } // Clear resets the Bitmap to be logically empty, but may retain -// some memory allocations that may speed up future operations +// some memory allocations that may speed up future operations. +// +// Clear also turns copy-on-write off: after Clear, the bitmap behaves like +// a new Bitmap and GetCopyOnWrite returns false, whether copy-on-write was +// enabled with SetCopyOnWrite or set internally by FromBuffer. Call +// SetCopyOnWrite(true) after Clear to keep using copy-on-write. func (rb *Bitmap) Clear() { rb.highlowcontainer.clear() } @@ -1608,7 +1613,8 @@ main: // Xor computes the symmetric difference between two bitmaps and stores the result in the current bitmap func (rb *Bitmap) Xor(x2 *Bitmap) { if rb == x2 { - rb.Clear() + // the result is empty; keep the copy-on-write setting, unlike Clear + rb.highlowcontainer.resize(0) return } pos1 := 0 @@ -1702,7 +1708,8 @@ main: // AndNot computes the difference between two bitmaps and stores the result in the current bitmap func (rb *Bitmap) AndNot(x2 *Bitmap) { if rb == x2 { - rb.Clear() + // the result is empty; keep the copy-on-write setting, unlike Clear + rb.highlowcontainer.resize(0) return } pos1 := 0 diff --git a/roaring64/roaring64.go b/roaring64/roaring64.go index 84444534..113a65d2 100644 --- a/roaring64/roaring64.go +++ b/roaring64/roaring64.go @@ -199,7 +199,11 @@ func New() *Bitmap { } // Clear resets the Bitmap to be logically empty, but may retain -// some memory allocations that may speed up future operations +// some memory allocations that may speed up future operations. +// +// Clear also turns copy-on-write off: after Clear, the bitmap behaves like +// a new Bitmap and GetCopyOnWrite returns false. Call SetCopyOnWrite(true) +// after Clear to keep using copy-on-write. func (rb *Bitmap) Clear() { rb.highlowcontainer.clear() } diff --git a/roaring64/roaring64cow_test.go b/roaring64/roaring64cow_test.go index ac7010cd..cec8fb62 100644 --- a/roaring64/roaring64cow_test.go +++ b/roaring64/roaring64cow_test.go @@ -1797,3 +1797,24 @@ func TestOrCOWSharedContainer(t *testing.T) { assert.False(t, rb2.Contains(1500), "rb2 was corrupted: contains a value that was only added to rb1") } + +// Clear resets the bitmap to a new one, so copy-on-write is turned off. +func TestClearResetsCopyOnWrite64(t *testing.T) { + rb := BitmapOf(1, 2, 3, 1<<40) + rb.SetCopyOnWrite(true) + rb.Clear() + assert.False(t, rb.GetCopyOnWrite()) + rb.Add(4) + assert.NoError(t, rb.Validate()) + + for _, op := range []func(*Bitmap){ + func(b *Bitmap) { b.Xor(b) }, + func(b *Bitmap) { b.AndNot(b) }, + } { + b := BitmapOf(1, 2, 3, 1<<40) + b.SetCopyOnWrite(true) + op(b) + assert.True(t, b.IsEmpty()) + assert.True(t, b.GetCopyOnWrite()) + } +}