Initial QSfera import
This commit is contained in:
+133
@@ -0,0 +1,133 @@
|
||||
package streams
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// BufferedReadSeeker wraps an io.ReadSeeker to provide buffering.
|
||||
// It implements the io.ReadSeeker interface.
|
||||
type BufferedReadSeeker struct {
|
||||
reader *bufio.Reader
|
||||
seeker io.ReadSeeker
|
||||
}
|
||||
|
||||
// NewBufferedReadSeeker creates a new BufferedReadSeeker with a default buffer size.
|
||||
func NewBufferedReadSeeker(rs io.ReadSeeker) *BufferedReadSeeker {
|
||||
return &BufferedReadSeeker{
|
||||
reader: bufio.NewReader(rs),
|
||||
seeker: rs,
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads data into p. It reads from the underlying buffered reader.
|
||||
func (brs *BufferedReadSeeker) Read(p []byte) (n int, err error) {
|
||||
return brs.reader.Read(p)
|
||||
}
|
||||
|
||||
// Seek sets the offset for the next Read. It is optimized to use the
|
||||
// buffer for seeks that land within the buffered data range.
|
||||
func (brs *BufferedReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||
// Determine the current position (where the next Read would start)
|
||||
underlyingPos, err := brs.seeker.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// The position of the stream as seen by clients
|
||||
logicalPos := underlyingPos - int64(brs.reader.Buffered())
|
||||
|
||||
// 2. Calculate the absolute target position for the seek
|
||||
var absTargetPos int64
|
||||
switch whence {
|
||||
case io.SeekStart:
|
||||
absTargetPos = offset
|
||||
case io.SeekCurrent:
|
||||
absTargetPos = logicalPos + offset
|
||||
case io.SeekEnd:
|
||||
// Seeking from the end requires a fallback, as we don't know the end
|
||||
// position without invalidating the buffer's state relative to the seeker.
|
||||
return brs.fallbackSeek(offset, whence)
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid whence: %d", whence)
|
||||
}
|
||||
|
||||
// 3. Check if the target position is within the current buffer
|
||||
if absTargetPos >= logicalPos && absTargetPos < underlyingPos {
|
||||
// The target is within the buffer. Calculate how many bytes to discard.
|
||||
bytesToDiscard := absTargetPos - logicalPos
|
||||
_, err := brs.reader.Discard(int(bytesToDiscard))
|
||||
if err != nil {
|
||||
// This is unlikely, but if Discard fails, fall back to a full seek
|
||||
return brs.fallbackSeek(offset, whence)
|
||||
}
|
||||
return absTargetPos, nil
|
||||
}
|
||||
|
||||
// 4. If the target is outside the buffer, perform a fallback seek
|
||||
return brs.fallbackSeek(absTargetPos, io.SeekStart)
|
||||
}
|
||||
|
||||
// fallbackSeek performs a seek on the underlying seeker and resets the buffer.
|
||||
func (brs *BufferedReadSeeker) fallbackSeek(offset int64, whence int) (int64, error) {
|
||||
newOffset, err := brs.seeker.Seek(offset, whence)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
brs.reader.Reset(brs.seeker)
|
||||
return newOffset, nil
|
||||
}
|
||||
|
||||
// Run the callback function with a buffered reader that supports Seek() and
|
||||
// Read(). Return an io.Reader that represents all content from the original
|
||||
// io.Reader.
|
||||
func CallbackWithSeekable(r io.Reader, callback func(io.Reader) error) (stream io.Reader, err error) {
|
||||
switch s := r.(type) {
|
||||
case io.ReadSeeker:
|
||||
pos, err := s.Seek(0, io.SeekCurrent)
|
||||
if err == nil {
|
||||
defer func() {
|
||||
_, serr := s.Seek(pos, io.SeekStart)
|
||||
if err == nil {
|
||||
err = serr
|
||||
}
|
||||
}()
|
||||
// Add bufferring to s for efficiency
|
||||
bs := s
|
||||
switch r.(type) {
|
||||
case *BufferedReadSeeker, *bytes.Reader:
|
||||
default:
|
||||
bs = NewBufferedReadSeeker(s)
|
||||
}
|
||||
err = callback(bs)
|
||||
return s, err
|
||||
}
|
||||
case *bytes.Buffer:
|
||||
err = callback(bytes.NewReader(s.Bytes()))
|
||||
return s, err
|
||||
}
|
||||
rewindBuffer := &bytes.Buffer{}
|
||||
tee := io.TeeReader(r, rewindBuffer)
|
||||
err = callback(bufio.NewReader(tee))
|
||||
return io.MultiReader(rewindBuffer, r), err
|
||||
}
|
||||
|
||||
// Skip reading the specified number of bytes efficiently
|
||||
func Skip(r io.Reader, amt int64) (err error) {
|
||||
if s, ok := r.(io.Seeker); ok {
|
||||
if _, serr := s.Seek(amt, io.SeekCurrent); serr == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = io.CopyN(io.Discard, r, amt)
|
||||
return
|
||||
}
|
||||
|
||||
// Read a single byte from the reader
|
||||
func ReadByte(r io.Reader) (ans byte, err error) {
|
||||
var v [1]byte
|
||||
_, err = io.ReadFull(r, v[:])
|
||||
ans = v[0]
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user