status-go/vendor/github.com/yeqown/reedsolomon/binary/binary.go

273 lines
5.4 KiB
Go
Raw Normal View History

// Package binary ...
// thanks to https://github.com/skip2/go-qrcode/blob/master/bitset/bitset.go
// I cannot do any better for now, so I just learn and write it again~
package binary
import (
"bytes"
"fmt"
"log"
)
const (
byteTrue byte = '1'
byteFalse byte = '0'
)
var (
// format string
format = "Binary length: %d, bits: %s"
)
// New ...
func New(booleans ...bool) *Binary {
b := &Binary{
bits: make([]byte, 0),
lenBits: 0,
}
b.AppendBools(booleans...)
return b
}
// NewFromBinaryString ... generate Bitset from binary string
// auto get length
func NewFromBinaryString(s string) (*Binary, error) {
var n = len(s) / 8
if len(s)%8 != 0 {
n++
}
b := &Binary{
bits: make([]byte, n), // prealloc memory, reducing useless space
lenBits: 0,
}
for _, c := range s {
switch c {
case '1':
b.AppendBools(true)
case '0':
b.AppendBools(false)
case ' ':
// skip space blank
continue
default:
err := fmt.Errorf("invalid char %c in NewFromBinaryString", c)
return nil, err
}
}
return b, nil
}
// Binary struct contains bits stream and methods to be called from outside
// exsample:
// b.Len()
// b.Subset(start, end)
// b.At(pos)
type Binary struct {
bits []byte // 1byte = 8bit
lenBits int // len(bits) * 8
}
// ensureCapacity ensures the Bitset can store an additional |numBits|.
//
// The underlying array is expanded if necessary. To prevent frequent
// reallocation, expanding the underlying array at least doubles its capacity.
//
// then no need to use append ~ will no panic (out of range)
func (b *Binary) ensureCapacity(numBits int) {
numBits += b.lenBits
newNumBytes := numBits / 8
if numBits%8 != 0 {
newNumBytes++
}
// if larger enough
if len(b.bits) >= newNumBytes {
return
}
// larger capcity, about 3 times of current capcity
b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...)
}
// At .get boolean value from
func (b *Binary) At(pos int) bool {
if pos < 0 || pos >= b.lenBits {
panic("out range of bits")
}
return (b.bits[pos/8]&(0x80>>uint(pos%8)) != 0)
}
// Subset do the same work like slice[start:end]
func (b *Binary) Subset(start, end int) (*Binary, error) {
if start > end || end > b.lenBits {
err := fmt.Errorf("Out of range start=%d end=%d lenBits=%d", start, end, b.lenBits)
return nil, err
}
result := New()
result.ensureCapacity(end - start)
for i := start; i < end; i++ {
if b.At(i) {
result.bits[result.lenBits/8] |= 0x80 >> uint(result.lenBits%8)
}
result.lenBits++
}
return result, nil
}
// Append other bitset link another Bitset to after the b
func (b *Binary) Append(other *Binary) {
b.ensureCapacity(other.Len())
for i := 0; i < other.lenBits; i++ {
if other.At(i) {
b.bits[b.lenBits/8] |= 0x80 >> uint(b.lenBits%8)
}
b.lenBits++
}
}
// AppendUint32 other bitset link another Bitset to after the b
func (b *Binary) AppendUint32(value uint32, numBits int) {
b.ensureCapacity(numBits)
if numBits > 32 {
log.Panicf("numBits %d out of range 0-32", numBits)
}
for i := numBits - 1; i >= 0; i-- {
if value&(1<<uint(i)) != 0 {
b.bits[b.lenBits/8] |= 0x80 >> uint(b.lenBits%8)
}
b.lenBits++
}
}
// AppendBytes ...
func (b *Binary) AppendBytes(byts ...byte) {
for _, byt := range byts {
b.AppendByte(byt, 8)
}
}
// AppendByte ... specified num bits to append
func (b *Binary) AppendByte(byt byte, numBits int) error {
if numBits > 8 || numBits < 0 {
return fmt.Errorf("numBits out of range 0-8")
}
b.ensureCapacity(numBits)
// append bit in byte
for i := numBits - 1; i >= 0; i-- {
// 0x01 << left shift count
// 0x80 >> right shift count
if byt&(0x01<<uint(i)) != 0 {
b.bits[b.lenBits/8] |= 0x80 >> uint(b.lenBits%8)
}
b.lenBits++
}
return nil
}
// AppendBools append multi bool after the bit stream of b
func (b *Binary) AppendBools(booleans ...bool) {
b.ensureCapacity(len(booleans))
for _, bv := range booleans {
if bv {
b.bits[b.lenBits/8] |= 0x80 >> uint(b.lenBits%8)
}
b.lenBits++
}
}
// AppendNumBools appends num bits of value value.
func (b *Binary) AppendNumBools(num int, boolean bool) {
booleans := make([]bool, num)
// if not false just append
if boolean {
for i := 0; i < num; i++ {
booleans[i] = boolean
}
}
b.AppendBools(booleans...)
}
// IterFunc used by func b.VisitAll ...
type IterFunc func(pos int, v bool)
// VisitAll loop the b.bits stream and send value into IterFunc
func (b *Binary) VisitAll(f IterFunc) {
for pos := 0; pos < b.Len(); pos++ {
f(pos, b.At(pos))
}
}
// String for printing
func (b *Binary) String() string {
var (
bitstr []byte
vb byte
)
b.VisitAll(func(pos int, v bool) {
vb = byteFalse
if v {
vb = byteTrue
}
bitstr = append(bitstr, vb)
})
return fmt.Sprintf(format, b.Len(), string(bitstr))
}
// Len ...
func (b *Binary) Len() int {
return b.lenBits
}
// Bytes ...
func (b *Binary) Bytes() []byte {
numBytes := b.lenBits / 8
if b.lenBits%8 != 0 {
numBytes++
}
return b.bits[:numBytes]
}
// EqualTo ...
func (b *Binary) EqualTo(other *Binary) bool {
if b.lenBits != other.lenBits {
return false
}
numByte := b.lenBits / 8
if !bytes.Equal(b.bits[:numByte], other.bits[:numByte]) {
return false
}
for pos := numByte * 8; pos < b.lenBits; pos++ {
if b.At(pos) != other.At(pos) {
return false
}
}
return true
}
// Copy ...
func (b *Binary) Copy() *Binary {
return &Binary{
bits: b.bits,
lenBits: b.lenBits,
}
}