Rename SpotReader, remove cruft, and move helpers
This commit is contained in:
95
cursor/cursor.go
Normal file
95
cursor/cursor.go
Normal file
@ -0,0 +1,95 @@
|
||||
package cursor
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Cursor reads data from a specific spot in a data source.
|
||||
type Cursor[Datum any] interface {
|
||||
// I almost parameterized Cursor by its implementation (i.e. the Curiously
|
||||
// Recurring Template Pattern), but then each parser would need that parameter.
|
||||
// That might work well in a language with much stronger type inference, but
|
||||
// not in Go. The upside would have been that for each implementation Impl,
|
||||
// Impl.Read could have returned an unboxed Impl, which would have slightly
|
||||
// simplified testing and maybe slightly reduced allocs.
|
||||
|
||||
// Read fill dst with data from this Cursor's position in the underlying
|
||||
// source. It returns the number of data it read and a new Cursor for
|
||||
// the position at which the read ended, or an error if the read failed.
|
||||
// All calls to a given Cursor will return data from the same position.
|
||||
// If n < len(dst), Read will return an error explaining why it read fewer
|
||||
// bytes than requested. If Read tried to read past the end of the source,
|
||||
// err will be io.EOF.
|
||||
Read(dst []Datum) (n uint64, next Cursor[Datum], err error)
|
||||
|
||||
// Pos returns the Cursor's position within the source.
|
||||
Pos() uint64
|
||||
}
|
||||
|
||||
type SliceCursor[Datum any] struct {
|
||||
data []Datum
|
||||
offset uint64
|
||||
}
|
||||
|
||||
func NewSlice[Datum any]([]Datum) SliceCursor[Datum] { panic("not implemented") }
|
||||
|
||||
func (sc SliceCursor[Datum]) Read(dst []Datum) (n uint64, next Cursor[Datum], err error) {
|
||||
copied := copy(dst, sc.data[sc.offset:])
|
||||
if copied < len(dst) {
|
||||
err = io.EOF
|
||||
}
|
||||
n = uint64(copied)
|
||||
sc.offset += n
|
||||
return n, sc, err
|
||||
}
|
||||
|
||||
func (sc SliceCursor[Datum]) Pos() uint64 {
|
||||
return sc.offset
|
||||
}
|
||||
|
||||
type ReaderAtCursor struct {
|
||||
r io.ReaderAt
|
||||
pos uint64
|
||||
}
|
||||
|
||||
func NewReaderAt(r io.ReaderAt) ReaderAtCursor {
|
||||
return ReaderAtCursor{r: r}
|
||||
}
|
||||
|
||||
func (rac ReaderAtCursor) Read(dst []byte) (uint64, Cursor[byte], error) {
|
||||
n, err := rac.r.ReadAt(dst, int64(rac.pos))
|
||||
if n > 0 {
|
||||
rac.pos += uint64(n)
|
||||
}
|
||||
return uint64(n), rac, err
|
||||
}
|
||||
|
||||
func (rac ReaderAtCursor) Pos() uint64 {
|
||||
return rac.pos
|
||||
}
|
||||
|
||||
// StringCursor is identical to SliceCursor[byte], but uses a string as its data source.
|
||||
// The advantage is that creating a StringCursor does not require copying the source
|
||||
// string into a []byte.
|
||||
type StringCursor struct {
|
||||
source string
|
||||
offset uint64
|
||||
}
|
||||
|
||||
func NewString(s string) StringCursor {
|
||||
return StringCursor{source: s}
|
||||
}
|
||||
|
||||
func (sc StringCursor) Read(dst []byte) (n uint64, next Cursor[byte], err error) {
|
||||
copied := copy(dst, sc.source[sc.offset:])
|
||||
if copied < len(dst) {
|
||||
err = io.EOF
|
||||
}
|
||||
n = uint64(copied)
|
||||
sc.offset += n
|
||||
return n, sc, err
|
||||
}
|
||||
|
||||
func (sc StringCursor) Pos() uint64 {
|
||||
return sc.offset
|
||||
}
|
38
cursor/helper.go
Normal file
38
cursor/helper.go
Normal file
@ -0,0 +1,38 @@
|
||||
package cursor
|
||||
|
||||
import "io"
|
||||
|
||||
// BufferedReaderAt uses a buffer to supplement an io.Reader
|
||||
// with limited backward seeking.
|
||||
type BufferedReaderAt struct{}
|
||||
|
||||
func NewBufferedReaderAt(r io.Reader, minBuffer uint64) *BufferedReaderAt
|
||||
|
||||
// ReadAt reads bytes from the underlying reader. If the offset is after
|
||||
// the end of the buffer, ReadAt will first read and ignore bytes from the
|
||||
// underlying reader until it reaches the offset. If the offset is
|
||||
// before the start of the buffer, ReadAt will return an error.
|
||||
//
|
||||
// If your parser needs unlimited lookahead, you should probably
|
||||
// just read the whole input into a slice and use BytesCursor.
|
||||
func (b *BufferedReaderAt) ReadAt(dst []byte, offset int64) (int, error)
|
||||
|
||||
// RuneReader is an io.RuneReader backed by a Cursor, for compatibility
|
||||
// with the regexp package.
|
||||
type RuneReader struct {
|
||||
cursor Cursor[byte]
|
||||
}
|
||||
|
||||
func NewRuneReader(c Cursor[byte]) *RuneReader {
|
||||
return &RuneReader{cursor: c}
|
||||
}
|
||||
|
||||
func (r *RuneReader) Read(dst []byte) (int, error) {
|
||||
n, c, err := r.cursor.Read(dst)
|
||||
r.cursor = c
|
||||
return int(n), err
|
||||
}
|
||||
|
||||
func (r *RuneReader) Cursor() Cursor[byte] {
|
||||
return r.cursor
|
||||
}
|
Reference in New Issue
Block a user