111 lines
1.9 KiB
Go
111 lines
1.9 KiB
Go
|
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)
|
||
|
}
|