1
1
mirror of https://github.com/wader/fq.git synced 2024-11-26 10:33:53 +03:00
fq/pkg/bitio/ioreader.go
2022-02-09 01:35:47 +01:00

76 lines
1.5 KiB
Go

package bitio
import (
"errors"
"io"
)
// IOReader is a io.Reader and io.ByteReader that reads from a bitio.Reader.
// Unaligned byte at EOF will be zero bit padded.
type IOReader struct {
r Reader
rErr error
b Buffer
}
// NewIOReader returns a new bitio.IOReader.
func NewIOReader(r Reader) *IOReader {
return &IOReader{r: r}
}
func (r *IOReader) Read(p []byte) (n int, err error) {
var ns int64
for {
// uses p even if returning nothing, io.Reader docs says:
// "it may use all of p as scratch space during the call"
if r.rErr == nil {
var rn int64
rn, err = r.r.ReadBits(p, int64(len(p))*8)
r.rErr = err
ns += rn
_, err = r.b.WriteBits(p, rn)
if err != nil {
return 0, err
}
}
if r.b.Len() >= 8 {
// read whole bytes
rBits := int64(len(p)) * 8
bBits := r.b.Len()
aBits := bBits - bBits%8
if rBits > aBits {
rBits = aBits
}
rn, rErr := r.b.ReadBits(p, rBits)
if rErr != nil {
return int(rn / 8), rErr
}
return int(rn / 8), nil
} else if r.rErr != nil {
if errors.Is(r.rErr, io.EOF) && r.b.Len() > 0 {
// TODO: hmm io.Buffer does this
if len(p) == 0 {
return 0, nil
}
_, err := r.b.ReadBits(p, r.b.Len())
if err != nil {
return 0, err
}
return 1, r.rErr
}
return 0, r.rErr
}
}
}
// required to make some readers like deflate not do their own buffering
func (r *IOReader) ReadByte() (byte, error) {
var rb [1]byte
_, err := r.Read(rb[:])
return rb[0], err
}