Skip to content

Commit ca385e5

Browse files
committed
Add ReadFile helper
1 parent 1a982b2 commit ca385e5

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

ioutil.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package vfs
22

33
import (
4+
"bytes"
45
"io"
56
"os"
67
)
@@ -24,3 +25,59 @@ func WriteFile(fs Filesystem, filename string, data []byte, perm os.FileMode) er
2425
}
2526
return err
2627
}
28+
29+
// ReadFile reads the file named by filename and returns the contents. A
30+
// successful call returns err == nil, not err == EOF. Because ReadFile reads
31+
// the whole file, it does not treat an EOF from Read as an error to be
32+
// reported.
33+
//
34+
// This is a port of the stdlib ioutil.ReadFile function.
35+
func ReadFile(fs Filesystem, filename string) ([]byte, error) {
36+
f, err := fs.OpenFile(filename, os.O_RDONLY, 0)
37+
if err != nil {
38+
return nil, err
39+
}
40+
defer f.Close()
41+
42+
// It's a good but not certain bet that FileInfo will tell us exactly how
43+
// much to read, so let's try it but be prepared for the answer to be wrong.
44+
var n int64
45+
if fi, err := fs.Stat(filename); err == nil {
46+
if size := fi.Size(); size < 1e9 {
47+
n = size
48+
}
49+
}
50+
51+
// As initial capacity for readAll, use n + a little extra in case Size is
52+
// zero, and to avoid another allocation after Read has filled the buffer.
53+
// The readAll call will read into its allocated internal buffer cheaply. If
54+
// the size was wrong, we'll either waste some space off the end or
55+
// reallocate as needed, but in the overwhelmingly common case we'll get it
56+
// just right.
57+
return readAll(f, n+bytes.MinRead)
58+
}
59+
60+
// readAll reads from r until an error or EOF and returns the data it read from
61+
// the internal buffer allocated with a specified capacity.
62+
//
63+
// This is a paste of the stdlib ioutil.readAll function.
64+
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
65+
buf := bytes.NewBuffer(make([]byte, 0, capacity))
66+
67+
// If the buffer overflows, we will get bytes.ErrTooLarge.
68+
// Return that as an error. Any other panic remains.
69+
defer func() {
70+
e := recover()
71+
if e == nil {
72+
return
73+
}
74+
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
75+
err = panicErr
76+
} else {
77+
panic(e)
78+
}
79+
}()
80+
81+
_, err = buf.ReadFrom(r)
82+
return buf.Bytes(), err
83+
}

ioutil_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,24 @@ func TestWriteFile(t *testing.T) {
3131
t.Fatalf("Bad file mode: %o (expected %o)", info.Mode(), testmode)
3232
}
3333
}
34+
35+
func TestReadFile(t *testing.T) {
36+
fs := memfs.Create()
37+
38+
f, _ := fs.OpenFile(testpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, testmode)
39+
f.Write(testdata)
40+
f.Close()
41+
42+
data, err := vfs.ReadFile(fs, testpath)
43+
if err != nil {
44+
t.Fatalf("ReadFile failed: %s", err)
45+
}
46+
if len(data) != len(testdata) {
47+
t.Fatalf("Bad data length: %d bytes (expected %d)", len(data), len(testdata))
48+
}
49+
50+
_, err = vfs.ReadFile(fs, "/doesnt-exist.txt")
51+
if err == nil {
52+
t.Fatalf("ReadFile failed: expected error")
53+
}
54+
}

0 commit comments

Comments
 (0)