mirror of
https://review.haiku-os.org/buildtools
synced 2025-02-12 08:47:41 +01:00
Updated dependencies: * GMP 6.2.1 * ISL 0.24 * MPL 1.2.1 * MPFR 4.1.0 The dependencies were pulled in by running the ./contrib/download_prerequisites script and then manually removing the symbolic links and archives, and renaming the directories (i.e mv isl-0.24 to isl)
241 lines
7.0 KiB
Go
241 lines
7.0 KiB
Go
// 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 fstest
|
|
|
|
import (
|
|
"io"
|
|
"io/fs"
|
|
"path"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// A MapFS is a simple in-memory file system for use in tests,
|
|
// represented as a map from path names (arguments to Open)
|
|
// to information about the files or directories they represent.
|
|
//
|
|
// The map need not include parent directories for files contained
|
|
// in the map; those will be synthesized if needed.
|
|
// But a directory can still be included by setting the MapFile.Mode's ModeDir bit;
|
|
// this may be necessary for detailed control over the directory's FileInfo
|
|
// or to create an empty directory.
|
|
//
|
|
// File system operations read directly from the map,
|
|
// so that the file system can be changed by editing the map as needed.
|
|
// An implication is that file system operations must not run concurrently
|
|
// with changes to the map, which would be a race.
|
|
// Another implication is that opening or reading a directory requires
|
|
// iterating over the entire map, so a MapFS should typically be used with not more
|
|
// than a few hundred entries or directory reads.
|
|
type MapFS map[string]*MapFile
|
|
|
|
// A MapFile describes a single file in a MapFS.
|
|
type MapFile struct {
|
|
Data []byte // file content
|
|
Mode fs.FileMode // FileInfo.Mode
|
|
ModTime time.Time // FileInfo.ModTime
|
|
Sys any // FileInfo.Sys
|
|
}
|
|
|
|
var _ fs.FS = MapFS(nil)
|
|
var _ fs.File = (*openMapFile)(nil)
|
|
|
|
// Open opens the named file.
|
|
func (fsys MapFS) Open(name string) (fs.File, error) {
|
|
if !fs.ValidPath(name) {
|
|
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
|
|
}
|
|
file := fsys[name]
|
|
if file != nil && file.Mode&fs.ModeDir == 0 {
|
|
// Ordinary file
|
|
return &openMapFile{name, mapFileInfo{path.Base(name), file}, 0}, nil
|
|
}
|
|
|
|
// Directory, possibly synthesized.
|
|
// Note that file can be nil here: the map need not contain explicit parent directories for all its files.
|
|
// But file can also be non-nil, in case the user wants to set metadata for the directory explicitly.
|
|
// Either way, we need to construct the list of children of this directory.
|
|
var list []mapFileInfo
|
|
var elem string
|
|
var need = make(map[string]bool)
|
|
if name == "." {
|
|
elem = "."
|
|
for fname, f := range fsys {
|
|
i := strings.Index(fname, "/")
|
|
if i < 0 {
|
|
if fname != "." {
|
|
list = append(list, mapFileInfo{fname, f})
|
|
}
|
|
} else {
|
|
need[fname[:i]] = true
|
|
}
|
|
}
|
|
} else {
|
|
elem = name[strings.LastIndex(name, "/")+1:]
|
|
prefix := name + "/"
|
|
for fname, f := range fsys {
|
|
if strings.HasPrefix(fname, prefix) {
|
|
felem := fname[len(prefix):]
|
|
i := strings.Index(felem, "/")
|
|
if i < 0 {
|
|
list = append(list, mapFileInfo{felem, f})
|
|
} else {
|
|
need[fname[len(prefix):len(prefix)+i]] = true
|
|
}
|
|
}
|
|
}
|
|
// If the directory name is not in the map,
|
|
// and there are no children of the name in the map,
|
|
// then the directory is treated as not existing.
|
|
if file == nil && list == nil && len(need) == 0 {
|
|
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
|
|
}
|
|
}
|
|
for _, fi := range list {
|
|
delete(need, fi.name)
|
|
}
|
|
for name := range need {
|
|
list = append(list, mapFileInfo{name, &MapFile{Mode: fs.ModeDir}})
|
|
}
|
|
sort.Slice(list, func(i, j int) bool {
|
|
return list[i].name < list[j].name
|
|
})
|
|
|
|
if file == nil {
|
|
file = &MapFile{Mode: fs.ModeDir}
|
|
}
|
|
return &mapDir{name, mapFileInfo{elem, file}, list, 0}, nil
|
|
}
|
|
|
|
// fsOnly is a wrapper that hides all but the fs.FS methods,
|
|
// to avoid an infinite recursion when implementing special
|
|
// methods in terms of helpers that would use them.
|
|
// (In general, implementing these methods using the package fs helpers
|
|
// is redundant and unnecessary, but having the methods may make
|
|
// MapFS exercise more code paths when used in tests.)
|
|
type fsOnly struct{ fs.FS }
|
|
|
|
func (fsys MapFS) ReadFile(name string) ([]byte, error) {
|
|
return fs.ReadFile(fsOnly{fsys}, name)
|
|
}
|
|
|
|
func (fsys MapFS) Stat(name string) (fs.FileInfo, error) {
|
|
return fs.Stat(fsOnly{fsys}, name)
|
|
}
|
|
|
|
func (fsys MapFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
|
return fs.ReadDir(fsOnly{fsys}, name)
|
|
}
|
|
|
|
func (fsys MapFS) Glob(pattern string) ([]string, error) {
|
|
return fs.Glob(fsOnly{fsys}, pattern)
|
|
}
|
|
|
|
type noSub struct {
|
|
MapFS
|
|
}
|
|
|
|
func (noSub) Sub() {} // not the fs.SubFS signature
|
|
|
|
func (fsys MapFS) Sub(dir string) (fs.FS, error) {
|
|
return fs.Sub(noSub{fsys}, dir)
|
|
}
|
|
|
|
// A mapFileInfo implements fs.FileInfo and fs.DirEntry for a given map file.
|
|
type mapFileInfo struct {
|
|
name string
|
|
f *MapFile
|
|
}
|
|
|
|
func (i *mapFileInfo) Name() string { return i.name }
|
|
func (i *mapFileInfo) Size() int64 { return int64(len(i.f.Data)) }
|
|
func (i *mapFileInfo) Mode() fs.FileMode { return i.f.Mode }
|
|
func (i *mapFileInfo) Type() fs.FileMode { return i.f.Mode.Type() }
|
|
func (i *mapFileInfo) ModTime() time.Time { return i.f.ModTime }
|
|
func (i *mapFileInfo) IsDir() bool { return i.f.Mode&fs.ModeDir != 0 }
|
|
func (i *mapFileInfo) Sys() any { return i.f.Sys }
|
|
func (i *mapFileInfo) Info() (fs.FileInfo, error) { return i, nil }
|
|
|
|
// An openMapFile is a regular (non-directory) fs.File open for reading.
|
|
type openMapFile struct {
|
|
path string
|
|
mapFileInfo
|
|
offset int64
|
|
}
|
|
|
|
func (f *openMapFile) Stat() (fs.FileInfo, error) { return &f.mapFileInfo, nil }
|
|
|
|
func (f *openMapFile) Close() error { return nil }
|
|
|
|
func (f *openMapFile) Read(b []byte) (int, error) {
|
|
if f.offset >= int64(len(f.f.Data)) {
|
|
return 0, io.EOF
|
|
}
|
|
if f.offset < 0 {
|
|
return 0, &fs.PathError{Op: "read", Path: f.path, Err: fs.ErrInvalid}
|
|
}
|
|
n := copy(b, f.f.Data[f.offset:])
|
|
f.offset += int64(n)
|
|
return n, nil
|
|
}
|
|
|
|
func (f *openMapFile) Seek(offset int64, whence int) (int64, error) {
|
|
switch whence {
|
|
case 0:
|
|
// offset += 0
|
|
case 1:
|
|
offset += f.offset
|
|
case 2:
|
|
offset += int64(len(f.f.Data))
|
|
}
|
|
if offset < 0 || offset > int64(len(f.f.Data)) {
|
|
return 0, &fs.PathError{Op: "seek", Path: f.path, Err: fs.ErrInvalid}
|
|
}
|
|
f.offset = offset
|
|
return offset, nil
|
|
}
|
|
|
|
func (f *openMapFile) ReadAt(b []byte, offset int64) (int, error) {
|
|
if offset < 0 || offset > int64(len(f.f.Data)) {
|
|
return 0, &fs.PathError{Op: "read", Path: f.path, Err: fs.ErrInvalid}
|
|
}
|
|
n := copy(b, f.f.Data[offset:])
|
|
if n < len(b) {
|
|
return n, io.EOF
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// A mapDir is a directory fs.File (so also an fs.ReadDirFile) open for reading.
|
|
type mapDir struct {
|
|
path string
|
|
mapFileInfo
|
|
entry []mapFileInfo
|
|
offset int
|
|
}
|
|
|
|
func (d *mapDir) Stat() (fs.FileInfo, error) { return &d.mapFileInfo, nil }
|
|
func (d *mapDir) Close() error { return nil }
|
|
func (d *mapDir) Read(b []byte) (int, error) {
|
|
return 0, &fs.PathError{Op: "read", Path: d.path, Err: fs.ErrInvalid}
|
|
}
|
|
|
|
func (d *mapDir) ReadDir(count int) ([]fs.DirEntry, error) {
|
|
n := len(d.entry) - d.offset
|
|
if n == 0 && count > 0 {
|
|
return nil, io.EOF
|
|
}
|
|
if count > 0 && n > count {
|
|
n = count
|
|
}
|
|
list := make([]fs.DirEntry, n)
|
|
for i := range list {
|
|
list[i] = &d.entry[d.offset+i]
|
|
}
|
|
d.offset += n
|
|
return list, nil
|
|
}
|