summaryrefslogtreecommitdiff
path: root/src/pkg/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/io')
-rw-r--r--src/pkg/io/Makefile3
-rw-r--r--src/pkg/io/io.go25
-rw-r--r--src/pkg/io/io_test.go76
-rw-r--r--src/pkg/io/ioutil/Makefile2
-rw-r--r--src/pkg/io/ioutil/ioutil.go2
-rw-r--r--src/pkg/io/ioutil/ioutil_test.go12
-rw-r--r--src/pkg/io/ioutil/tempfile_test.go2
-rw-r--r--src/pkg/io/multi.go60
-rw-r--r--src/pkg/io/multi_test.go88
-rw-r--r--src/pkg/io/pipe.go27
-rw-r--r--src/pkg/io/pipe_test.go12
11 files changed, 278 insertions, 31 deletions
diff --git a/src/pkg/io/Makefile b/src/pkg/io/Makefile
index 8c27ce551..9786002f0 100644
--- a/src/pkg/io/Makefile
+++ b/src/pkg/io/Makefile
@@ -2,11 +2,12 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../Make.$(GOARCH)
+include ../../Make.inc
TARG=io
GOFILES=\
io.go\
+ multi.go\
pipe.go\
include ../../Make.pkg
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index dcdc883b1..1a6eca95a 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -19,6 +19,9 @@ type Error struct {
// but failed to return an explicit error.
var ErrShortWrite os.Error = &Error{"short write"}
+// ErrShortBuffer means that a read required a longer buffer than was provided.
+var ErrShortBuffer os.Error = &Error{"short buffer"}
+
// ErrUnexpectedEOF means that os.EOF was encountered in the
// middle of reading a fixed-size block or data structure.
var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"}
@@ -165,8 +168,11 @@ func WriteString(w Writer, s string) (n int, err os.Error) {
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading fewer than min bytes,
// ReadAtLeast returns ErrUnexpectedEOF.
+// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
- n = 0
+ if len(buf) < min {
+ return 0, ErrShortBuffer
+ }
for n < min {
nn, e := r.Read(buf[n:])
if nn > 0 {
@@ -179,7 +185,7 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
return n, e
}
}
- return n, nil
+ return
}
// ReadFull reads exactly len(buf) bytes from r into buf.
@@ -197,10 +203,15 @@ func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) {
- // If the writer has a ReadFrom method, use it to to do the copy.
+ // If the writer has a ReadFrom method, use it to do the copy.
// Avoids a buffer allocation and a copy.
if rt, ok := dst.(ReaderFrom); ok {
- return rt.ReadFrom(LimitReader(src, n))
+ written, err = rt.ReadFrom(LimitReader(src, n))
+ if written < n && err == nil {
+ // rt stopped early; must have been EOF.
+ err = os.EOF
+ }
+ return
}
buf := make([]byte, 32*1024)
for written < n {
@@ -240,12 +251,12 @@ func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) {
// Otherwise, if src implements the WriterTo interface,
// the copy is implemented by calling src.WriteTo(dst).
func Copy(dst Writer, src Reader) (written int64, err os.Error) {
- // If the writer has a ReadFrom method, use it to to do the copy.
+ // If the writer has a ReadFrom method, use it to do the copy.
// Avoids an allocation and a copy.
if rt, ok := dst.(ReaderFrom); ok {
return rt.ReadFrom(src)
}
- // Similarly, if the reader has a WriteTo method, use it to to do the copy.
+ // Similarly, if the reader has a WriteTo method, use it to do the copy.
if wt, ok := src.(WriterTo); ok {
return wt.WriteTo(dst)
}
@@ -336,7 +347,7 @@ func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error)
case 2:
offset += s.limit
}
- if offset < s.off || offset > s.limit {
+ if offset < s.base || offset > s.limit {
return 0, os.EINVAL
}
s.off = offset
diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go
index 4ad1e5951..4fcd85e69 100644
--- a/src/pkg/io/io_test.go
+++ b/src/pkg/io/io_test.go
@@ -7,6 +7,8 @@ package io_test
import (
"bytes"
. "io"
+ "os"
+ "strings"
"testing"
)
@@ -78,3 +80,77 @@ func TestCopynWriteTo(t *testing.T) {
t.Errorf("Copyn did not work properly")
}
}
+
+type noReadFrom struct {
+ w Writer
+}
+
+func (w *noReadFrom) Write(p []byte) (n int, err os.Error) {
+ return w.w.Write(p)
+}
+
+func TestCopynEOF(t *testing.T) {
+ // Test that EOF behavior is the same regardless of whether
+ // argument to Copyn has ReadFrom.
+
+ b := new(bytes.Buffer)
+
+ n, err := Copyn(&noReadFrom{b}, strings.NewReader("foo"), 3)
+ if n != 3 || err != nil {
+ t.Errorf("Copyn(noReadFrom, foo, 3) = %d, %v; want 3, nil", n, err)
+ }
+
+ n, err = Copyn(&noReadFrom{b}, strings.NewReader("foo"), 4)
+ if n != 3 || err != os.EOF {
+ t.Errorf("Copyn(noReadFrom, foo, 4) = %d, %v; want 3, EOF", n, err)
+ }
+
+ n, err = Copyn(b, strings.NewReader("foo"), 3) // b has read from
+ if n != 3 || err != nil {
+ t.Errorf("Copyn(bytes.Buffer, foo, 3) = %d, %v; want 3, nil", n, err)
+ }
+
+ n, err = Copyn(b, strings.NewReader("foo"), 4) // b has read from
+ if n != 3 || err != os.EOF {
+ t.Errorf("Copyn(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err)
+ }
+}
+
+func TestReadAtLeast(t *testing.T) {
+ var rb bytes.Buffer
+ rb.Write([]byte("0123"))
+ buf := make([]byte, 2)
+ n, err := ReadAtLeast(&rb, buf, 2)
+ if err != nil {
+ t.Error(err)
+ }
+ n, err = ReadAtLeast(&rb, buf, 4)
+ if err != ErrShortBuffer {
+ t.Errorf("expected ErrShortBuffer got %v", err)
+ }
+ if n != 0 {
+ t.Errorf("expected to have read 0 bytes, got %v", n)
+ }
+ n, err = ReadAtLeast(&rb, buf, 1)
+ if err != nil {
+ t.Error(err)
+ }
+ if n != 2 {
+ t.Errorf("expected to have read 2 bytes, got %v", n)
+ }
+ n, err = ReadAtLeast(&rb, buf, 2)
+ if err != os.EOF {
+ t.Errorf("expected EOF, got %v", err)
+ }
+ if n != 0 {
+ t.Errorf("expected to have read 0 bytes, got %v", n)
+ }
+ rb.Write([]byte("4"))
+ n, err = ReadAtLeast(&rb, buf, 2)
+ if err != ErrUnexpectedEOF {
+ t.Errorf("expected ErrUnexpectedEOF, got %v", err)
+ }
+ if n != 1 {
+ t.Errorf("expected to have read 1 bytes, got %v", n)
+ }
+}
diff --git a/src/pkg/io/ioutil/Makefile b/src/pkg/io/ioutil/Makefile
index 77b75bcec..d406d4b7d 100644
--- a/src/pkg/io/ioutil/Makefile
+++ b/src/pkg/io/ioutil/Makefile
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../../Make.$(GOARCH)
+include ../../../Make.inc
TARG=io/ioutil
GOFILES=\
diff --git a/src/pkg/io/ioutil/ioutil.go b/src/pkg/io/ioutil/ioutil.go
index d33869380..fb3fdcda1 100644
--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -49,7 +49,7 @@ func ReadFile(filename string) ([]byte, os.Error) {
// WriteFile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it with permissions perm;
// otherwise WriteFile truncates it before writing.
-func WriteFile(filename string, data []byte, perm int) os.Error {
+func WriteFile(filename string, data []byte, perm uint32) os.Error {
f, err := os.Open(filename, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, perm)
if err != nil {
return err
diff --git a/src/pkg/io/ioutil/ioutil_test.go b/src/pkg/io/ioutil/ioutil_test.go
index ecbf41ca6..150ee6d63 100644
--- a/src/pkg/io/ioutil/ioutil_test.go
+++ b/src/pkg/io/ioutil/ioutil_test.go
@@ -37,7 +37,7 @@ func TestReadFile(t *testing.T) {
}
func TestWriteFile(t *testing.T) {
- filename := "_obj/rumpelstilzchen"
+ filename := "_test/rumpelstilzchen"
data := "Programming today is a race between software engineers striving to " +
"build bigger and better idiot-proof programs, and the Universe trying " +
"to produce bigger and better idiots. So far, the Universe is winning."
@@ -74,19 +74,19 @@ func TestReadDir(t *testing.T) {
}
foundTest := false
- foundObj := false
+ foundTestDir := false
for _, dir := range list {
switch {
case dir.IsRegular() && dir.Name == "ioutil_test.go":
foundTest = true
- case dir.IsDirectory() && dir.Name == "_obj":
- foundObj = true
+ case dir.IsDirectory() && dir.Name == "_test":
+ foundTestDir = true
}
}
if !foundTest {
t.Fatalf("ReadDir %s: test file not found", dirname)
}
- if !foundObj {
- t.Fatalf("ReadDir %s: _obj directory not found", dirname)
+ if !foundTestDir {
+ t.Fatalf("ReadDir %s: _test directory not found", dirname)
}
}
diff --git a/src/pkg/io/ioutil/tempfile_test.go b/src/pkg/io/ioutil/tempfile_test.go
index fe43f9566..d949a86cf 100644
--- a/src/pkg/io/ioutil/tempfile_test.go
+++ b/src/pkg/io/ioutil/tempfile_test.go
@@ -23,7 +23,7 @@ func TestTempFile(t *testing.T) {
t.Errorf("TempFile(dir, `ioutil_test`) = %v, %v", f, err)
}
if f != nil {
- re := testing.MustCompile("^" + regexp.QuoteMeta(dir) + "/ioutil_test[0-9]+$")
+ re := regexp.MustCompile("^" + regexp.QuoteMeta(dir) + "/ioutil_test[0-9]+$")
if !re.MatchString(f.Name()) {
t.Errorf("TempFile(`"+dir+"`, `ioutil_test`) created bad name %s", f.Name())
}
diff --git a/src/pkg/io/multi.go b/src/pkg/io/multi.go
new file mode 100644
index 000000000..88e4f1b76
--- /dev/null
+++ b/src/pkg/io/multi.go
@@ -0,0 +1,60 @@
+// Copyright 2010 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.
+
+package io
+
+import "os"
+
+type multiReader struct {
+ readers []Reader
+}
+
+func (mr *multiReader) Read(p []byte) (n int, err os.Error) {
+ for len(mr.readers) > 0 {
+ n, err = mr.readers[0].Read(p)
+ if n > 0 || err != os.EOF {
+ if err == os.EOF {
+ // This shouldn't happen.
+ // Well-behaved Readers should never
+ // return non-zero bytes read with an
+ // EOF. But if so, we clean it.
+ err = nil
+ }
+ return
+ }
+ mr.readers = mr.readers[1:]
+ }
+ return 0, os.EOF
+}
+
+// MultiReader returns a Reader that's the logical concatenation of
+// the provided input readers. They're read sequentially. Once all
+// inputs are drained, Read will return os.EOF.
+func MultiReader(readers ...Reader) Reader {
+ return &multiReader{readers}
+}
+
+type multiWriter struct {
+ writers []Writer
+}
+
+func (t *multiWriter) Write(p []byte) (n int, err os.Error) {
+ for _, w := range t.writers {
+ n, err = w.Write(p)
+ if err != nil {
+ return
+ }
+ if n != len(p) {
+ err = ErrShortWrite
+ return
+ }
+ }
+ return len(p), nil
+}
+
+// MultiWriter creates a writer that duplicates its writes to all the
+// provided writers, similar to the Unix tee(1) command.
+func MultiWriter(writers ...Writer) Writer {
+ return &multiWriter{writers}
+}
diff --git a/src/pkg/io/multi_test.go b/src/pkg/io/multi_test.go
new file mode 100644
index 000000000..3ecb7c75d
--- /dev/null
+++ b/src/pkg/io/multi_test.go
@@ -0,0 +1,88 @@
+// Copyright 2010 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.
+
+package io_test
+
+import (
+ . "io"
+ "bytes"
+ "crypto/sha1"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+)
+
+func TestMultiReader(t *testing.T) {
+ var mr Reader
+ var buf []byte
+ nread := 0
+ withFooBar := func(tests func()) {
+ r1 := strings.NewReader("foo ")
+ r2 := strings.NewReader("bar")
+ mr = MultiReader(r1, r2)
+ buf = make([]byte, 20)
+ tests()
+ }
+ expectRead := func(size int, expected string, eerr os.Error) {
+ nread++
+ n, gerr := mr.Read(buf[0:size])
+ if n != len(expected) {
+ t.Errorf("#%d, expected %d bytes; got %d",
+ nread, len(expected), n)
+ }
+ got := string(buf[0:n])
+ if got != expected {
+ t.Errorf("#%d, expected %q; got %q",
+ nread, expected, got)
+ }
+ if gerr != eerr {
+ t.Errorf("#%d, expected error %v; got %v",
+ nread, eerr, gerr)
+ }
+ buf = buf[n:]
+ }
+ withFooBar(func() {
+ expectRead(2, "fo", nil)
+ expectRead(5, "o ", nil)
+ expectRead(5, "bar", nil)
+ expectRead(5, "", os.EOF)
+ })
+ withFooBar(func() {
+ expectRead(4, "foo ", nil)
+ expectRead(1, "b", nil)
+ expectRead(3, "ar", nil)
+ expectRead(1, "", os.EOF)
+ })
+ withFooBar(func() {
+ expectRead(5, "foo ", nil)
+ })
+}
+
+func TestMultiWriter(t *testing.T) {
+ sha1 := sha1.New()
+ sink := new(bytes.Buffer)
+ mw := MultiWriter(sha1, sink)
+
+ sourceString := "My input text."
+ source := strings.NewReader(sourceString)
+ written, err := Copy(mw, source)
+
+ if written != int64(len(sourceString)) {
+ t.Errorf("short write of %d, not %d", written, len(sourceString))
+ }
+
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+
+ sha1hex := fmt.Sprintf("%x", sha1.Sum())
+ if sha1hex != "01cb303fa8c30a64123067c5aa6284ba7ec2d31b" {
+ t.Error("incorrect sha1 value")
+ }
+
+ if sink.String() != sourceString {
+ t.Errorf("expected %q; got %q", sourceString, sink.String())
+ }
+}
diff --git a/src/pkg/io/pipe.go b/src/pkg/io/pipe.go
index 898526921..df76418b9 100644
--- a/src/pkg/io/pipe.go
+++ b/src/pkg/io/pipe.go
@@ -52,6 +52,13 @@ func (p *pipe) run() {
case <-p.done:
if ndone++; ndone == 2 {
// both reader and writer are gone
+ // close out any existing i/o
+ if r1 == nil {
+ p.r2 <- pipeResult{0, os.EINVAL}
+ }
+ if w1 == nil {
+ p.w2 <- pipeResult{0, os.EINVAL}
+ }
return
}
continue
@@ -89,6 +96,11 @@ func (p *pipe) run() {
p.r2 <- pipeResult{0, werr}
continue
}
+ if rerr != nil {
+ // read end is closed
+ p.r2 <- pipeResult{0, os.EINVAL}
+ continue
+ }
r1 = nil // disable Read until this one is done
case wb = <-w1:
if rerr != nil {
@@ -96,6 +108,11 @@ func (p *pipe) run() {
p.w2 <- pipeResult{0, rerr}
continue
}
+ if werr != nil {
+ // write end is closed
+ p.w2 <- pipeResult{0, os.EINVAL}
+ continue
+ }
w1 = nil // disable Write until this one is done
}
@@ -275,20 +292,14 @@ func Pipe() (*PipeReader, *PipeWriter) {
r.c2 = p.r2
r.cclose = p.rclose
r.done = p.done
- // TODO(rsc): Should be able to write
- // runtime.SetFinalizer(r, (*PipeReader).finalizer)
- // but 6g doesn't see the finalizer method.
- runtime.SetFinalizer(&r.pipeHalf, (*pipeHalf).finalizer)
+ runtime.SetFinalizer(r, (*PipeReader).finalizer)
w := new(PipeWriter)
w.c1 = p.w1
w.c2 = p.w2
w.cclose = p.wclose
w.done = p.done
- // TODO(rsc): Should be able to write
- // runtime.SetFinalizer(w, (*PipeWriter).finalizer)
- // but 6g doesn't see the finalizer method.
- runtime.SetFinalizer(&w.pipeHalf, (*pipeHalf).finalizer)
+ runtime.SetFinalizer(w, (*PipeWriter).finalizer)
return r, w
}
diff --git a/src/pkg/io/pipe_test.go b/src/pkg/io/pipe_test.go
index 902d7a0a3..bd4b94f0a 100644
--- a/src/pkg/io/pipe_test.go
+++ b/src/pkg/io/pipe_test.go
@@ -157,12 +157,12 @@ func (p pipeTest) String() string {
}
var pipeTests = []pipeTest{
- pipeTest{true, nil, false},
- pipeTest{true, nil, true},
- pipeTest{true, ErrShortWrite, true},
- pipeTest{false, nil, false},
- pipeTest{false, nil, true},
- pipeTest{false, ErrShortWrite, true},
+ {true, nil, false},
+ {true, nil, true},
+ {true, ErrShortWrite, true},
+ {false, nil, false},
+ {false, nil, true},
+ {false, ErrShortWrite, true},
}
func delayClose(t *testing.T, cl closer, ch chan int, tt pipeTest) {