summaryrefslogtreecommitdiff
path: root/src/pkg/bufio
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-04-26 09:55:32 +0200
committerOndřej Surý <ondrej@sury.org>2011-04-26 09:55:32 +0200
commit7b15ed9ef455b6b66c6b376898a88aef5d6a9970 (patch)
tree3ef530baa80cdf29436ba981f5783be6b4d2202b /src/pkg/bufio
parent50104cc32a498f7517a51c8dc93106c51c7a54b4 (diff)
downloadgolang-7b15ed9ef455b6b66c6b376898a88aef5d6a9970.tar.gz
Imported Upstream version 2011.04.13upstream/2011.04.13
Diffstat (limited to 'src/pkg/bufio')
-rw-r--r--src/pkg/bufio/bufio.go95
-rw-r--r--src/pkg/bufio/bufio_test.go134
2 files changed, 184 insertions, 45 deletions
diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go
index eae5c5ce9..32a25afae 100644
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -282,6 +282,33 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) {
panic("not reached")
}
+// ReadLine tries to return a single line, not including the end-of-line bytes.
+// If the line was too long for the buffer then isPrefix is set and the
+// beginning of the line is returned. The rest of the line will be returned
+// from future calls. isPrefix will be false when returning the last fragment
+// of the line. The returned buffer is only valid until the next call to
+// ReadLine. ReadLine either returns a non-nil line or it returns an error,
+// never both.
+func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
+ line, err = b.ReadSlice('\n')
+ if err == ErrBufferFull {
+ return line, true, nil
+ }
+
+ if len(line) == 0 {
+ return
+ }
+ err = nil
+
+ if line[len(line)-1] == '\n' {
+ line = line[:len(line)-1]
+ }
+ if len(line) > 0 && line[len(line)-1] == '\r' {
+ line = line[:len(line)-1]
+ }
+ return
+}
+
// ReadBytes reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and including the delimiter.
// If ReadBytes encounters an error before finding a delimiter,
@@ -415,38 +442,27 @@ func (b *Writer) Buffered() int { return b.n }
// If nn < len(p), it also returns an error explaining
// why the write is short.
func (b *Writer) Write(p []byte) (nn int, err os.Error) {
- if b.err != nil {
- return 0, b.err
- }
- nn = 0
- for len(p) > 0 {
- n := b.Available()
- if n <= 0 {
- if b.Flush(); b.err != nil {
- break
- }
- n = b.Available()
- }
- if b.Buffered() == 0 && len(p) >= len(b.buf) {
+ for len(p) > b.Available() && b.err == nil {
+ var n int
+ if b.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, b.err = b.wr.Write(p)
- nn += n
- p = p[n:]
- if b.err != nil {
- break
- }
- continue
+ } else {
+ n = copy(b.buf[b.n:], p)
+ b.n += n
+ b.Flush()
}
- if n > len(p) {
- n = len(p)
- }
- copy(b.buf[b.n:b.n+n], p[0:n])
- b.n += n
nn += n
p = p[n:]
}
- return nn, b.err
+ if b.err != nil {
+ return nn, b.err
+ }
+ n := copy(b.buf[b.n:], p)
+ b.n += n
+ nn += n
+ return nn, nil
}
// WriteByte writes a single byte.
@@ -496,24 +512,21 @@ func (b *Writer) WriteRune(rune int) (size int, err os.Error) {
// If the count is less than len(s), it also returns an error explaining
// why the write is short.
func (b *Writer) WriteString(s string) (int, os.Error) {
- if b.err != nil {
- return 0, b.err
- }
- // Common case, worth making fast.
- if b.Available() >= len(s) || len(b.buf) >= len(s) && b.Flush() == nil {
- for i := 0; i < len(s); i++ { // loop over bytes, not runes.
- b.buf[b.n] = s[i]
- b.n++
- }
- return len(s), nil
+ nn := 0
+ for len(s) > b.Available() && b.err == nil {
+ n := copy(b.buf[b.n:], s)
+ b.n += n
+ nn += n
+ s = s[n:]
+ b.Flush()
}
- for i := 0; i < len(s); i++ { // loop over bytes, not runes.
- b.WriteByte(s[i])
- if b.err != nil {
- return i, b.err
- }
+ if b.err != nil {
+ return nn, b.err
}
- return len(s), nil
+ n := copy(b.buf[b.n:], s)
+ b.n += n
+ nn += n
+ return nn, nil
}
// buffered input and output
diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go
index 059ca6dd2..123adac29 100644
--- a/src/pkg/bufio/bufio_test.go
+++ b/src/pkg/bufio/bufio_test.go
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package bufio
+package bufio_test
import (
+ . "bufio"
"bytes"
"fmt"
"io"
+ "io/ioutil"
"os"
"strings"
"testing"
@@ -502,9 +504,8 @@ func TestWriteString(t *testing.T) {
b.WriteString("7890") // easy after flush
b.WriteString("abcdefghijklmnopqrstuvwxy") // hard
b.WriteString("z")
- b.Flush()
- if b.err != nil {
- t.Error("WriteString", b.err)
+ if err := b.Flush(); err != nil {
+ t.Error("WriteString", err)
}
s := "01234567890abcdefghijklmnopqrstuvwxyz"
if string(buf.Bytes()) != s {
@@ -570,3 +571,128 @@ func TestPeekThenUnreadRune(t *testing.T) {
r.UnreadRune()
r.ReadRune() // Used to panic here
}
+
+var testOutput = []byte("0123456789abcdefghijklmnopqrstuvwxy")
+var testInput = []byte("012\n345\n678\n9ab\ncde\nfgh\nijk\nlmn\nopq\nrst\nuvw\nxy")
+var testInputrn = []byte("012\r\n345\r\n678\r\n9ab\r\ncde\r\nfgh\r\nijk\r\nlmn\r\nopq\r\nrst\r\nuvw\r\nxy\r\n\n\r\n")
+
+// TestReader wraps a []byte and returns reads of a specific length.
+type testReader struct {
+ data []byte
+ stride int
+}
+
+func (t *testReader) Read(buf []byte) (n int, err os.Error) {
+ n = t.stride
+ if n > len(t.data) {
+ n = len(t.data)
+ }
+ if n > len(buf) {
+ n = len(buf)
+ }
+ copy(buf, t.data)
+ t.data = t.data[n:]
+ if len(t.data) == 0 {
+ err = os.EOF
+ }
+ return
+}
+
+func testReadLine(t *testing.T, input []byte) {
+ //for stride := 1; stride < len(input); stride++ {
+ for stride := 1; stride < 2; stride++ {
+ done := 0
+ reader := testReader{input, stride}
+ l, _ := NewReaderSize(&reader, len(input)+1)
+ for {
+ line, isPrefix, err := l.ReadLine()
+ if len(line) > 0 && err != nil {
+ t.Errorf("ReadLine returned both data and error: %s", err)
+ }
+ if isPrefix {
+ t.Errorf("ReadLine returned prefix")
+ }
+ if err != nil {
+ if err != os.EOF {
+ t.Fatalf("Got unknown error: %s", err)
+ }
+ break
+ }
+ if want := testOutput[done : done+len(line)]; !bytes.Equal(want, line) {
+ t.Errorf("Bad line at stride %d: want: %x got: %x", stride, want, line)
+ }
+ done += len(line)
+ }
+ if done != len(testOutput) {
+ t.Errorf("ReadLine didn't return everything: got: %d, want: %d (stride: %d)", done, len(testOutput), stride)
+ }
+ }
+}
+
+func TestReadLine(t *testing.T) {
+ testReadLine(t, testInput)
+ testReadLine(t, testInputrn)
+}
+
+func TestLineTooLong(t *testing.T) {
+ buf := bytes.NewBuffer([]byte("aaabbbcc\n"))
+ l, _ := NewReaderSize(buf, 3)
+ line, isPrefix, err := l.ReadLine()
+ if !isPrefix || !bytes.Equal(line, []byte("aaa")) || err != nil {
+ t.Errorf("bad result for first line: %x %s", line, err)
+ }
+ line, isPrefix, err = l.ReadLine()
+ if !isPrefix || !bytes.Equal(line, []byte("bbb")) || err != nil {
+ t.Errorf("bad result for second line: %x", line)
+ }
+ line, isPrefix, err = l.ReadLine()
+ if isPrefix || !bytes.Equal(line, []byte("cc")) || err != nil {
+ t.Errorf("bad result for third line: %x", line)
+ }
+ line, isPrefix, err = l.ReadLine()
+ if isPrefix || err == nil {
+ t.Errorf("expected no more lines: %x %s", line, err)
+ }
+}
+
+func TestReadAfterLines(t *testing.T) {
+ line1 := "line1"
+ restData := "line2\nline 3\n"
+ inbuf := bytes.NewBuffer([]byte(line1 + "\n" + restData))
+ outbuf := new(bytes.Buffer)
+ maxLineLength := len(line1) + len(restData)/2
+ l, _ := NewReaderSize(inbuf, maxLineLength)
+ line, isPrefix, err := l.ReadLine()
+ if isPrefix || err != nil || string(line) != line1 {
+ t.Errorf("bad result for first line: isPrefix=%v err=%v line=%q", isPrefix, err, string(line))
+ }
+ n, err := io.Copy(outbuf, l)
+ if int(n) != len(restData) || err != nil {
+ t.Errorf("bad result for Read: n=%d err=%v", n, err)
+ }
+ if outbuf.String() != restData {
+ t.Errorf("bad result for Read: got %q; expected %q", outbuf.String(), restData)
+ }
+}
+
+func TestReadEmptyBuffer(t *testing.T) {
+ l, _ := NewReaderSize(bytes.NewBuffer(nil), 10)
+ line, isPrefix, err := l.ReadLine()
+ if err != os.EOF {
+ t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
+ }
+}
+
+func TestLinesAfterRead(t *testing.T) {
+ l, _ := NewReaderSize(bytes.NewBuffer([]byte("foo")), 10)
+ _, err := ioutil.ReadAll(l)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ line, isPrefix, err := l.ReadLine()
+ if err != os.EOF {
+ t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
+ }
+}