diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-04-26 09:55:32 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-04-26 09:55:32 +0200 |
commit | 7b15ed9ef455b6b66c6b376898a88aef5d6a9970 (patch) | |
tree | 3ef530baa80cdf29436ba981f5783be6b4d2202b /src/pkg/bufio | |
parent | 50104cc32a498f7517a51c8dc93106c51c7a54b4 (diff) | |
download | golang-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.go | 95 | ||||
-rw-r--r-- | src/pkg/bufio/bufio_test.go | 134 |
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) + } +} |