diff --git a/arraycontainer.go b/arraycontainer.go index f21bfa42..f1e4c853 100644 --- a/arraycontainer.go +++ b/arraycontainer.go @@ -1338,7 +1338,7 @@ func (ac *arrayContainer) addOffset(x uint16) (container, container) { return low, high } -// validate checks cardinality and sort order of the array container +// validate checks cardinality and that content is strictly increasing func (ac *arrayContainer) validate() error { cardinality := ac.getCardinality() @@ -1353,7 +1353,7 @@ func (ac *arrayContainer) validate() error { previous := ac.content[0] for i := 1; i < len(ac.content); i++ { next := ac.content[i] - if previous > next { + if previous >= next { return ErrArrayIncorrectSort } previous = next diff --git a/arraycontainer_test.go b/arraycontainer_test.go index 112c3103..8408080f 100644 --- a/arraycontainer_test.go +++ b/arraycontainer_test.go @@ -628,11 +628,20 @@ func TestArrayContainerValidation(t *testing.T) { array.content[500] = uint16(upperBound + upperBound) err = array.validate() - assert.Error(t, err) + assert.ErrorIs(t, err, ErrArrayIncorrectSort) + // Adjacent duplicates must be rejected (strictly increasing), matching + // CRoaring, Java, and Rust validators (previous >= next). array = newArrayContainer() + for i := 0; i < 10; i++ { + array.iadd(uint16(i)) + } + array.content[5] = array.content[4] + err = array.validate() + assert.ErrorIs(t, err, ErrArrayIncorrectSort) - // Technically a run, but make sure the incorrect sort detection handles equal elements + // Repeated iadd of the same value is a no-op; container stays valid. + array = newArrayContainer() for i := 0; i < upperBound; i++ { array.iadd(uint16(1)) } diff --git a/roaring_test.go b/roaring_test.go index 6b31c61f..04c9abdd 100644 --- a/roaring_test.go +++ b/roaring_test.go @@ -3427,6 +3427,24 @@ func TestBitMapValidationFromDeserialization(t *testing.T) { }, err: ErrArrayIncorrectSort, }, + { + name: "Array Adjacent Duplicates", + loader: func(bm *Bitmap) { + arrayEntries := make([]uint32, 0, 10) + for i := 0; i < 10; i++ { + arrayEntries = append(arrayEntries, uint32(i)) + } + bm.AddMany(arrayEntries) + }, + corruptor: func(s []byte) { + // Portable single-container array layout: values start at offset 16 + // as little-endian uint16s: content[i] at 16+2*i. + // Set content[5] equal to content[4] (adjacent duplicate). + s[26] = 4 + s[27] = 0 + }, + err: ErrArrayIncorrectSort, + }, } for _, tt := range deserializationTests {