Skip to content

Commit cb0a0f5

Browse files
committed
io: adopt Discard, NopCloser, ReadAll from io/ioutil
As proposed and approved in #40025, Discard, NopCloser, and ReadAll do not really fit into io/ioutil, which exists mainly to hold things that would cause an import cycle if implemented in io itself, which is to say things that import "os". These three do not import "os" - they are generic io helpers like many of the things in io itself, so it makes sense for them to be there. Fixes #40025. Change-Id: I77f47e9b2a72839edf7446997936631980047b67 Reviewed-on: https://go-review.googlesource.com/c/go/+/263141 Trust: Russ Cox <[email protected]> Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Rob Pike <[email protected]>
1 parent 7211694 commit cb0a0f5

File tree

3 files changed

+126
-79
lines changed

3 files changed

+126
-79
lines changed

src/io/example_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,17 @@ func ExamplePipe() {
241241
// Output:
242242
// some io.Reader stream to be read
243243
}
244+
245+
func ExampleReadAll() {
246+
r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")
247+
248+
b, err := ioutil.ReadAll(r)
249+
if err != nil {
250+
log.Fatal(err)
251+
}
252+
253+
fmt.Printf("%s", b)
254+
255+
// Output:
256+
// Go is a general-purpose language designed with systems programming in mind.
257+
}

src/io/io.go

+80-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ package io
1414

1515
import (
1616
"errors"
17+
"sync"
1718
)
1819

1920
// Seek whence values.
@@ -46,9 +47,9 @@ var EOF = errors.New("EOF")
4647
// middle of reading a fixed-size block or data structure.
4748
var ErrUnexpectedEOF = errors.New("unexpected EOF")
4849

49-
// ErrNoProgress is returned by some clients of an io.Reader when
50+
// ErrNoProgress is returned by some clients of an Reader when
5051
// many calls to Read have failed to return any data or error,
51-
// usually the sign of a broken io.Reader implementation.
52+
// usually the sign of a broken Reader implementation.
5253
var ErrNoProgress = errors.New("multiple Read calls return no data or error")
5354

5455
// Reader is the interface that wraps the basic Read method.
@@ -177,7 +178,7 @@ type ReadWriteSeeker interface {
177178
//
178179
// ReadFrom reads data from r until EOF or error.
179180
// The return value n is the number of bytes read.
180-
// Any error except io.EOF encountered during the read is also returned.
181+
// Any error except EOF encountered during the read is also returned.
181182
//
182183
// The Copy function uses ReaderFrom if available.
183184
type ReaderFrom interface {
@@ -390,7 +391,7 @@ func Copy(dst Writer, src Reader) (written int64, err error) {
390391
// buf will not be used to perform the copy.
391392
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
392393
if buf != nil && len(buf) == 0 {
393-
panic("empty buffer in io.CopyBuffer")
394+
panic("empty buffer in CopyBuffer")
394395
}
395396
return copyBuffer(dst, src, buf)
396397
}
@@ -564,3 +565,78 @@ func (t *teeReader) Read(p []byte) (n int, err error) {
564565
}
565566
return
566567
}
568+
569+
// Discard is an Writer on which all Write calls succeed
570+
// without doing anything.
571+
var Discard Writer = discard{}
572+
573+
type discard struct{}
574+
575+
// discard implements ReaderFrom as an optimization so Copy to
576+
// ioutil.Discard can avoid doing unnecessary work.
577+
var _ ReaderFrom = discard{}
578+
579+
func (discard) Write(p []byte) (int, error) {
580+
return len(p), nil
581+
}
582+
583+
func (discard) WriteString(s string) (int, error) {
584+
return len(s), nil
585+
}
586+
587+
var blackHolePool = sync.Pool{
588+
New: func() interface{} {
589+
b := make([]byte, 8192)
590+
return &b
591+
},
592+
}
593+
594+
func (discard) ReadFrom(r Reader) (n int64, err error) {
595+
bufp := blackHolePool.Get().(*[]byte)
596+
readSize := 0
597+
for {
598+
readSize, err = r.Read(*bufp)
599+
n += int64(readSize)
600+
if err != nil {
601+
blackHolePool.Put(bufp)
602+
if err == EOF {
603+
return n, nil
604+
}
605+
return
606+
}
607+
}
608+
}
609+
610+
// NopCloser returns a ReadCloser with a no-op Close method wrapping
611+
// the provided Reader r.
612+
func NopCloser(r Reader) ReadCloser {
613+
return nopCloser{r}
614+
}
615+
616+
type nopCloser struct {
617+
Reader
618+
}
619+
620+
func (nopCloser) Close() error { return nil }
621+
622+
// ReadAll reads from r until an error or EOF and returns the data it read.
623+
// A successful call returns err == nil, not err == EOF. Because ReadAll is
624+
// defined to read from src until EOF, it does not treat an EOF from Read
625+
// as an error to be reported.
626+
func ReadAll(r Reader) ([]byte, error) {
627+
b := make([]byte, 0, 512)
628+
for {
629+
if len(b) == cap(b) {
630+
// Add more capacity (let append pick how much).
631+
b = append(b, 0)[:len(b)]
632+
}
633+
n, err := r.Read(b[len(b):cap(b)])
634+
b = b[:len(b)+n]
635+
if err != nil {
636+
if err == EOF {
637+
err = nil
638+
}
639+
return b, err
640+
}
641+
}
642+
}

src/io/ioutil/ioutil.go

+32-75
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,20 @@
66
package ioutil
77

88
import (
9-
"bytes"
109
"io"
1110
"io/fs"
1211
"os"
1312
"sort"
14-
"sync"
1513
)
1614

17-
// readAll reads from r until an error or EOF and returns the data it read
18-
// from the internal buffer allocated with a specified capacity.
19-
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
20-
var buf bytes.Buffer
21-
// If the buffer overflows, we will get bytes.ErrTooLarge.
22-
// Return that as an error. Any other panic remains.
23-
defer func() {
24-
e := recover()
25-
if e == nil {
26-
return
27-
}
28-
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
29-
err = panicErr
30-
} else {
31-
panic(e)
32-
}
33-
}()
34-
if int64(int(capacity)) == capacity {
35-
buf.Grow(int(capacity))
36-
}
37-
_, err = buf.ReadFrom(r)
38-
return buf.Bytes(), err
39-
}
40-
4115
// ReadAll reads from r until an error or EOF and returns the data it read.
4216
// A successful call returns err == nil, not err == EOF. Because ReadAll is
4317
// defined to read from src until EOF, it does not treat an EOF from Read
4418
// as an error to be reported.
19+
//
20+
// As of Go 1.16, this function simply calls io.ReadAll.
4521
func ReadAll(r io.Reader) ([]byte, error) {
46-
return readAll(r, bytes.MinRead)
22+
return io.ReadAll(r)
4723
}
4824

4925
// ReadFile reads the file named by filename and returns the contents.
@@ -58,7 +34,8 @@ func ReadFile(filename string) ([]byte, error) {
5834
defer f.Close()
5935
// It's a good but not certain bet that FileInfo will tell us exactly how much to
6036
// read, so let's try it but be prepared for the answer to be wrong.
61-
var n int64 = bytes.MinRead
37+
const minRead = 512
38+
var n int64 = minRead
6239

6340
if fi, err := f.Stat(); err == nil {
6441
// As initial capacity for readAll, use Size + a little extra in case Size
@@ -67,11 +44,30 @@ func ReadFile(filename string) ([]byte, error) {
6744
// cheaply. If the size was wrong, we'll either waste some space off the end
6845
// or reallocate as needed, but in the overwhelmingly common case we'll get
6946
// it just right.
70-
if size := fi.Size() + bytes.MinRead; size > n {
47+
if size := fi.Size() + minRead; size > n {
7148
n = size
7249
}
7350
}
74-
return readAll(f, n)
51+
52+
if int64(int(n)) != n {
53+
n = minRead
54+
}
55+
56+
b := make([]byte, 0, n)
57+
for {
58+
if len(b) == cap(b) {
59+
// Add more capacity (let append pick how much).
60+
b = append(b, 0)[:len(b)]
61+
}
62+
n, err := f.Read(b[len(b):cap(b)])
63+
b = b[:len(b)+n]
64+
if err != nil {
65+
if err == io.EOF {
66+
err = nil
67+
}
68+
return b, err
69+
}
70+
}
7571
}
7672

7773
// WriteFile writes data to a file named by filename.
@@ -105,55 +101,16 @@ func ReadDir(dirname string) ([]fs.FileInfo, error) {
105101
return list, nil
106102
}
107103

108-
type nopCloser struct {
109-
io.Reader
110-
}
111-
112-
func (nopCloser) Close() error { return nil }
113-
114104
// NopCloser returns a ReadCloser with a no-op Close method wrapping
115105
// the provided Reader r.
106+
//
107+
// As of Go 1.16, this function simply calls io.NopCloser.
116108
func NopCloser(r io.Reader) io.ReadCloser {
117-
return nopCloser{r}
118-
}
119-
120-
type devNull int
121-
122-
// devNull implements ReaderFrom as an optimization so io.Copy to
123-
// ioutil.Discard can avoid doing unnecessary work.
124-
var _ io.ReaderFrom = devNull(0)
125-
126-
func (devNull) Write(p []byte) (int, error) {
127-
return len(p), nil
128-
}
129-
130-
func (devNull) WriteString(s string) (int, error) {
131-
return len(s), nil
132-
}
133-
134-
var blackHolePool = sync.Pool{
135-
New: func() interface{} {
136-
b := make([]byte, 8192)
137-
return &b
138-
},
139-
}
140-
141-
func (devNull) ReadFrom(r io.Reader) (n int64, err error) {
142-
bufp := blackHolePool.Get().(*[]byte)
143-
readSize := 0
144-
for {
145-
readSize, err = r.Read(*bufp)
146-
n += int64(readSize)
147-
if err != nil {
148-
blackHolePool.Put(bufp)
149-
if err == io.EOF {
150-
return n, nil
151-
}
152-
return
153-
}
154-
}
109+
return io.NopCloser(r)
155110
}
156111

157112
// Discard is an io.Writer on which all Write calls succeed
158113
// without doing anything.
159-
var Discard io.Writer = devNull(0)
114+
//
115+
// As of Go 1.16, this value is simply io.Discard.
116+
var Discard io.Writer = io.Discard

0 commit comments

Comments
 (0)