1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package fs
6
7import "io"
8
9// ReadFileFS is the interface implemented by a file system
10// that provides an optimized implementation of [ReadFile].
11type ReadFileFS interface {
12	FS
13
14	// ReadFile reads the named file and returns its contents.
15	// A successful call returns a nil error, not io.EOF.
16	// (Because ReadFile reads the whole file, the expected EOF
17	// from the final Read is not treated as an error to be reported.)
18	//
19	// The caller is permitted to modify the returned byte slice.
20	// This method should return a copy of the underlying data.
21	ReadFile(name string) ([]byte, error)
22}
23
24// ReadFile reads the named file from the file system fs and returns its contents.
25// A successful call returns a nil error, not [io.EOF].
26// (Because ReadFile reads the whole file, the expected EOF
27// from the final Read is not treated as an error to be reported.)
28//
29// If fs implements [ReadFileFS], ReadFile calls fs.ReadFile.
30// Otherwise ReadFile calls fs.Open and uses Read and Close
31// on the returned [File].
32func ReadFile(fsys FS, name string) ([]byte, error) {
33	if fsys, ok := fsys.(ReadFileFS); ok {
34		return fsys.ReadFile(name)
35	}
36
37	file, err := fsys.Open(name)
38	if err != nil {
39		return nil, err
40	}
41	defer file.Close()
42
43	var size int
44	if info, err := file.Stat(); err == nil {
45		size64 := info.Size()
46		if int64(int(size64)) == size64 {
47			size = int(size64)
48		}
49	}
50
51	data := make([]byte, 0, size+1)
52	for {
53		if len(data) >= cap(data) {
54			d := append(data[:cap(data)], 0)
55			data = d[:len(data)]
56		}
57		n, err := file.Read(data[len(data):cap(data)])
58		data = data[:len(data)+n]
59		if err != nil {
60			if err == io.EOF {
61				err = nil
62			}
63			return data, err
64		}
65	}
66}
67