Fix Or/Xor dropping bits when combining bitmaps of different lengths (#51)

* fix: union/xor of different-sized bitmaps drops bits in variadic path

Or and Xor with extra bitmaps used _or_many/_xor_many, which apply a single
word count to every input. The count came from pointersOf, whose length is
the maximum over the extra slice only and ignores other, so a longer other
lost its tail words. More generally a uniform count cannot combine operands
of different lengths: a longer source is truncated and a shorter one is read
past its end.

Fold each source in with the single-operand SIMD primitive over its own
length, matching the scalar reference in simd_generic.go. Fixes the amd64
accelerated and AVX-512 branches and the arm64 branch.

* Keep the _many primitives for equal-sized operands, fold only ragged ones

* test: cover longer ragged bitmap extras

---------

Co-authored-by: Roman <roman.atachiants@gmail.com>
This commit is contained in:
dualfrozandRoman committed 2026-09-23 15:30:32 +04:00
1 parent 58b05bdfdd
commit 47698fd8b9
4 files changed
+146 -12

No files matched your search

+30
View File
@@ -236,6 +236,36 @@ func dimensionsOf(n, m int) uint64 {
// pointersOf returns a pointer to an array containing pointers to the
// first element of each bitmap and the maximum length of all bitmaps
// simdBinary is one of the accelerated two-operand primitives.
type simdBinary func(dst, src unsafe.Pointer, n uint64)
// uniformLen returns the word count shared by every source, or zero when the sources
// differ in length. The _many primitives apply a single count to all of their inputs,
// so they can only combine sources that are all the same length.
func uniformLen(other Bitmap, extra []Bitmap) int {
n := len(other)
for i := range extra {
if len(extra[i]) != n {
return 0
}
}
return n
}
// foldEach applies op to every source in turn, each over its own length. This is what
// ragged inputs need: a longer source would be truncated by a shared count, a shorter
// one read past its end.
func foldEach(dst *Bitmap, op simdBinary, other Bitmap, extra []Bitmap) {
if len(other) > 0 {
op(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&other[0]), uint64(len(other)))
}
for i := range extra {
if len(extra[i]) > 0 {
op(unsafe.Pointer(&(*dst)[0]), unsafe.Pointer(&extra[i][0]), uint64(len(extra[i])))
}
}
}
func pointersOf(other Bitmap, extra []Bitmap) (unsafe.Pointer, int) {
out := make([]unsafe.Pointer, len(extra)+1)
out[0] = unsafe.Pointer(&other[0])