summaryrefslogtreecommitdiff
path: root/src/pkg/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/io')
-rw-r--r--src/pkg/io/io.go1
-rw-r--r--src/pkg/io/io_test.go20
-rw-r--r--src/pkg/io/ioutil/blackhole.go23
-rw-r--r--src/pkg/io/ioutil/ioutil.go14
-rw-r--r--src/pkg/io/multi.go11
-rw-r--r--src/pkg/io/multi_test.go27
6 files changed, 67 insertions, 29 deletions
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index f7073ffc0..022fdb676 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -74,6 +74,7 @@ type Reader interface {
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
+// Write must not modify the slice data, even temporarily.
type Writer interface {
Write(p []byte) (n int, err error)
}
diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go
index bd7a82f17..57db1fbf0 100644
--- a/src/pkg/io/io_test.go
+++ b/src/pkg/io/io_test.go
@@ -281,6 +281,8 @@ func TestSectionReader_ReadAt(t *testing.T) {
{data: dat, off: 3, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[5 : 5+len(dat)/2], err: nil},
{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil},
{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF},
+ {data: dat, off: 0, n: 0, bufLen: 0, at: -1, exp: "", err: EOF},
+ {data: dat, off: 0, n: 0, bufLen: 0, at: 1, exp: "", err: EOF},
}
for i, tt := range tests {
r := strings.NewReader(tt.data)
@@ -319,3 +321,21 @@ func TestSectionReader_Seek(t *testing.T) {
t.Errorf("Read = %v, %v; want 0, EOF", n, err)
}
}
+
+func TestSectionReader_Size(t *testing.T) {
+ tests := []struct {
+ data string
+ want int64
+ }{
+ {"a long sample data, 1234567890", 30},
+ {"", 0},
+ }
+
+ for _, tt := range tests {
+ r := strings.NewReader(tt.data)
+ sr := NewSectionReader(r, 0, int64(len(tt.data)))
+ if got := sr.Size(); got != tt.want {
+ t.Errorf("Size = %v; want %v", got, tt.want)
+ }
+ }
+}
diff --git a/src/pkg/io/ioutil/blackhole.go b/src/pkg/io/ioutil/blackhole.go
deleted file mode 100644
index 101d2c121..000000000
--- a/src/pkg/io/ioutil/blackhole.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2012 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 ioutil
-
-var blackHoleBuf = make(chan []byte, 1)
-
-func blackHole() []byte {
- select {
- case b := <-blackHoleBuf:
- return b
- default:
- }
- return make([]byte, 8192)
-}
-
-func blackHolePut(p []byte) {
- select {
- case blackHoleBuf <- p:
- default:
- }
-}
diff --git a/src/pkg/io/ioutil/ioutil.go b/src/pkg/io/ioutil/ioutil.go
index b2508b789..909a81563 100644
--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -10,6 +10,7 @@ import (
"io"
"os"
"sort"
+ "sync"
)
// readAll reads from r until an error or EOF and returns the data it read
@@ -136,14 +137,21 @@ func (devNull) WriteString(s string) (int, error) {
return len(s), nil
}
+var blackHolePool = sync.Pool{
+ New: func() interface{} {
+ b := make([]byte, 8192)
+ return &b
+ },
+}
+
func (devNull) ReadFrom(r io.Reader) (n int64, err error) {
- buf := blackHole()
- defer blackHolePut(buf)
+ bufp := blackHolePool.Get().(*[]byte)
readSize := 0
for {
- readSize, err = r.Read(buf)
+ readSize, err = r.Read(*bufp)
n += int64(readSize)
if err != nil {
+ blackHolePool.Put(bufp)
if err == io.EOF {
return n, nil
}
diff --git a/src/pkg/io/multi.go b/src/pkg/io/multi.go
index 2c7e816cf..e26cc53e9 100644
--- a/src/pkg/io/multi.go
+++ b/src/pkg/io/multi.go
@@ -26,9 +26,12 @@ func (mr *multiReader) Read(p []byte) (n int, err error) {
// 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 EOF.
+// inputs have returned EOF, Read will return EOF. If any of the readers
+// return a non-nil, non-EOF error, Read will return that error.
func MultiReader(readers ...Reader) Reader {
- return &multiReader{readers}
+ r := make([]Reader, len(readers))
+ copy(r, readers)
+ return &multiReader{r}
}
type multiWriter struct {
@@ -52,5 +55,7 @@ func (t *multiWriter) Write(p []byte) (n int, err error) {
// 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}
+ w := make([]Writer, len(writers))
+ copy(w, writers)
+ return &multiWriter{w}
}
diff --git a/src/pkg/io/multi_test.go b/src/pkg/io/multi_test.go
index eb717f7bc..56c6769a9 100644
--- a/src/pkg/io/multi_test.go
+++ b/src/pkg/io/multi_test.go
@@ -9,6 +9,7 @@ import (
"crypto/sha1"
"fmt"
. "io"
+ "io/ioutil"
"strings"
"testing"
)
@@ -86,3 +87,29 @@ func TestMultiWriter(t *testing.T) {
t.Errorf("expected %q; got %q", sourceString, sink.String())
}
}
+
+// Test that MultiReader copies the input slice and is insulated from future modification.
+func TestMultiReaderCopy(t *testing.T) {
+ slice := []Reader{strings.NewReader("hello world")}
+ r := MultiReader(slice...)
+ slice[0] = nil
+ data, err := ioutil.ReadAll(r)
+ if err != nil || string(data) != "hello world" {
+ t.Errorf("ReadAll() = %q, %v, want %q, nil", data, err, "hello world")
+ }
+}
+
+// Test that MultiWriter copies the input slice and is insulated from future modification.
+func TestMultiWriterCopy(t *testing.T) {
+ var buf bytes.Buffer
+ slice := []Writer{&buf}
+ w := MultiWriter(slice...)
+ slice[0] = nil
+ n, err := w.Write([]byte("hello world"))
+ if err != nil || n != 11 {
+ t.Errorf("Write(`hello world`) = %d, %v, want 11, nil", n, err)
+ }
+ if buf.String() != "hello world" {
+ t.Errorf("buf.String() = %q, want %q", buf.String(), "hello world")
+ }
+}