diff --git a/src/lib/Makefile b/src/lib/Makefile index 9aaa71e8225..8c605cb8803 100644 --- a/src/lib/Makefile +++ b/src/lib/Makefile @@ -45,6 +45,7 @@ DIRS=\ tabwriter\ template\ testing\ + testing/iotest\ time\ unicode\ utf8\ @@ -115,6 +116,7 @@ nuke: nuke.dirs bignum.install: fmt.install bufio.install: io.install os.install +crypto/aes.install: os.install exec.install: os.install strings.install exvar.install: fmt.install http.install log.install strconv.install sync.install flag.install: fmt.install os.install strconv.install @@ -123,7 +125,7 @@ path.install: io.install once.install: sync.install strings.install: utf8.install testing.install: flag.install fmt.install runtime.install - +testing/iotest.install: io.install os.install fmt.install: io.install reflect.install strconv.install GODEPS=fmt.install io.install once.install regexp.install sort.install \ strconv.install strings.install unicode.install utf8.install container/vector.install diff --git a/src/lib/bufio/bufio.go b/src/lib/bufio/bufio.go index 3b4aeb82022..7bfbb089f72 100644 --- a/src/lib/bufio/bufio.go +++ b/src/lib/bufio/bufio.go @@ -421,21 +421,17 @@ func (b *Writer) Flush() os.Error { if b.err != nil { return b.err } - n := 0; - for n < b.n { - m, e := b.wr.Write(b.buf[n:b.n]); - n += m; - if m == 0 && e == nil { - e = io.ErrShortWrite - } - if e != nil { - if n < b.n { - copySlice(b.buf[0:b.n-n], b.buf[n:b.n]) - } - b.n -= n; - b.err = e; - return e + n, e := b.wr.Write(b.buf[0:b.n]); + if n < b.n && e == nil { + e = io.ErrShortWrite; + } + if e != nil { + if n > 0 && n < b.n { + copySlice(b.buf[0:b.n-n], b.buf[n:b.n]) } + b.n -= n; + b.err = e; + return e } b.n = 0; return nil @@ -505,14 +501,14 @@ func (b *Writer) WriteByte(c byte) os.Error { // buffered input and output -// ReadWriter stores (a pointer to) a Reader and a Writer. +// ReadWriter stores pointers to a Reader and a Writer. // It implements io.ReadWriter. type ReadWriter struct { *Reader; *Writer; } -// NewReadWriter allocates a new ReadWriter holding r and w. +// NewReadWriter allocates a new ReadWriter that dispatches to r and w. func NewReadWriter(r *Reader, w *Writer) *ReadWriter { return &ReadWriter{r, w} } diff --git a/src/lib/bufio/bufio_test.go b/src/lib/bufio/bufio_test.go index 240a6d03734..6e5135df782 100644 --- a/src/lib/bufio/bufio_test.go +++ b/src/lib/bufio/bufio_test.go @@ -10,61 +10,9 @@ import ( "io"; "os"; "testing"; + "testing/iotest"; ) -// Should be in language! -func copy(p []byte, q []byte) { - for i := 0; i < len(p); i++ { - p[i] = q[i] - } -} - -// Reads from p. -type byteReader struct { - p []byte -} - -func newByteReader(p []byte) io.Reader { - b := new(byteReader); - b.p = p; - return b -} - -func (b *byteReader) Read(p []byte) (int, os.Error) { - n := len(p); - if n > len(b.p) { - n = len(b.p) - } - copy(p[0:n], b.p[0:n]); - b.p = b.p[n:len(b.p)]; - return n, nil -} - - -// Reads from p but only returns half of what you asked for. -type halfByteReader struct { - p []byte -} - -func newHalfByteReader(p []byte) io.Reader { - b := new(halfByteReader); - b.p = p; - return b -} - -func (b *halfByteReader) Read(p []byte) (int, os.Error) { - n := len(p)/2; - if n == 0 && len(p) > 0 { - n = 1 - } - if n > len(b.p) { - n = len(b.p) - } - copy(p[0:n], b.p[0:n]); - b.p = b.p[n:len(b.p)]; - return n, nil -} - // Reads from a reader and rot13s the result. type rot13Reader struct { r io.Reader @@ -82,24 +30,56 @@ func (r13 *rot13Reader) Read(p []byte) (int, os.Error) { return n, e } for i := 0; i < n; i++ { - if 'a' <= p[i] && p[i] <= 'z' || 'A' <= p[i] && p[i] <= 'Z' { - if 'a' <= p[i] && p[i] <= 'm' || 'A' <= p[i] && p[i] <= 'M' { - p[i] += 13; - } else { - p[i] -= 13; - } + c := p[i] | 0x20; // lowercase byte + if 'a' <= c && c <= 'm' { + p[i] += 13; + } else if 'n' <= c && c <= 'z' { + p[i] -= 13; } } return n, nil } +// Call ReadByte to accumulate the text of a file +func readBytes(buf *Reader) string { + var b [1000]byte; + nb := 0; + for { + c, e := buf.ReadByte(); + if e == io.ErrEOF { + break + } + if e != nil { + panic("Data: "+e.String()) + } + b[nb] = c; + nb++; + } + return string(b[0:nb]) +} + +func TestReaderSimple(t *testing.T) { + data := io.StringBytes("hello world"); + b := NewReader(io.NewByteReader(data)); + if s := readBytes(b); s != "hello world" { + t.Errorf("simple hello world test failed: got %q", s); + } + + b = NewReader(newRot13Reader(io.NewByteReader(data))); + if s := readBytes(b); s != "uryyb jbeyq" { + t.Error("rot13 hello world test failed: got %q", s); + } +} + + type readMaker struct { name string; - fn func([]byte) io.Reader; + fn func(io.Reader) io.Reader; } var readMakers = []readMaker { - readMaker{ "full", func(p []byte) io.Reader { return newByteReader(p) } }, - readMaker{ "half", func(p []byte) io.Reader { return newHalfByteReader(p) } }, + readMaker{ "full", func(r io.Reader) io.Reader { return r } }, + readMaker{ "byte", iotest.OneByteReader }, + readMaker{ "half", iotest.HalfReader }, } // Call ReadLineString (which ends up calling everything else) @@ -119,25 +99,6 @@ func readLines(b *Reader) string { return s } -// Call ReadByte to accumulate the text of a file -func readBytes(buf *Reader) string { - var b [1000]byte; - nb := 0; - for { - c, e := buf.ReadByte(); - if e == io.ErrEOF { - break - } - if e != nil { - panic("GetBytes: "+e.String()) - } - b[nb] = c; - nb++; - } - // BUG return string(b[0:nb]) ? - return string(b[0:nb]) -} - // Call Read to accumulate the text of a file func reads(buf *Reader, m int) string { var b [1000]byte; @@ -172,18 +133,6 @@ var bufsizes = []int { 23, 32, 46, 64, 93, 128, 1024, 4096 } -func TestReaderSimple(t *testing.T) { - b := NewReader(newByteReader(io.StringBytes("hello world"))); - if s := readBytes(b); s != "hello world" { - t.Errorf("simple hello world test failed: got %q", s); - } - - b = NewReader(newRot13Reader(newByteReader(io.StringBytes("hello world")))); - if s := readBytes(b); s != "uryyb jbeyq" { - t.Error("rot13 hello world test failed: got %q", s); - } -} - func TestReader(t *testing.T) { var texts [31]string; str := ""; @@ -204,7 +153,7 @@ func TestReader(t *testing.T) { readmaker := readMakers[i]; bufreader := bufreaders[j]; bufsize := bufsizes[k]; - read := readmaker.fn(textbytes); + read := readmaker.fn(io.NewByteReader(textbytes)); buf, e := NewReaderSize(read, bufsize); s := bufreader.fn(buf); if s != text { @@ -217,122 +166,92 @@ func TestReader(t *testing.T) { } } -type writeBuffer interface { - Write(p []byte) (int, os.Error); - GetBytes() []byte -} - -// Accumulates bytes into a byte array. -type byteWriter struct { - p []byte; - n int -} - -func newByteWriter() writeBuffer { - return new(byteWriter) -} - -func (w *byteWriter) Write(p []byte) (int, os.Error) { - if w.p == nil { - w.p = make([]byte, len(p)+100) - } else if w.n + len(p) >= len(w.p) { - newp := make([]byte, len(w.p)*2 + len(p)); - copy(newp[0:w.n], w.p[0:w.n]); - w.p = newp - } - copy(w.p[w.n:w.n+len(p)], p); - w.n += len(p); - return len(p), nil -} - -func (w *byteWriter) GetBytes() []byte { - return w.p[0:w.n] -} - -// Accumulates bytes written into a byte array -// but Write only takes half of what you give it. -// TODO: Could toss this -- Write() is not supposed to do that. -type halfByteWriter struct { - bw writeBuffer -} - -func newHalfByteWriter() writeBuffer { - w := new(halfByteWriter); - w.bw = newByteWriter(); - return w -} - -func (w *halfByteWriter) Write(p []byte) (int, os.Error) { - n := (len(p)+1) / 2; - // BUG return w.bw.Write(p[0:n]) - r, e := w.bw.Write(p[0:n]); - return r, e -} - -func (w *halfByteWriter) GetBytes() []byte { - return w.bw.GetBytes() -} - -type writeMaker struct { - name string; - fn func()writeBuffer; -} func TestWriter(t *testing.T) { var data [8192]byte; - var writers = []writeMaker { - writeMaker{ "full", newByteWriter }, - writeMaker{ "half", newHalfByteWriter }, - }; - for i := 0; i < len(data); i++ { data[i] = byte(' '+ i%('~'-' ')); } + w := new(io.ByteBuffer); for i := 0; i < len(bufsizes); i++ { for j := 0; j < len(bufsizes); j++ { - for k := 0; k < len(writers); k++ { - nwrite := bufsizes[i]; - bs := bufsizes[j]; + nwrite := bufsizes[i]; + bs := bufsizes[j]; - // Write nwrite bytes using buffer size bs. - // Check that the right amount makes it out - // and that the data is correct. + // Write nwrite bytes using buffer size bs. + // Check that the right amount makes it out + // and that the data is correct. - write := writers[k].fn(); - buf, e := NewWriterSize(write, bs); - context := fmt.Sprintf("write=%s nwrite=%d bufsize=%d", writers[k].name, nwrite, bs); - if e != nil { - t.Errorf("%s: NewWriterSize %d: %v", context, bs, e); - continue; - } - n, e1 := buf.Write(data[0:nwrite]); - if e1 != nil || n != nwrite { - t.Errorf("%s: buf.Write %d = %d, %v", context, nwrite, n, e1); - continue; - } - if e = buf.Flush(); e != nil { - t.Errorf("%s: buf.Flush = %v", context, e); - } + w.Reset(); + buf, e := NewWriterSize(w, bs); + context := fmt.Sprintf("nwrite=%d bufsize=%d", nwrite, bs); + if e != nil { + t.Errorf("%s: NewWriterSize %d: %v", context, bs, e); + continue; + } + n, e1 := buf.Write(data[0:nwrite]); + if e1 != nil || n != nwrite { + t.Errorf("%s: buf.Write %d = %d, %v", context, nwrite, n, e1); + continue; + } + if e = buf.Flush(); e != nil { + t.Errorf("%s: buf.Flush = %v", context, e); + } - written := write.GetBytes(); - if len(written) != nwrite { - t.Errorf("%s: %d bytes written", context, len(written)); - } - for l := 0; l < len(written); l++ { - if written[i] != data[i] { - t.Errorf("%s: wrong bytes written"); - t.Errorf("want=%s", data[0:len(written)]); - t.Errorf("have=%s", written); - } + written := w.Data(); + if len(written) != nwrite { + t.Errorf("%s: %d bytes written", context, len(written)); + } + for l := 0; l < len(written); l++ { + if written[i] != data[i] { + t.Errorf("%s: wrong bytes written"); + t.Errorf("want=%s", data[0:len(written)]); + t.Errorf("have=%s", written); } } } } } +// Check that write errors are returned properly. + +type errorWriterTest struct { + n, m int; + err os.Error; + expect os.Error; +} + +func (w errorWriterTest) Write(p []byte) (int, os.Error) { + return len(p)*w.n/w.m, w.err; +} + +var errorWriterTests = []errorWriterTest { + errorWriterTest{ 0, 1, nil, io.ErrShortWrite }, + errorWriterTest{ 1, 2, nil, io.ErrShortWrite }, + errorWriterTest{ 1, 1, nil, nil }, + errorWriterTest{ 0, 1, os.EPIPE, os.EPIPE }, + errorWriterTest{ 1, 2, os.EPIPE, os.EPIPE }, + errorWriterTest{ 1, 1, os.EPIPE, os.EPIPE }, +} + +func TestWriteErrors(t *testing.T) { + for i, w := range errorWriterTests { + buf := NewWriter(w); + n, e := buf.Write(io.StringBytes("hello world")); + if e != nil { + t.Errorf("Write hello to %v: %v", w, e); + continue; + } + e = buf.Flush(); + if e != w.expect { + t.Errorf("Flush %v: got %v, wanted %v", w, e, w.expect); + } + } +} + func TestNewReaderSizeIdempotent(t *testing.T) { const BufSize = 1000; - b, err := NewReaderSize(newByteReader(io.StringBytes("hello world")), BufSize); + b, err := NewReaderSize(io.NewByteReader(io.StringBytes("hello world")), BufSize); if err != nil { t.Error("NewReaderSize create fail", err); } @@ -356,7 +275,7 @@ func TestNewReaderSizeIdempotent(t *testing.T) { func TestNewWriterSizeIdempotent(t *testing.T) { const BufSize = 1000; - b, err := NewWriterSize(newByteWriter(), BufSize); + b, err := NewWriterSize(new(io.ByteBuffer), BufSize); if err != nil { t.Error("NewWriterSize create fail", err); } diff --git a/src/lib/io/bytebuffer.go b/src/lib/io/bytebuffer.go index c862818fd77..921ddb17aea 100644 --- a/src/lib/io/bytebuffer.go +++ b/src/lib/io/bytebuffer.go @@ -42,13 +42,17 @@ func (b *ByteBuffer) Len() int { // Truncate discards all but the first n unread bytes from the buffer. // It is an error to call b.Truncate(n) with n > b.Len(). func (b *ByteBuffer) Truncate(n int) { + if n == 0 { + // Reuse buffer space. + b.off = 0; + } b.buf = b.buf[0 : b.off + n]; } // Reset resets the buffer so it has no content. // b.Reset() is the same as b.Truncate(0). func (b *ByteBuffer) Reset() { - b.buf = b.buf[0 : b.off]; + b.Truncate(0); } // Write appends the contents of p to the buffer. The return diff --git a/src/lib/io/io.go b/src/lib/io/io.go index 70c82d56293..91b6ffd8b9b 100644 --- a/src/lib/io/io.go +++ b/src/lib/io/io.go @@ -185,3 +185,29 @@ func Copy(src Reader, dst Writer) (written int64, err os.Error) { } return written, err } + +// A ByteReader satisfies Reads by consuming data from a slice of bytes. +// Clients can call NewByteReader to create one or wrap pointers +// to their own slices: r := ByteReader{&data}. +type ByteReader struct { + Data *[]byte +} + +func (r ByteReader) Read(p []byte) (int, os.Error) { + n := len(p); + b := r.Data; + if n > len(b) { + n = len(b); + } + for i := 0; i < n; i++ { + p[i] = b[i]; + } + *b = b[n:len(b)]; + return n, nil; +} + +// NewByteReader returns a new ByteReader reading from data. +func NewByteReader(data []byte) ByteReader { + return ByteReader{ &data }; +} + diff --git a/src/lib/testing/iotest/Makefile b/src/lib/testing/iotest/Makefile new file mode 100644 index 00000000000..5bfa513c735 --- /dev/null +++ b/src/lib/testing/iotest/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/testing/ + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + reader.$O\ + + +phases: a1 +_obj$D/iotest.a: phases + +a1: $(O1) + $(AR) grc _obj$D/iotest.a reader.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/iotest.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg$D/iotest.a + +packages: _obj$D/iotest.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/iotest.a $(GOROOT)/pkg$D/iotest.a diff --git a/src/lib/testing/iotest/reader.go b/src/lib/testing/iotest/reader.go new file mode 100644 index 00000000000..0bb8633388a --- /dev/null +++ b/src/lib/testing/iotest/reader.go @@ -0,0 +1,44 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The iotest package implements Readers and Writers +// useful only for testing. +package iotest + +import ( + "io"; + "os"; +) + +type oneByteReader struct { + r io.Reader; +} + +func (r *oneByteReader) Read(p []byte) (int, os.Error) { + if len(p) == 0 { + return 0, nil; + } + return r.r.Read(p[0:1]); +} + +// OneByteReader returns a Reader that implements +// each non-empty Read by reading one byte from r. +func OneByteReader(r io.Reader) io.Reader { + return &oneByteReader{r}; +} + +type halfReader struct { + r io.Reader; +} + +func (r *halfReader) Read(p []byte) (int, os.Error) { + return r.r.Read(p[0:(len(p)+1)/2]); +} + +// HalfReader returns a Reader that implements Read +// by reading half as many requested bytes from r. +func HalfReader(r io.Reader) io.Reader { + return &halfReader{r}; +} +