lotus/lib/rlepluslazy/internal/bitvector_test.go
Jakub Sztandera 3d6071ed3f
Improve benchmarks and fix bitvector iterator
License: MIT
Signed-off-by: Jakub Sztandera <kubuxu@protonmail.ch>
2019-12-07 15:48:20 +01:00

154 lines
3.6 KiB
Go

package bitvector_test
import (
"runtime"
"testing"
"github.com/stretchr/testify/assert"
bitvector "github.com/filecoin-project/go-lotus/lib/rlepluslazy/internal"
)
func TestBitVector(t *testing.T) {
t.Run("zero value", func(t *testing.T) {
var v bitvector.BitVector
assert.Equal(t, bitvector.LSB0, v.BytePacking)
})
t.Run("Push", func(t *testing.T) {
// MSB0 bit numbering
v := bitvector.BitVector{BytePacking: bitvector.MSB0}
v.Push(1)
v.Push(0)
v.Push(1)
v.Push(1)
assert.Equal(t, byte(176), v.Buf[0])
// LSB0 bit numbering
v = bitvector.BitVector{BytePacking: bitvector.LSB0}
v.Push(1)
v.Push(0)
v.Push(1)
v.Push(1)
assert.Equal(t, byte(13), v.Buf[0])
})
t.Run("Get", func(t *testing.T) {
bits := []byte{1, 0, 1, 1, 0, 0, 1, 0}
for _, numbering := range []bitvector.BitNumbering{bitvector.MSB0, bitvector.LSB0} {
v := bitvector.BitVector{BytePacking: numbering}
for _, bit := range bits {
v.Push(bit)
}
for idx, expected := range bits {
actual, _ := v.Get(uint(idx))
assert.Equal(t, expected, actual)
}
}
})
t.Run("Extend", func(t *testing.T) {
val := byte(171) // 0b10101011
var v bitvector.BitVector
// MSB0 bit numbering
v = bitvector.BitVector{}
v.Extend(val, 4, bitvector.MSB0)
assertBitVector(t, []byte{1, 0, 1, 0}, v)
v.Extend(val, 5, bitvector.MSB0)
assertBitVector(t, []byte{1, 0, 1, 0, 1, 0, 1, 0, 1}, v)
// LSB0 bit numbering
v = bitvector.BitVector{}
v.Extend(val, 4, bitvector.LSB0)
assertBitVector(t, []byte{1, 1, 0, 1}, v)
v.Extend(val, 5, bitvector.LSB0)
assertBitVector(t, []byte{1, 1, 0, 1, 1, 1, 0, 1, 0}, v)
})
t.Run("invalid counts to Take/Extend/Iterator cause panics", func(t *testing.T) {
v := bitvector.BitVector{BytePacking: bitvector.LSB0}
assert.Panics(t, func() { v.Extend(0xff, 9, bitvector.LSB0) })
assert.Panics(t, func() { v.Take(0, 9, bitvector.LSB0) })
next := v.Iterator(bitvector.LSB0)
assert.Panics(t, func() { next(9) })
})
t.Run("Take", func(t *testing.T) {
var v bitvector.BitVector
bits := []byte{1, 0, 1, 0, 1, 0, 1, 1}
for _, bit := range bits {
v.Push(bit)
}
assert.Equal(t, byte(176), v.Take(4, 4, bitvector.MSB0))
assert.Equal(t, byte(13), v.Take(4, 4, bitvector.LSB0))
})
t.Run("Iterator", func(t *testing.T) {
var buf []byte
// make a bitvector of 256 sample bits
for i := 0; i < 1000; i++ {
buf = append(buf, byte(i))
}
v := bitvector.NewBitVector(buf, bitvector.LSB0)
next := v.Iterator(bitvector.LSB0)
// compare to Get()
for i := uint(0); i < v.Len; i++ {
expected, _ := v.Get(i)
assert.Equal(t, expected, next(1), "at index %d", i)
}
// out of range should return zero
assert.Equal(t, byte(0), next(1))
assert.Equal(t, byte(0), next(8))
// compare to Take()
next = v.Iterator(bitvector.LSB0)
assert.Equal(t, next(5), v.Take(0, 5, bitvector.LSB0))
assert.Equal(t, next(8), v.Take(5, 8, bitvector.LSB0))
})
}
// Note: When using this helper assertion, expectedBits should *only* be 0s and 1s.
func assertBitVector(t *testing.T, expectedBits []byte, actual bitvector.BitVector) {
assert.Equal(t, uint(len(expectedBits)), actual.Len)
for idx, bit := range expectedBits {
actualBit, err := actual.Get(uint(idx))
assert.NoError(t, err)
assert.Equal(t, bit, actualBit)
}
}
func BenchmarkTake(b *testing.B) {
buf := make([]byte, 0, 128)
for i := 0; i < 128; i++ {
buf = append(buf, byte(128+i))
}
v := bitvector.NewBitVector(buf, bitvector.LSB0)
b.ReportAllocs()
b.ResetTimer()
next := v.Iterator(bitvector.LSB0)
for i := 0; i < b.N; i++ {
runtime.KeepAlive(next(8))
}
}