From af7e4ac12d42e726180d4474122a2266f18b07d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ximo=20Cuadros?= Date: Thu, 29 Apr 2021 00:50:51 +0200 Subject: [PATCH] util: add ReadFile function --- util/util.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++ util/util_test.go | 21 ++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/util/util.go b/util/util.go index 34c1d9e..dcddb18 100644 --- a/util/util.go +++ b/util/util.go @@ -222,3 +222,53 @@ func getUnderlyingAndPath(fs billy.Basic, path string) (billy.Basic, string) { return u.Underlying(), path } + +// ReadFile reads the named file and returns the contents from the given filesystem. +// A successful call returns err == nil, not err == EOF. +// Because ReadFile reads the whole file, it does not treat an EOF from Read +// as an error to be reported. +func ReadFile(fs billy.Basic, name string) ([]byte, error) { + f, err := fs.Open(name) + if err != nil { + return nil, err + } + + defer f.Close() + + var size int + if info, err := fs.Stat(name); err == nil { + size64 := info.Size() + if int64(int(size64)) == size64 { + size = int(size64) + } + } + + size++ // one byte for final read at EOF + // If a file claims a small size, read at least 512 bytes. + // In particular, files in Linux's /proc claim size 0 but + // then do not work right if read in small pieces, + // so an initial read of 1 byte would not work correctly. + + if size < 512 { + size = 512 + } + + data := make([]byte, 0, size) + for { + if len(data) >= cap(data) { + d := append(data[:cap(data)], 0) + data = d[:len(data)] + } + + n, err := f.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/util/util_test.go b/util/util_test.go index 7b01db9..8fd2f8b 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -41,3 +41,24 @@ func TestTempDir(t *testing.T) { } } } + +func TestReadFile(t *testing.T) { + fs := memfs.New() + f, err := util.TempFile(fs, "", "") + if err != nil { + t.Fatal(err) + } + + f.Write([]byte("foo")) + f.Close() + + data, err := util.ReadFile(fs, f.Name()) + if err != nil { + t.Fatal(err) + } + + if string(data) != "foo" || err != nil { + t.Errorf("ReadFile(%q, %q) = %v, %v", fs, f.Name(), data, err) + } + +}