readfile.mx raw

   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  
   5  package fs
   6  
   7  import "io"
   8  
   9  // ReadFileFS is the interface implemented by a file system
  10  // that provides an optimized implementation of [ReadFile].
  11  type 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].
  32  func ReadFile(fsys FS, name []byte) ([]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 := []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