mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-02 02:59:00 -05:00
Bumps [github.com/kovidgoyal/imaging](https://github.com/kovidgoyal/imaging) from 1.6.4 to 1.7.2. - [Release notes](https://github.com/kovidgoyal/imaging/releases) - [Changelog](https://github.com/kovidgoyal/imaging/blob/master/.goreleaser.yaml) - [Commits](https://github.com/kovidgoyal/imaging/compare/v1.6.4...v1.7.2) --- updated-dependencies: - dependency-name: github.com/kovidgoyal/imaging dependency-version: 1.7.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
134 lines
3.8 KiB
Go
134 lines
3.8 KiB
Go
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
|
|
}
|