From 27e249049e9f303c994927a7ecb837cd5e85c512 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Wed, 4 Aug 2010 09:44:02 +1000 Subject: [PATCH] bufio: introduce Peek. I'll leave whether or not this obsoletes UnreadByte for a future CL. R=r, rsc CC=golang-dev https://golang.org/cl/1912042 --- src/pkg/bufio/bufio.go | 40 +++++++++++++++++++++++++++++-------- src/pkg/bufio/bufio_test.go | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go index e73f082890..37bdea274a 100644 --- a/src/pkg/bufio/bufio.go +++ b/src/pkg/bufio/bufio.go @@ -28,6 +28,7 @@ type Error struct { var ( ErrInvalidUnreadByte os.Error = &Error{"bufio: invalid use of UnreadByte"} ErrBufferFull os.Error = &Error{"bufio: buffer full"} + ErrNegativeCount os.Error = &Error{"bufio: negative count"} errInternal os.Error = &Error{"bufio: internal error"} ) @@ -83,13 +84,11 @@ func NewReader(rd io.Reader) *Reader { // fill reads a new chunk into the buffer. func (b *Reader) fill() { // Slide existing data to beginning. - if b.w > b.r { - copy(b.buf[0:b.w-b.r], b.buf[b.r:b.w]) + if b.r > 0 { + copy(b.buf, b.buf[b.r:b.w]) b.w -= b.r - } else { - b.w = 0 + b.r = 0 } - b.r = 0 // Read new data. n, e := b.rd.Read(b.buf[b.w:]) @@ -99,6 +98,31 @@ func (b *Reader) fill() { } } +// Peek returns the next n bytes without advancing the reader. The bytes stop +// being valid at the next read call. If Peek returns fewer than n bytes, it +// also returns an error explaining why the read is short. The error is +// ErrBufferFull if n is larger than b's buffer size. +func (b *Reader) Peek(n int) ([]byte, os.Error) { + if n < 0 { + return nil, ErrNegativeCount + } + if n > len(b.buf) { + return nil, ErrBufferFull + } + for b.w-b.r < n && b.err == nil { + b.fill() + } + m := b.w - b.r + if m > n { + m = n + } + err := b.err + if m < n && err == nil { + err = ErrBufferFull + } + return b.buf[b.r : b.r+m], err +} + // Read reads data into p. // It returns the number of bytes read into p. // If nn < len(p), also returns an error explaining @@ -129,7 +153,7 @@ func (b *Reader) Read(p []byte) (nn int, err os.Error) { if n > b.w-b.r { n = b.w - b.r } - copy(p[0:n], b.buf[b.r:b.r+n]) + copy(p[0:n], b.buf[b.r:]) p = p[n:] b.r += n b.lastbyte = int(b.buf[b.r-1]) @@ -291,10 +315,10 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { buf := make([]byte, n) n = 0 for i := 0; i < nfull; i++ { - copy(buf[n:n+len(full[i])], full[i]) + copy(buf[n:], full[i]) n += len(full[i]) } - copy(buf[n:n+len(frag)], frag) + copy(buf[n:], frag) return buf, err } diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go index 2279fe3b12..876270fcaa 100644 --- a/src/pkg/bufio/bufio_test.go +++ b/src/pkg/bufio/bufio_test.go @@ -419,3 +419,41 @@ func TestBufferFull(t *testing.T) { t.Errorf("second ReadSlice(,) = %q, %v", line, err) } } + +func TestPeek(t *testing.T) { + p := make([]byte, 10) + buf, _ := NewReaderSize(strings.NewReader("abcdefghij"), 4) + if s, err := buf.Peek(1); string(s) != "a" || err != nil { + t.Fatalf("want %q got %q, err=%v", "a", string(s), err) + } + if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { + t.Fatalf("want %q got %q, err=%v", "abcd", string(s), err) + } + if _, err := buf.Peek(5); err != ErrBufferFull { + t.Fatalf("want ErrBufFull got %v", err) + } + if _, err := buf.Read(p[0:3]); string(p[0:3]) != "abc" || err != nil { + t.Fatalf("want %q got %q, err=%v", "abc", string(p[0:3]), err) + } + if s, err := buf.Peek(1); string(s) != "d" || err != nil { + t.Fatalf("want %q got %q, err=%v", "d", string(s), err) + } + if s, err := buf.Peek(2); string(s) != "de" || err != nil { + t.Fatalf("want %q got %q, err=%v", "de", string(s), err) + } + if _, err := buf.Read(p[0:3]); string(p[0:3]) != "def" || err != nil { + t.Fatalf("want %q got %q, err=%v", "def", string(p[0:3]), err) + } + if s, err := buf.Peek(4); string(s) != "ghij" || err != nil { + t.Fatalf("want %q got %q, err=%v", "ghij", string(s), err) + } + if _, err := buf.Read(p[0:4]); string(p[0:4]) != "ghij" || err != nil { + t.Fatalf("want %q got %q, err=%v", "ghij", string(p[0:3]), err) + } + if s, err := buf.Peek(0); string(s) != "" || err != nil { + t.Fatalf("want %q got %q, err=%v", "", string(s), err) + } + if _, err := buf.Peek(1); err != os.EOF { + t.Fatalf("want EOF got %v", err) + } +}