mirror of
https://github.com/wader/fq.git
synced 2024-12-27 07:24:48 +03:00
93 lines
1.7 KiB
Go
93 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
|
||
|
}
|