Skip to content

Commit

Permalink
bufiox.NewReader/NewReaderSize: support Seek
Browse files Browse the repository at this point in the history
  • Loading branch information
xushiwei committed Apr 12, 2020
1 parent 6a6c8b7 commit 679d5a8
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
29 changes: 29 additions & 0 deletions bufiox/bufio.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bufiox
import (
"bufio"
"bytes"
"errors"
"io"
"unsafe"
)
Expand Down Expand Up @@ -67,3 +68,31 @@ func ReadAll(b *bufio.Reader) (ret []byte, err error) {
}

// -----------------------------------------------------------------------------

func getUnderlyingReader(b *bufio.Reader) io.Reader {
r := (*reader)(unsafe.Pointer(b))
return r.rd
}

// ErrSeekUnsupported error.
var ErrSeekUnsupported = errors.New("bufio: the underlying reader doesn't support seek")

// Seek sets the offset for the next Read or Write to offset, interpreted
// according to whence: SeekStart means relative to the start of the file,
// SeekCurrent means relative to the current offset, and SeekEnd means
// relative to the end. Seek returns the new offset relative to the start
// of the file and an error, if any.
//
func Seek(b *bufio.Reader, offset int64, whence int) (int64, error) {
r := getUnderlyingReader(b)
if seeker, ok := r.(io.Seeker); ok {
newoff, err := seeker.Seek(offset, whence)
if err == nil {
b.Reset(r)
}
return newoff, err
}
return 0, ErrSeekUnsupported
}

// -----------------------------------------------------------------------------
76 changes: 76 additions & 0 deletions bufiox/bufio_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package bufiox

import (
"bufio"
"io"
"os"
"strings"
"testing"
"unsafe"
)

// -------------------------------------------------------------------------------------

func TestReaderSize(t *testing.T) {
b1 := NewReaderBuffer(nil)
size1 := unsafe.Sizeof(*b1)
b2 := bufio.NewReader(os.Stdin)
size2 := unsafe.Sizeof(*b2)
if size1 != size2 {
t.Fatal("TestReaderSize: sizeof(bufiox.Reader) != sizeof(bufio.Reader)")
}
b, err := ReadAll(b1)
if err != nil || b != nil {
t.Fatal("ReadAll failed:", err, b)
}
}

func TestGetUnderlyingReaderAndSeek(t *testing.T) {
r := strings.NewReader("Hello, china!!!")
b := bufio.NewReader(r)
if getUnderlyingReader(b) != r {
t.Fatal("getUnderlyingReader failed")
}
b.ReadByte()
r1 := getUnderlyingReader(b)
b1 := bufio.NewReader(r1)
if _, err1 := b1.ReadByte(); err1 != io.EOF {
t.Fatal("bufio.NewReader cache?")
}
newoff, err := Seek(b, 7, io.SeekStart)
if err != nil || newoff != 7 {
t.Fatal("Seek failed:", err, newoff)
}
china, err := b.ReadString('!')
if err != nil {
t.Fatal("ReadString failed:", err)
}
if china != "china!" {
t.Fatal("Seek failed:", china)
}
}

// -------------------------------------------------------------------------------------

func TestSeeker(t *testing.T) {
r := strings.NewReader("Hello, china!!!")
b := NewReader(r)
var rdseeker io.ReadSeeker = b
rdseeker.Seek(7, io.SeekStart)
china, err := b.ReadString('!')
if err != nil {
t.Fatal("ReadString failed:", err)
}
if china != "china!" {
t.Fatal("Seek failed:", china)
}
b.Seek(0, io.SeekStart)

b2 := NewReaderSize(r, 64)
data, err := ReadAll(&b2.Reader)
if err != nil || string(data) != "Hello, china!!!" {
t.Fatal("ReadAll failed:", err, data)
}
}

// -------------------------------------------------------------------------------------
40 changes: 40 additions & 0 deletions bufiox/seek.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package bufiox

import (
"bufio"
"io"
)

// -----------------------------------------------------------------------------

// Reader class.
type Reader struct {
bufio.Reader
}

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.ReadSeeker) *Reader {
r := bufio.NewReader(rd)
return &Reader{Reader: *r}
}

// NewReaderSize returns a new Reader whose buffer has at least the specified
// size. If the argument io.Reader is already a Reader with large enough size,
// it returns the underlying Reader.
//
func NewReaderSize(rd io.ReadSeeker, size int) *Reader {
r := bufio.NewReaderSize(rd, size)
return &Reader{Reader: *r}
}

// Seek sets the offset for the next Read or Write to offset, interpreted
// according to whence: SeekStart means relative to the start of the file,
// SeekCurrent means relative to the current offset, and SeekEnd means
// relative to the end. Seek returns the new offset relative to the start
// of the file and an error, if any.
//
func (p *Reader) Seek(offset int64, whence int) (int64, error) {
return Seek(&p.Reader, offset, whence)
}

// -----------------------------------------------------------------------------

0 comments on commit 679d5a8

Please sign in to comment.