Skip to content

Commit 154e055

Browse files
authored
Merge pull request #226 from bits-and-blooms/fix/deleteat-shrink-slice
fix deleteat
2 parents bb7c7fb + 79a6ce7 commit 154e055

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

bitset.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,20 @@ func (b *BitSet) DeleteAt(i uint) *BitSet {
560560

561561
b.length = b.length - 1
562562

563+
// the bitset may now use one word less: shrink the slice so that
564+
// len(b.set) keeps matching the number of words in use, as the rest
565+
// of the package expects. Otherwise, functions that scan the whole
566+
// slice (e.g., SetAll, Count) would operate on a word that lies
567+
// beyond the length of the bitset.
568+
if wordCount := b.wordCount(); wordCount < len(b.set) {
569+
// the discarded words must be zeroed: extendSet may later revive
570+
// them with a fast resize, and they must not carry stale bits.
571+
for i := wordCount; i < len(b.set); i++ {
572+
b.set[i] = 0
573+
}
574+
b.set = b.set[:wordCount]
575+
}
576+
563577
return b
564578
}
565579

bitset_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,6 +2222,36 @@ func TestDeleteWithBitSetInstance(t *testing.T) {
22222222
}
22232223
}
22242224

2225+
// DeleteAt reduces the length of the bitset, and it must shrink the
2226+
// underlying slice accordingly, otherwise operations that scan the whole
2227+
// slice see words beyond the length of the bitset. See issue #225.
2228+
func TestDeleteAtKeepsWordCount(t *testing.T) {
2229+
for _, length := range []uint{1, 64, 65, 128, 129, 192, 256, 257} {
2230+
b := New(length - 1)
2231+
b.Set(length - 1) // b has 'length' bits, the last one set
2232+
b.DeleteAt(0)
2233+
2234+
if b.Len() != length-1 {
2235+
t.Fatalf("length %d: expected a length of %d, got %d", length, length-1, b.Len())
2236+
}
2237+
if len(b.set) != wordsNeeded(b.Len()) {
2238+
t.Errorf("length %d: expected %d words, got %d", length, wordsNeeded(b.Len()), len(b.set))
2239+
}
2240+
2241+
b.SetAll()
2242+
if !b.All() {
2243+
t.Errorf("length %d: All should be true after SetAll, %d bits set out of %d", length, b.Count(), b.Len())
2244+
}
2245+
2246+
// a word dropped by DeleteAt must not come back with stale bits
2247+
// when the bitset grows back to its former size
2248+
b.Set(length - 1)
2249+
if b.Count() != length {
2250+
t.Errorf("length %d: expected %d bits set, got %d", length, length, b.Count())
2251+
}
2252+
}
2253+
}
2254+
22252255
func TestWriteTo(t *testing.T) {
22262256
const length = 9585
22272257
const oneEvery = 97

0 commit comments

Comments
 (0)