Files

117 lines
2.2 KiB
Go

package exiftool
import (
"bytes"
"errors"
"io"
)
// MarkReader reads from upstream and stops at marker boundaries.
type MarkReader struct {
upstream io.Reader
buf []byte
mark []byte
// valid data: buf[start:pending]
// pending, need to check if contains mark: buf[pending:end]
start int
pending int
end int
// if true, mark begins from buf[pending]
hasMark bool
// if true, upstream is EOF
upstreamEOF bool
// if true, this reader returns EOF
paused bool
}
// NewMarkReader creates a reader that stops at each marker and requires Reset to continue.
func NewMarkReader(upstream io.Reader, bufferSize int, mark string) (*MarkReader, error) {
if bufferSize < 2*len(mark) {
return nil, errors.New("buffer too small")
}
return &MarkReader{
upstream: upstream,
buf: make([]byte, bufferSize),
mark: []byte(mark),
}, nil
}
// Reset resumes reading after a previously encountered marker boundary.
func (r *MarkReader) Reset() {
r.paused = false
}
// Read returns bytes up to (but excluding) the next marker, then reports EOF until Reset.
func (r *MarkReader) Read(p []byte) (int, error) {
if len(p) == 0 {
return 0, nil
}
if r.paused {
return 0, io.EOF
}
if r.start == r.pending {
// compact buffer
copy(r.buf[0:], r.buf[r.start:r.end])
r.end -= r.start
r.pending -= r.start
r.start = 0
// fill buffer
n, err := io.ReadAtLeast(r.upstream, r.buf[r.end:], len(r.mark))
r.end += n
if err == io.EOF || err == io.ErrUnexpectedEOF {
r.upstreamEOF = true
err = nil
}
if err != nil {
return 0, err
}
r.checkMarkInPending()
}
// read from valid data
n := copy(p, r.buf[r.start:r.pending])
r.start += n
if r.start == r.pending {
switch {
case r.hasMark:
r.pending += len(r.mark)
r.start += len(r.mark)
r.hasMark = false
r.paused = true
r.checkMarkInPending()
case r.upstreamEOF:
r.paused = r.start == r.end
}
}
if r.paused {
return n, io.EOF
}
return n, nil
}
func (r *MarkReader) checkMarkInPending() {
if markAt := bytes.Index(r.buf[r.pending:r.end], r.mark); markAt >= 0 {
r.hasMark = true
r.pending = r.pending + markAt
return
}
if r.upstreamEOF {
r.pending = r.end
return
}
r.pending = max(r.pending, r.end-len(r.mark)+1)
}