You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

155 lines
3.1 KiB

package common
import (
"io"
"math"
"math/rand"
. "github.com/tendermint/tendermint/binary"
)
// Not goroutine safe
type BitArray []uint64
func NewBitArray(length uint) BitArray {
return BitArray(make([]uint64, (length+63)/64))
}
func ReadBitArray(r io.Reader, n *int64, err *error) BitArray {
lengthTotal := ReadUInt32(r, n, err)
lengthWritten := ReadUInt32(r, n, err)
if *err != nil {
return nil
}
buf := make([]uint64, int(lengthTotal))
for i := uint32(0); i < lengthWritten; i++ {
buf[i] = ReadUInt64(r, n, err)
if err != nil {
return nil
}
}
return BitArray(buf)
}
func (bA BitArray) WriteTo(w io.Writer) (n int64, err error) {
// Count the last element > 0.
lastNonzeroIndex := -1
for i, elem := range bA {
if elem > 0 {
lastNonzeroIndex = i
}
}
WriteUInt32(w, uint32(len(bA)), &n, &err)
WriteUInt32(w, uint32(lastNonzeroIndex+1), &n, &err)
for i, elem := range bA {
if i > lastNonzeroIndex {
break
}
WriteUInt64(w, elem, &n, &err)
}
return
}
func (bA BitArray) GetIndex(i uint) bool {
return bA[i/64]&uint64(1<<(i%64)) > 0
}
func (bA BitArray) SetIndex(i uint, v bool) {
if v {
bA[i/64] |= uint64(1 << (i % 64))
} else {
bA[i/64] &= ^uint64(1 << (i % 64))
}
}
func (bA BitArray) Copy() BitArray {
c := make([]uint64, len(bA))
copy(c, bA)
return BitArray(c)
}
func (bA BitArray) Or(o BitArray) BitArray {
c := bA.Copy()
for i, _ := range c {
c[i] = o[i] | c[i]
}
return c
}
func (bA BitArray) And(o BitArray) BitArray {
c := bA.Copy()
for i, _ := range c {
c[i] = o[i] & c[i]
}
return c
}
func (bA BitArray) Not() BitArray {
c := bA.Copy()
for i, _ := range c {
c[i] = ^c[i]
}
return c
}
func (bA BitArray) Sub(o BitArray) BitArray {
return bA.And(o.Not())
}
// NOTE: returns counts or a longer int slice as necessary.
func (bA BitArray) AddToCounts(counts []int) []int {
for bytei := 0; bytei < len(bA); bytei++ {
for biti := 0; biti < 64; biti++ {
if (bA[bytei] & (1 << uint(biti))) == 0 {
continue
}
index := 64*bytei + biti
if len(counts) <= index {
counts = append(counts, make([]int, (index-len(counts)+1))...)
}
counts[index]++
}
}
return counts
}
func (bA BitArray) PickRandom() (int, bool) {
randStart := rand.Intn(len(bA))
for i := 0; i < len(bA); i++ {
bytei := ((i + randStart) % len(bA))
if bA[bytei] > 0 {
randBitStart := rand.Intn(64)
for j := 0; j < 64; j++ {
biti := ((j + randBitStart) % 64)
//fmt.Printf("%X %v %v %v\n", iHas, j, biti, randBitStart)
if (bA[bytei] & (1 << uint(biti))) > 0 {
return 64*int(bytei) + int(biti), true
}
}
panic("should not happen")
}
}
return 0, false
}
// Pick an index from this BitArray that is 1 && whose count is lowest.
func (bA BitArray) PickRarest(counts []int) (rarest int, ok bool) {
smallestCount := math.MaxInt32
for bytei := 0; bytei < len(bA); bytei++ {
if bA[bytei] > 0 {
for biti := 0; biti < 64; biti++ {
if (bA[bytei] & (1 << uint(biti))) == 0 {
continue
}
index := 64*bytei + biti
if counts[index] < smallestCount {
smallestCount = counts[index]
rarest = index
ok = true
}
}
panic("should not happen")
}
}
return
}