Commit 9144d875 authored by Robert Griesemer's avatar Robert Griesemer

bufio: make all read functions UnreadByte-friendly

Fixes #7844.

LGTM=crawshaw
R=golang-codereviews, crawshaw
CC=golang-codereviews
https://golang.org/cl/90620045
parent 13ea1fd2
...@@ -274,26 +274,36 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { ...@@ -274,26 +274,36 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
for { for {
// Search buffer. // Search buffer.
if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 { if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 {
line := b.buf[b.r : b.r+i+1] line = b.buf[b.r : b.r+i+1]
b.r += i + 1 b.r += i + 1
return line, nil break
} }
// Pending error? // Pending error?
if b.err != nil { if b.err != nil {
line := b.buf[b.r:b.w] line = b.buf[b.r:b.w]
b.r = b.w b.r = b.w
return line, b.readErr() err = b.readErr()
break
} }
// Buffer full? // Buffer full?
if n := b.Buffered(); n >= len(b.buf) { if n := b.Buffered(); n >= len(b.buf) {
b.r = b.w b.r = b.w
return b.buf, ErrBufferFull line = b.buf
err = ErrBufferFull
break
} }
b.fill() // buffer is not full b.fill() // buffer is not full
} }
// Handle last byte, if any.
if i := len(line) - 1; i >= 0 {
b.lastByte = int(line[i])
}
return
} }
// ReadLine is a low-level line-reading primitive. Most callers should use // ReadLine is a low-level line-reading primitive. Most callers should use
...@@ -309,6 +319,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { ...@@ -309,6 +319,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
// //
// The text returned from ReadLine does not include the line end ("\r\n" or "\n"). // The text returned from ReadLine does not include the line end ("\r\n" or "\n").
// No indication or error is given if the input ends without a final line end. // No indication or error is given if the input ends without a final line end.
// Calling UnreadByte after ReadLine will always unread the last byte read
// (possibly a character belonging to the line end) even if that byte is not
// part of the line returned by ReadLine.
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) { func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
line, err = b.ReadSlice('\n') line, err = b.ReadSlice('\n')
if err == ErrBufferFull { if err == ErrBufferFull {
......
...@@ -348,6 +348,62 @@ func TestUnreadByteMultiple(t *testing.T) { ...@@ -348,6 +348,62 @@ func TestUnreadByteMultiple(t *testing.T) {
} }
} }
func TestUnreadByteOthers(t *testing.T) {
// A list of readers to use in conjuction with UnreadByte.
var readers = []func(*Reader, byte) ([]byte, error){
(*Reader).ReadBytes,
(*Reader).ReadSlice,
func(r *Reader, delim byte) ([]byte, error) {
data, err := r.ReadString(delim)
return []byte(data), err
},
// ReadLine doesn't fit the data/pattern easily
// so we leave it out. It should be covered via
// the ReadSlice test since ReadLine simply calls
// ReadSlice, and it's that function that handles
// the last byte.
}
// Try all readers with UnreadByte.
for rno, read := range readers {
// Some input data that is longer than the minimum reader buffer size.
const n = 10
var buf bytes.Buffer
for i := 0; i < n; i++ {
buf.WriteString("abcdefg")
}
r := NewReaderSize(&buf, minReadBufferSize)
readTo := func(delim byte, want string) {
data, err := read(r, delim)
if err != nil {
t.Fatalf("#%d: unexpected error reading to %c: %v", rno, delim, err)
}
if got := string(data); got != want {
t.Fatalf("#%d: got %q, want %q", rno, got, want)
}
}
// Read the data with occasional UnreadByte calls.
for i := 0; i < n; i++ {
readTo('d', "abcd")
for j := 0; j < 3; j++ {
if err := r.UnreadByte(); err != nil {
t.Fatalf("#%d: unexpected error on UnreadByte: %v", rno, err)
}
readTo('d', "d")
}
readTo('g', "efg")
}
// All data should have been read.
_, err := r.ReadByte()
if err != io.EOF {
t.Errorf("#%d: got error %v; want EOF", rno, err)
}
}
}
// Test that UnreadRune fails if the preceding operation was not a ReadRune. // Test that UnreadRune fails if the preceding operation was not a ReadRune.
func TestUnreadRuneError(t *testing.T) { func TestUnreadRuneError(t *testing.T) {
buf := make([]byte, 3) // All runes in this test are 3 bytes long buf := make([]byte, 3) // All runes in this test are 3 bytes long
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment