lotus/lib/rlepluslazy/rleplus.go

111 lines
1.9 KiB
Go
Raw Normal View History

package rlepluslazy
import (
"encoding/binary"
"errors"
"fmt"
bitvector "github.com/filecoin-project/go-lotus/lib/rlepluslazy/internal"
"golang.org/x/xerrors"
)
const Version = 0
var (
ErrWrongVersion = errors.New("invalid RLE+ version")
ErrDecode = fmt.Errorf("invalid encoding for RLE+ version %d", Version)
)
type RLE struct {
vec *bitvector.BitVector
}
func FromBuf(buf []byte) (*RLE, error) {
rle := &RLE{vec: bitvector.NewBitVector(buf, bitvector.LSB0)}
if err := rle.check(); err != nil {
return nil, xerrors.Errorf("could not create RLE+ for a buffer: %w", err)
}
return rle, nil
}
func (rle *RLE) check() error {
ver := rle.vec.Take(0, 2, bitvector.LSB0)
if ver != Version {
return ErrWrongVersion
}
return nil
}
func (rle *RLE) Iterator() (*iterator, error) {
vit := rle.vec.Iterator(bitvector.LSB0)
vit(2) // Take version
it := &iterator{next: vit}
if err := it.prep(vit(1)); err != nil {
return nil, err
}
return it, nil
}
type iterator struct {
next func(uint) byte
curIdx uint64
rep uint64
}
func (it *iterator) HasNext() bool {
return it.rep != 0
}
func (it *iterator) prep(curBit byte) error {
loop:
for it.rep == 0 {
x := it.next(1)
switch x {
case 1:
it.rep = 1
case 0:
y := it.next(1)
switch y {
case 1:
it.rep = uint64(it.next(4))
case 0:
var buf = make([]byte, 0, 10)
for {
b := it.next(8)
buf = append(buf, b)
if b&0x80 == 0 {
break
}
if len(buf) > 10 {
return xerrors.Errorf("run too long: %w", ErrDecode)
}
}
it.rep, _ = binary.Uvarint(buf)
}
// run with 0 length means end
if it.rep == 0 {
break loop
}
}
if curBit == 0 {
curBit = 1
it.curIdx = it.curIdx + it.rep
it.rep = 0
}
}
return nil
}
func (it *iterator) Next() (uint64, error) {
it.rep--
res := it.curIdx
it.curIdx++
return res, it.prep(0)
}