From cfeaaa27e31f8ffdaf70dc332b482aa53e6d2c25 Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Tue, 3 Sep 2024 16:10:27 -0600 Subject: [PATCH] Implemented (but not tested) cursor.RuneReader --- cursor/cursor.go | 18 ++++++++++++++++++ cursor/cursor_test.go | 1 + cursor/helper.go | 27 ++++++++++++++++++++------- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/cursor/cursor.go b/cursor/cursor.go index 4b9303d..f6ff7d1 100644 --- a/cursor/cursor.go +++ b/cursor/cursor.go @@ -25,6 +25,9 @@ type Cursor[Datum any] interface { // 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 SliceCursor[Datum any] struct { @@ -56,6 +59,11 @@ 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 struct { r io.ReaderAt pos uint64 @@ -77,6 +85,11 @@ func (rac ReaderAtCursor) Pos() uint64 { return rac.pos } +func (rac ReaderAtCursor) At(pos uint64) Cursor[byte] { + rac.pos = pos + return rac +} + // 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. @@ -105,3 +118,8 @@ func (sc StringCursor) Read(dst []byte) (n uint64, next Cursor[byte], err error) func (sc StringCursor) Pos() uint64 { return sc.offset } + +func (sc StringCursor) At(pos uint64) Cursor[byte] { + sc.offset = pos + return sc +} diff --git a/cursor/cursor_test.go b/cursor/cursor_test.go index d963ebe..3b71009 100644 --- a/cursor/cursor_test.go +++ b/cursor/cursor_test.go @@ -73,6 +73,7 @@ func testCursor[C cursor.Cursor[byte]](t *testing.T, makeCursor func([]byte) C) test.ErrorIs(t, err, io.EOF) test.EqOp(t, len(data), int(n)) })) + t.Run("At", Todo) } func TestSliceCursor(t *testing.T) { diff --git a/cursor/helper.go b/cursor/helper.go index ff946f6..1f0547d 100644 --- a/cursor/helper.go +++ b/cursor/helper.go @@ -1,6 +1,11 @@ package cursor -import "io" +import ( + "errors" + "fmt" + "io" + "unicode/utf8" +) // BufferedReaderAt uses a buffer to supplement an io.Reader // with limited backward seeking. @@ -31,12 +36,20 @@ 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 (rr *RuneReader) ReadRune() (r rune, size int, err error) { + var b [4]byte + s := b[:] + n, next, err := rr.cursor.Read(s) + if err != nil && !errors.Is(err, io.EOF) { + rr.cursor = next + return 0, 0, fmt.Errorf("ReadRune: %w", err) + } + s = s[:n] + r, size = utf8.DecodeRune(s) + rr.cursor = rr.cursor.At(rr.cursor.Pos() + uint64(size)) + return r, size, err } -func (r *RuneReader) Cursor() Cursor[byte] { - return r.cursor +func (rr *RuneReader) Cursor() Cursor[byte] { + return rr.cursor }