115 lines
2.9 KiB
Go
115 lines
2.9 KiB
Go
// SPDX-License-Identifier: Unlicense
|
|
|
|
package cursor
|
|
|
|
import (
|
|
"errors"
|
|
"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) or if the cursor's position is at the end of the data source,
|
|
// Read will return an error explaining why it read fewer bytes than requested.
|
|
// If the error was due to the cursor reaching the end of the data 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
|
|
|
|
// At returns a new cursor at the position pos.
|
|
At(pos uint64) Cursor[Datum]
|
|
}
|
|
|
|
type ReaderAt[T any] interface {
|
|
ReadAt(p []T, off int64) (n int, err error)
|
|
}
|
|
|
|
type SliceReaderAt[T any] []T
|
|
|
|
func (s SliceReaderAt[T]) ReadAt(dst []T, off int64) (n int, err error) {
|
|
if off < 0 {
|
|
return 0, errors.New("SliceReaderAt.ReadAt: negative offset")
|
|
}
|
|
if off >= int64(len(s)) {
|
|
return 0, io.EOF
|
|
}
|
|
n = copy(dst, s[off:])
|
|
if n < len(dst) {
|
|
err = io.EOF
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
type SliceCursor[Datum any] struct {
|
|
data []Datum
|
|
offset uint64
|
|
}
|
|
|
|
func NewSlice[Datum any](data []Datum) SliceCursor[Datum] {
|
|
return SliceCursor[Datum]{
|
|
data: data,
|
|
offset: 0,
|
|
}
|
|
}
|
|
|
|
func (sc SliceCursor[Datum]) Read(dst []Datum) (n uint64, next Cursor[Datum], err error) {
|
|
if sc.offset == uint64(len(sc.data)) {
|
|
return 0, sc, io.EOF
|
|
}
|
|
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
|
|
}
|
|
|
|
func (sc SliceCursor[Datum]) At(pos uint64) Cursor[Datum] {
|
|
sc.offset = pos
|
|
return sc
|
|
}
|
|
|
|
type ReaderAtCursor[T any] struct {
|
|
r ReaderAt[T]
|
|
pos uint64
|
|
}
|
|
|
|
func NewReaderAt[T any](r ReaderAt[T]) ReaderAtCursor[T] {
|
|
return ReaderAtCursor[T]{r: r}
|
|
}
|
|
|
|
func (rac ReaderAtCursor[T]) Read(dst []T) (uint64, Cursor[T], 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[T]) Pos() uint64 {
|
|
return rac.pos
|
|
}
|
|
|
|
func (rac ReaderAtCursor[T]) At(pos uint64) Cursor[T] {
|
|
rac.pos = pos
|
|
return rac
|
|
}
|