Fix Xor modifying its argument and 64-bit self-XOR - #564
Open
gitRasheed wants to merge 2 commits into
Open
Conversation
An array or run receiver XORed with a bitmap container ran the in-place operation on the argument's container and adopted it, so a.Xor(b) changed b and left both bitmaps sharing storage. Compute the result on the receiver's side instead. The test covers every in-place operation and container pair.
Keys present only in the argument were inserted by pointer, so editing the receiver afterwards changed the argument. Clone them, as Or does. XORing a bitmap with itself removed entries from the structure being iterated and could panic; clear the receiver instead, as the 32-bit Xor does.
gitRasheed
force-pushed
the
fix/xor-argument-mutation
branch
from
September 5, 2026 21:33
0c0d656 to
0765837
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Three bugs I hit while making
roaring64.Bitmap.Xorwork in place (#565). Each fails on master with the tests added here.32-bit
a.Xor(b)modifiesbwhen a container ofais an array or run and the matching container ofbis a bitmap. Since v2.14.5 that pair runs the in-place XOR onb's container and adopts it, sobholds the result too and the two bitmaps share a container:64-bit
Xorinserted the containers of argument-only keys by pointer, soa.Xor(b); a.Remove(v)removedvfromb. They are now cloned, asOrdoes since roaring64: honor copy-on-write in Bitmap.Or #525.64-bit
x.Xor(x)with more than one key panicked while iterating the structure it was deleting from. It now clears the receiver, as the 32-bitXordoes.Type of Change
Changes Made
What was changed?
arraycontainer.go,runcontainer.go:ixorBitmapcomputes on the receiver's side.roaring64/roaring64.go: clone on insertion, early return for self-XOR.inplace_ownership_test.goin both packages: every in-place operation on every pair of nine container shapes, copy-on-write on and off, checked against the pure function; the argument must be unchanged after the call and after the result is edited; each operation applied to the same bitmap twice.Why was it changed?
An in-place operation must not modify its argument.
How was it changed?
Two one-line call changes, a
Clone()and a four-line guard.Testing
go test ./...passes on arm64 and amd64. Or, And and AndNot pass the new test on master; only Xor fails.Formatting
go fmtclean.Performance Impact
Cloning costs where the old code shared. The two disjoint-key benchmarks, four interleaved single-value keys per input, slow down: two XORs that insert and then cancel go from 359 to 1,310 ns on c8g.xlarge with 4 to 40 allocations, and clone plus one XOR goes from 1,069 to 1,705 ns. Matching-key synthetic cases move by up to 8.6% between the two builds and the six real-data cases by under 0.5%.
Breaking Changes
None.