diff options
author | Russ Cox <rsc@golang.org> | 2020-07-06 11:26:26 -0400 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2020-10-20 17:52:55 +0000 |
commit | f098ccf04a33e2e4d2dffa2e90fe77ca8a0fcbb4 (patch) | |
tree | a478de6a5075d156a273bda1614bd506e13664d3 /src/io | |
parent | b1f76f7a220a806d74bf55da374ea89467753e1f (diff) | |
download | go-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.go | 63 | ||||
-rw-r--r-- | src/io/fs/readfile_test.go | 43 |
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") + } +} |