aboutsummaryrefslogtreecommitdiff
path: root/src/io
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2020-07-06 11:26:26 -0400
committerRuss Cox <rsc@golang.org>2020-10-20 17:52:55 +0000
commitf098ccf04a33e2e4d2dffa2e90fe77ca8a0fcbb4 (patch)
treea478de6a5075d156a273bda1614bd506e13664d3 /src/io
parentb1f76f7a220a806d74bf55da374ea89467753e1f (diff)
downloadgo-f098ccf04a33e2e4d2dffa2e90fe77ca8a0fcbb4.tar.gz
go-f098ccf04a33e2e4d2dffa2e90fe77ca8a0fcbb4.zip
io/fs: add ReadFile and ReadFileFS
Add ReadFile helper function, ReadFileFS interface, and test. Add ReadFile method to fstest.MapFS. Add testing of ReadFile method to fstest.TestFS. For #41190. Change-Id: I5b6a41e2e582824e570463b698b635abaa436c32 Reviewed-on: https://go-review.googlesource.com/c/go/+/243912 Trust: Russ Cox <rsc@golang.org> Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/io')
-rw-r--r--src/io/fs/readfile.go63
-rw-r--r--src/io/fs/readfile_test.go43
2 files changed, 106 insertions, 0 deletions
diff --git a/src/io/fs/readfile.go b/src/io/fs/readfile.go
new file mode 100644
index 0000000000..7ee9eadac4
--- /dev/null
+++ b/src/io/fs/readfile.go
@@ -0,0 +1,63 @@
+// Copyright 2020 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 fs
+
+import "io"
+
+// ReadFileFS is the interface implemented by a file system
+// that provides an optimized implementation of ReadFile.
+type ReadFileFS interface {
+ FS
+
+ // ReadFile reads the named file and returns its contents.
+ // A successful call returns a nil error, not io.EOF.
+ // (Because ReadFile reads the whole file, the expected EOF
+ // from the final Read is not treated as an error to be reported.)
+ ReadFile(name string) ([]byte, error)
+}
+
+// ReadFile reads the named file from the file system fs and returns its contents.
+// A successful call returns a nil error, not io.EOF.
+// (Because ReadFile reads the whole file, the expected EOF
+// from the final Read is not treated as an error to be reported.)
+//
+// If fs implements ReadFileFS, ReadFile calls fs.ReadFile.
+// Otherwise ReadFile calls fs.Open and uses Read and Close
+// on the returned file.
+func ReadFile(fsys FS, name string) ([]byte, error) {
+ if fsys, ok := fsys.(ReadFileFS); ok {
+ return fsys.ReadFile(name)
+ }
+
+ file, err := fsys.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ var size int
+ if info, err := file.Stat(); err == nil {
+ size64 := info.Size()
+ if int64(int(size64)) == size64 {
+ size = int(size64)
+ }
+ }
+
+ data := make([]byte, 0, size+1)
+ for {
+ if len(data) >= cap(data) {
+ d := append(data[:cap(data)], 0)
+ data = d[:len(data)]
+ }
+ n, err := file.Read(data[len(data):cap(data)])
+ data = data[:len(data)+n]
+ if err != nil {
+ if err == io.EOF {
+ err = nil
+ }
+ return data, err
+ }
+ }
+}
diff --git a/src/io/fs/readfile_test.go b/src/io/fs/readfile_test.go
new file mode 100644
index 0000000000..0afa334ace
--- /dev/null
+++ b/src/io/fs/readfile_test.go
@@ -0,0 +1,43 @@
+// Copyright 2020 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 fs_test
+
+import (
+ . "io/fs"
+ "testing"
+ "testing/fstest"
+ "time"
+)
+
+var testFsys = fstest.MapFS{
+ "hello.txt": {
+ Data: []byte("hello, world"),
+ Mode: 0456,
+ ModTime: time.Now(),
+ Sys: &sysValue,
+ },
+}
+
+var sysValue int
+
+type readFileOnly struct{ ReadFileFS }
+
+func (readFileOnly) Open(name string) (File, error) { return nil, ErrNotExist }
+
+type openOnly struct{ FS }
+
+func TestReadFile(t *testing.T) {
+ // Test that ReadFile uses the method when present.
+ data, err := ReadFile(readFileOnly{testFsys}, "hello.txt")
+ if string(data) != "hello, world" || err != nil {
+ t.Fatalf(`ReadFile(readFileOnly, "hello.txt") = %q, %v, want %q, nil`, data, err, "hello, world")
+ }
+
+ // Test that ReadFile uses Open when the method is not present.
+ data, err = ReadFile(openOnly{testFsys}, "hello.txt")
+ if string(data) != "hello, world" || err != nil {
+ t.Fatalf(`ReadFile(openOnly, "hello.txt") = %q, %v, want %q, nil`, data, err, "hello, world")
+ }
+}