tendermint/common/bit_array.go

170 lines
3.4 KiB
Go
Raw Normal View History

2014-09-14 15:37:32 -07:00
package common
import (
"fmt"
2014-09-14 15:37:32 -07:00
"math/rand"
2014-10-15 20:15:38 -07:00
"strings"
2014-09-14 15:37:32 -07:00
)
// Not goroutine safe
type BitArray struct {
bits uint
elems []uint64
}
2014-09-14 15:37:32 -07:00
func NewBitArray(bits uint) BitArray {
return BitArray{bits, make([]uint64, (bits+63)/64)}
2014-09-14 15:37:32 -07:00
}
func (bA BitArray) Size() uint {
return bA.bits
}
func (bA BitArray) IsZero() bool {
return bA.bits == 0
}
// NOTE: behavior is undefined if i >= bA.bits
2014-09-14 15:37:32 -07:00
func (bA BitArray) GetIndex(i uint) bool {
2014-10-25 14:27:53 -07:00
if i >= bA.bits {
return false
}
return bA.elems[i/64]&uint64(1<<(i%64)) > 0
2014-09-14 15:37:32 -07:00
}
// NOTE: behavior is undefined if i >= bA.bits
2014-10-25 14:27:53 -07:00
func (bA BitArray) SetIndex(i uint, v bool) bool {
if i >= bA.bits {
return false
}
2014-09-14 15:37:32 -07:00
if v {
bA.elems[i/64] |= uint64(1 << (i % 64))
2014-09-14 15:37:32 -07:00
} else {
bA.elems[i/64] &= ^uint64(1 << (i % 64))
2014-09-14 15:37:32 -07:00
}
2014-10-25 14:27:53 -07:00
return true
2014-09-14 15:37:32 -07:00
}
func (bA BitArray) Copy() BitArray {
c := make([]uint64, len(bA.elems))
copy(c, bA.elems)
return BitArray{bA.bits, c}
}
func (bA BitArray) copyBits(bits uint) BitArray {
c := make([]uint64, (bits+63)/64)
copy(c, bA.elems)
return BitArray{bits, c}
2014-09-14 15:37:32 -07:00
}
// Returns a BitArray of larger bits size.
2014-09-14 15:37:32 -07:00
func (bA BitArray) Or(o BitArray) BitArray {
c := bA.copyBits(MaxUint(bA.bits, o.bits))
for i := 0; i < len(c.elems); i++ {
c.elems[i] |= o.elems[i]
2014-09-14 15:37:32 -07:00
}
return c
}
// Returns a BitArray of smaller bit size.
2014-09-14 15:37:32 -07:00
func (bA BitArray) And(o BitArray) BitArray {
c := bA.copyBits(MinUint(bA.bits, o.bits))
for i := 0; i < len(c.elems); i++ {
c.elems[i] &= o.elems[i]
2014-09-14 15:37:32 -07:00
}
return c
}
func (bA BitArray) Not() BitArray {
c := bA.Copy()
for i := 0; i < len(c.elems); i++ {
c.elems[i] = ^c.elems[i]
2014-09-14 15:37:32 -07:00
}
return c
}
func (bA BitArray) Sub(o BitArray) BitArray {
2014-10-25 14:27:53 -07:00
if bA.bits > o.bits {
c := bA.Copy()
for i := 0; i < len(o.elems)-1; i++ {
c.elems[i] &= ^c.elems[i]
}
i := uint(len(o.elems) - 1)
if i >= 0 {
for idx := i * 64; idx < o.bits; idx++ {
c.SetIndex(idx, c.GetIndex(idx) && !o.GetIndex(idx))
}
}
return c
} else {
return bA.And(o.Not())
}
2014-09-14 15:37:32 -07:00
}
func (bA BitArray) PickRandom() (uint, bool) {
length := len(bA.elems)
2014-10-25 14:27:53 -07:00
if length == 0 {
return 0, false
}
randElemStart := rand.Intn(length)
for i := 0; i < length; i++ {
elemIdx := ((i + randElemStart) % length)
if elemIdx < length-1 {
if bA.elems[elemIdx] > 0 {
randBitStart := rand.Intn(64)
for j := 0; j < 64; j++ {
bitIdx := ((j + randBitStart) % 64)
if (bA.elems[elemIdx] & (1 << uint(bitIdx))) > 0 {
return 64*uint(elemIdx) + uint(bitIdx), true
}
2014-09-14 15:37:32 -07:00
}
panic("should not happen")
2014-09-14 15:37:32 -07:00
}
} else {
// Special case for last elem, to ignore straggler bits
elemBits := int(bA.bits) % 64
if elemBits == 0 {
elemBits = 64
}
randBitStart := rand.Intn(elemBits)
for j := 0; j < elemBits; j++ {
bitIdx := ((j + randBitStart) % elemBits)
if (bA.elems[elemIdx] & (1 << uint(bitIdx))) > 0 {
return 64*uint(elemIdx) + uint(bitIdx), true
2014-09-14 15:37:32 -07:00
}
}
}
}
return 0, false
2014-09-14 15:37:32 -07:00
}
2014-10-15 20:15:38 -07:00
func (bA BitArray) String() string {
return bA.StringWithIndent("")
}
func (bA BitArray) StringWithIndent(indent string) string {
lines := []string{}
bits := ""
for i := uint(0); i < bA.bits; i++ {
if bA.GetIndex(i) {
2014-10-15 20:15:38 -07:00
bits += "X"
} else {
bits += "_"
}
if i%100 == 99 {
lines = append(lines, bits)
bits = ""
}
if i%10 == 9 {
bits += " "
}
if i%50 == 49 {
bits += " "
}
}
if len(bits) > 0 {
lines = append(lines, bits)
}
return fmt.Sprintf("BA{%v:%v}", bA.bits, strings.Join(lines, indent))
2014-10-15 20:15:38 -07:00
}