summaryrefslogtreecommitdiff
path: root/src/pkg/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/io')
-rw-r--r--src/pkg/io/io.go81
-rw-r--r--src/pkg/io/io_test.go76
-rw-r--r--src/pkg/io/ioutil/blackhole.go23
-rw-r--r--src/pkg/io/ioutil/ioutil.go6
-rw-r--r--src/pkg/io/ioutil/ioutil_test.go3
-rw-r--r--src/pkg/io/ioutil/tempfile.go10
-rw-r--r--src/pkg/io/ioutil/tempfile_test.go3
7 files changed, 141 insertions, 61 deletions
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index 54bf159eb..23d05e575 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -130,11 +130,23 @@ type ReadWriteSeeker interface {
}
// ReaderFrom is the interface that wraps the ReadFrom method.
+//
+// ReadFrom reads data from r until EOF or error.
+// The return value n is the number of bytes read.
+// Any error except io.EOF encountered during the read is also returned.
+//
+// The Copy function uses ReaderFrom if available.
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
// WriterTo is the interface that wraps the WriteTo method.
+//
+// WriteTo writes data to w until there's no more data to write or
+// when an error occurs. The return value n is the number of bytes
+// written. Any error encountered during the write is also returned.
+//
+// The Copy function uses WriterTo if available.
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
@@ -204,6 +216,11 @@ type ByteScanner interface {
UnreadByte() error
}
+// ByteWriter is the interface that wraps the WriteByte method.
+type ByteWriter interface {
+ WriteByte(c byte) error
+}
+
// RuneReader is the interface that wraps the ReadRune method.
//
// ReadRune reads a single UTF-8 encoded Unicode character
@@ -245,6 +262,7 @@ func WriteString(w Writer, s string) (n int, err error) {
// 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.
+// On return, n >= min if and only if err == nil.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
if len(buf) < min {
return 0, ErrShortBuffer
@@ -254,12 +272,10 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
nn, err = r.Read(buf[n:])
n += nn
}
- if err == EOF {
- if n >= min {
- err = nil
- } else if n > 0 {
- err = ErrUnexpectedEOF
- }
+ if n >= min {
+ err = nil
+ } else if n > 0 && err == EOF {
+ err = ErrUnexpectedEOF
}
return
}
@@ -269,56 +285,28 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
// The error is EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// ReadFull returns ErrUnexpectedEOF.
+// On return, n == len(buf) if and only if err == nil.
func ReadFull(r Reader, buf []byte) (n int, err error) {
return ReadAtLeast(r, buf, len(buf))
}
// CopyN copies n bytes (or until an error) from src to dst.
// It returns the number of bytes copied and the earliest
-// error encountered while copying. Because Read can
-// return the full amount requested as well as an error
-// (including EOF), so can CopyN.
+// error encountered while copying.
+// On return, written == n if and only if err == nil.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented using it.
func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
- // 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 {
- written, err = rt.ReadFrom(LimitReader(src, n))
- if written < n && err == nil {
- // rt stopped early; must have been EOF.
- err = EOF
- }
- return
+ written, err = Copy(dst, LimitReader(src, n))
+ if written == n {
+ return n, nil
}
- buf := make([]byte, 32*1024)
- for written < n {
- l := len(buf)
- if d := n - written; d < int64(l) {
- l = int(d)
- }
- nr, er := src.Read(buf[0:l])
- if nr > 0 {
- nw, ew := dst.Write(buf[0:nr])
- if nw > 0 {
- written += int64(nw)
- }
- if ew != nil {
- err = ew
- break
- }
- if nr != nw {
- err = ErrShortWrite
- break
- }
- }
- if er != nil {
- err = er
- break
- }
+ if written < n && err == nil {
+ // src stopped early; must have been EOF.
+ err = EOF
}
- return written, err
+ return
}
// Copy copies from src to dst until either EOF is reached
@@ -451,6 +439,11 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
off += s.base
if max := s.limit - off; int64(len(p)) > max {
p = p[0:max]
+ n, err = s.r.ReadAt(p, off)
+ if err == nil {
+ err = EOF
+ }
+ return n, err
}
return s.r.ReadAt(p, off)
}
diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go
index 1e671b59b..1bc451e44 100644
--- a/src/pkg/io/io_test.go
+++ b/src/pkg/io/io_test.go
@@ -6,6 +6,8 @@ package io_test
import (
"bytes"
+ "errors"
+ "fmt"
. "io"
"strings"
"testing"
@@ -88,6 +90,12 @@ func (w *noReadFrom) Write(p []byte) (n int, err error) {
return w.w.Write(p)
}
+type wantedAndErrReader struct{}
+
+func (wantedAndErrReader) Read(p []byte) (int, error) {
+ return len(p), errors.New("wantedAndErrReader error")
+}
+
func TestCopyNEOF(t *testing.T) {
// Test that EOF behavior is the same regardless of whether
// argument to CopyN has ReadFrom.
@@ -113,6 +121,16 @@ func TestCopyNEOF(t *testing.T) {
if n != 3 || err != EOF {
t.Errorf("CopyN(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err)
}
+
+ n, err = CopyN(b, wantedAndErrReader{}, 5)
+ if n != 5 || err != nil {
+ t.Errorf("CopyN(bytes.Buffer, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err)
+ }
+
+ n, err = CopyN(&noReadFrom{b}, wantedAndErrReader{}, 5)
+ if n != 5 || err != nil {
+ t.Errorf("CopyN(noReadFrom, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err)
+ }
}
func TestReadAtLeast(t *testing.T) {
@@ -120,22 +138,30 @@ func TestReadAtLeast(t *testing.T) {
testReadAtLeast(t, &rb)
}
-// A version of bytes.Buffer that returns n > 0, EOF on Read
+// A version of bytes.Buffer that returns n > 0, err on Read
// when the input is exhausted.
-type dataAndEOFBuffer struct {
+type dataAndErrorBuffer struct {
+ err error
bytes.Buffer
}
-func (r *dataAndEOFBuffer) Read(p []byte) (n int, err error) {
+func (r *dataAndErrorBuffer) Read(p []byte) (n int, err error) {
n, err = r.Buffer.Read(p)
if n > 0 && r.Buffer.Len() == 0 && err == nil {
- err = EOF
+ err = r.err
}
return
}
func TestReadAtLeastWithDataAndEOF(t *testing.T) {
- var rb dataAndEOFBuffer
+ var rb dataAndErrorBuffer
+ rb.err = EOF
+ testReadAtLeast(t, &rb)
+}
+
+func TestReadAtLeastWithDataAndError(t *testing.T) {
+ var rb dataAndErrorBuffer
+ rb.err = fmt.Errorf("fake error")
testReadAtLeast(t, &rb)
}
@@ -169,8 +195,12 @@ func testReadAtLeast(t *testing.T, rb ReadWriter) {
}
rb.Write([]byte("4"))
n, err = ReadAtLeast(rb, buf, 2)
- if err != ErrUnexpectedEOF {
- t.Errorf("expected ErrUnexpectedEOF, got %v", err)
+ want := ErrUnexpectedEOF
+ if rb, ok := rb.(*dataAndErrorBuffer); ok && rb.err != EOF {
+ want = rb.err
+ }
+ if err != want {
+ t.Errorf("expected %v, got %v", want, err)
}
if n != 1 {
t.Errorf("expected to have read 1 bytes, got %v", n)
@@ -203,3 +233,35 @@ func TestTeeReader(t *testing.T) {
t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err)
}
}
+
+func TestSectionReader_ReadAt(tst *testing.T) {
+ dat := "a long sample data, 1234567890"
+ tests := []struct {
+ data string
+ off int
+ n int
+ bufLen int
+ at int
+ exp string
+ err error
+ }{
+ {data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: EOF},
+ {data: dat, off: 0, n: len(dat), bufLen: 0, at: 0, exp: "", err: nil},
+ {data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: EOF},
+ {data: dat, off: 0, n: len(dat) + 2, bufLen: len(dat), at: 0, exp: dat, err: nil},
+ {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 0, exp: dat[:len(dat)/2], err: nil},
+ {data: dat, off: 0, n: len(dat), bufLen: len(dat), at: 0, exp: dat, err: nil},
+ {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[2 : 2+len(dat)/2], err: nil},
+ {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},
+ }
+ for i, t := range tests {
+ r := strings.NewReader(t.data)
+ s := NewSectionReader(r, int64(t.off), int64(t.n))
+ buf := make([]byte, t.bufLen)
+ if n, err := s.ReadAt(buf, int64(t.at)); n != len(t.exp) || string(buf[:n]) != t.exp || err != t.err {
+ tst.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, t.at, buf[:n], err, t.exp, t.err)
+ }
+ }
+}
diff --git a/src/pkg/io/ioutil/blackhole.go b/src/pkg/io/ioutil/blackhole.go
new file mode 100644
index 000000000..101d2c121
--- /dev/null
+++ b/src/pkg/io/ioutil/blackhole.go
@@ -0,0 +1,23 @@
+// 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 f072b8c75..0eb146c0a 100644
--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -130,12 +130,12 @@ func (devNull) Write(p []byte) (int, error) {
return len(p), nil
}
-var blackHole = make([]byte, 8192)
-
func (devNull) ReadFrom(r io.Reader) (n int64, err error) {
+ buf := blackHole()
+ defer blackHolePut(buf)
readSize := 0
for {
- readSize, err = r.Read(blackHole)
+ readSize, err = r.Read(buf)
n += int64(readSize)
if err != nil {
if err == io.EOF {
diff --git a/src/pkg/io/ioutil/ioutil_test.go b/src/pkg/io/ioutil/ioutil_test.go
index d9c43bead..c297847b4 100644
--- a/src/pkg/io/ioutil/ioutil_test.go
+++ b/src/pkg/io/ioutil/ioutil_test.go
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package ioutil_test
+package ioutil
import (
- . "io/ioutil"
"os"
"testing"
)
diff --git a/src/pkg/io/ioutil/tempfile.go b/src/pkg/io/ioutil/tempfile.go
index 42d2e6758..4a06e9756 100644
--- a/src/pkg/io/ioutil/tempfile.go
+++ b/src/pkg/io/ioutil/tempfile.go
@@ -8,26 +8,30 @@ import (
"os"
"path/filepath"
"strconv"
+ "sync"
"time"
)
-// Random number state, accessed without lock; racy but harmless.
+// Random number state.
// We generate random temporary file names so that there's a good
// chance the file doesn't exist yet - keeps the number of tries in
// TempFile to a minimum.
var rand uint32
+var randmu sync.Mutex
func reseed() uint32 {
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
}
func nextSuffix() string {
+ randmu.Lock()
r := rand
if r == 0 {
r = reseed()
}
r = r*1664525 + 1013904223 // constants from Numerical Recipes
rand = r
+ randmu.Unlock()
return strconv.Itoa(int(1e9 + r%1e9))[1:]
}
@@ -38,8 +42,8 @@ func nextSuffix() string {
// for temporary files (see os.TempDir).
// Multiple programs calling TempFile simultaneously
// will not choose the same file. The caller can use f.Name()
-// to find the name of the file. It is the caller's responsibility to
-// remove the file when no longer needed.
+// to find the pathname of the file. It is the caller's responsibility
+// to remove the file when no longer needed.
func TempFile(dir, prefix string) (f *os.File, err error) {
if dir == "" {
dir = os.TempDir()
diff --git a/src/pkg/io/ioutil/tempfile_test.go b/src/pkg/io/ioutil/tempfile_test.go
index 80c62f672..d2a132a11 100644
--- a/src/pkg/io/ioutil/tempfile_test.go
+++ b/src/pkg/io/ioutil/tempfile_test.go
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package ioutil_test
+package ioutil
import (
- . "io/ioutil"
"os"
"path/filepath"
"regexp"