1
1
mirror of https://github.com/wader/fq.git synced 2024-09-19 15:57:29 +03:00
fq/internal/aheadreadseeker/aheadreadseeker.go
Mattias Wadman 684a083813 interp,decode: Support decode group argument
Will be used for probing via -d <string> and filename
2023-05-03 10:01:13 +02:00

97 lines
1.7 KiB
Go

package aheadreadseeker
import (
"errors"
"io"
)
// TODO: smarter cache? cache behind too somehow?
func min64(a, b int64) int64 {
if a < b {
return a
}
return b
}
type Reader struct {
rs io.ReadSeeker
minRead int
offset int64
cache []byte
cacheOffset int64
cacheUsed int
}
func New(rs io.ReadSeeker, minRead int) *Reader {
return &Reader{
rs: rs,
minRead: minRead,
}
}
func (r *Reader) Read(p []byte) (n int, err error) {
for {
if r.offset >= r.cacheOffset && r.offset < r.cacheOffset+int64(r.cacheUsed) {
d := r.offset - r.cacheOffset
copyLen := min64(int64(r.cacheUsed)-d, int64(len(p)))
copy(p[0:copyLen], r.cache[d:d+copyLen])
r.offset += copyLen
return int(copyLen), nil
}
readBytes := len(p)
if readBytes < r.minRead {
readBytes = r.minRead
}
if readBytes > len(r.cache) {
r.cache = make([]byte, readBytes)
}
n, err := io.ReadFull(r.rs, r.cache[0:readBytes])
r.cacheOffset = r.offset
r.cacheUsed = n
if n == 0 || (!errors.Is(err, io.ErrUnexpectedEOF) && errors.Is(err, io.EOF)) {
return 0, err
}
}
}
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
var absOff int64
var err error
switch whence {
case io.SeekStart:
absOff = offset
case io.SeekCurrent:
absOff = r.offset + offset
case io.SeekEnd:
absOff, err = r.rs.Seek(offset, whence)
if err != nil {
return 0, err
}
}
if absOff >= r.cacheOffset && absOff < r.cacheOffset+int64(r.cacheUsed) {
r.offset = absOff
return absOff, nil
}
_, err = r.rs.Seek(absOff, io.SeekStart)
if err != nil {
return 0, err
}
r.offset = absOff
r.cacheOffset = 0
r.cacheUsed = 0
return absOff, nil
}
func (r *Reader) Unwrap() any {
return r.rs
}