FileSystem

FileSystem is a filesystem interface.

You can access the native filesystem by calling FileSystem::native().

A FileSystem object expects either Windows paths or POSIX-style paths, depending on its implementation. The NativePath class always manipulate paths in the appropriate format for the native filesystem. When working with an abstract FileSystem where the path format is unknown, pathFormat() will return a PathFormat object suitable for manipulating paths in the expected format. See Manipulating Paths for more information.

You can create a virtual filesystem using createVirtualFileSystem(), or even implement your own. Virtual filesystems don't necessarily support all the member functions in the FileSystem interface.

Result Codes

Most FileSystem member functions update an internal result code. This result code is stored in a thread-local variable of type FSResult. Some member functions, such as makeDir(), return the result code explicitly. Other member functions, such as loadTextAutodetect(), do not return the result code explicitly. You can always access the last result code by calling lastResult().

Note that, because the internal result code is thread-local, it's shared across all FileSystem instances. For example, an operation on a virtual filesystem will update the result code for the native filesystem, and vice versa.

In some cases, a function may set the result code to Unknown instead of one of the result codes documented here. Such cases mean that Plywood needs to be updated to recognize the underlying error.

Header File

#include <ply-runtime/filesystem/FileSystem.h>

Also included from <ply-runtime/Base.h>.

Member Functions

static FileSystem* native() [code]

Returns a FileSystem object that interacts with the native filesystem.

PathFormat pathFormat() const [code]

Returns a PathFormat describing the filesystem's expected path format. This object can be used to manipulate paths in the expected format.

FSResult setWorkingDirectory(StringView path) [code]

Sets the working directory of the process in the target filesystem. May not be supported in virtual filesystems.

This function updates the internal result code. The result code is also returned directly. Expected result codes are OK or NotFound.

String getWorkingDirectory() [code]

Gets the working directory of the process in the target filesystem.

This function updates the internal result code. The expected result code is OK.

FSResult lastResult() const [code]

Returns the last result code.

static FSResult setLastResult(FSResult result) [code]

Sets the last result code.

ExistsResult exists(StringView path) [code]

Checks whether a file or directory with the specified pathname exists. The return value is one of File, Directory or NotFound.

This function does not update the internal result code.

bool isDir(StringView path) [code]

Returns true if path specifies the name of a directory. Shorthand for exists(path) == ExistsResult::Directory.

This function does not update the internal result code.

Directory listDir(StringView path, u32 flags = WithSizes | WithTimes) [code]

Returns a Directory object. The Directory object can be used in a range-based for loop to enumerate the contents of a directory. Such a range-based for loop iterates over a sequence of DirectoryEntry objects. It is recommended to iterate by reference in order to avoid copying the DirectoryEntry object. Each DirectoryEntry takes the following form:

struct DirectoryEntry {
    String name;
    bool isDir;
    u64 fileSize;           // Only valid if WithSizes was specified
    double creationTime;    // Only valid if WithTimes was specified
    double accessTime;
    double modificationTime;
};

The entries are returned in an arbitrary order, and the special directory entries "." and ".." are not enumerated.

The flags argument accepts a bitwise-or of zero or more of the following values:

  • FileSystem::WithSizes
  • FileSystem::WithFlags

The fileSize member of DirectoryEntry is only valid if WithSizes was set. The creationTime, accessTime and modificationTime members are only valid if WithTimes was set. Using these flags is likely faster than calling getFileStatus() on the individual files, especially on Windows. Conversely, if the additional information is not needed, it might be faster to omit these flags on some platforms.

The following example outputs a list of non-directory files in the current directory:

StringWriter sw = StdOut::strWriter();
for (const DirectoryEntry& entry : FileSystem::native()->listDir(".", 0)) {
    if (!entry.isDir) {
        sw << entry.name << '\n';
    }
}

The initial call to listDir() updates the internal result code. Expected result codes are OK, NotFound or AccessDenied. Each iteration of the loop updates the internal result code as well. Expected result codes are OK or NotFound (if, for example, the volume was removed during iteration).

Walk walk(StringView path, u32 flags = WithSizes | WithTimes) [code]

Similar to os.walk() in Python. Returns a FileSystem::Walk object. The FileSystem::Walk object can be used in a range-based for loop to enumerate the contents of a directory tree. Such a range-based for loop iterates over a sequence of WalkTriple objects, with one WalkTriple object for each directory in the tree rooted at top. It is recommended to iterate by reference in order to avoid copying the WalkTriple object. Each WalkTriple takes the following form:

struct WalkTriple {
    struct FileInfo {
        String name;
        u64 fileSize;           // Only valid if WithSizes was specified
        double creationTime;    // Only valid if WithTimes was specified
        double accessTime;
        double modificationTime;
    };

    String dirPath;
    Array<String> dirNames;
    Array<FileInfo> files;
};

dirPath contains the path to the directory. dirNames is a list of the names of the subdirectories in dirPath excluding "." and "..". files is a list of WalkTriple::FileInfo objects for the non-directory files in dirPath. Note that the names in the lists contain no path components. To get a full path (which begins with top) to a file or directory in dirPath, call pathFormat().join(dirPath, name).

On each iteration of the loop, the caller can modify the dirNames list in-place, and walk() will only recurse into the subdirectories whose names remain in dirNames. This can be used to prune the search, impose a specific order of visiting, or inform walk() about directories that the caller creates or renames.

The flags argument accepts a bitwise-or of zero or more of the following values:

  • FileSystem::WithSizes
  • FileSystem::WithFlags

The fileSize member of WalkTriple::FileInfo is only valid if WithSizes was set. The creationTime, accessTime and modificationTime members are only valid if WithTimes was set. Using these flags is likely faster than calling getFileStatus() on the individual files, especially on Windows. Conversely, if the additional information is not needed, it might be faster to omit these flags on some platforms.

The following example displays the number of bytes taken by non-directory files in each directory under the starting directory, except that it doesn't look under any subdirectory that starts with a dot ".":

StringWriter sw = StdOut::createStringWriter();
for (WalkTriple& triple : fs->walk(".", FileSystem::WithSizes)) {
    // Calculate the number of bytes taken by non-directory files
    u64 sum = 0;
    for (const WalkTriple::FileInfo& file : triple.files) {
        sum += file.fileSize;
    }
    sw.format("{}: {} bytes\n", triple.dirPath, sum);

    // Prune subdirectories that start with "."
    for (u32 i = 0; i < triple.dirNames.numItems(); i++) {
        if (triple.dirNames[i].startsWith(".")) {
            triple.dirNames.erase(i);
            i--;
        }
    }
}

The initial call to walk() updates the internal result code. Expected result codes are OK, NotFound or AccessDenied. Each iteration of the loop updates the internal result code as well. Expected result codes are OK, NotFound (if, for example, the volume was removed during iteration) or AccessDenied.

FSResult makeDir(StringView path) [code]

Creates a new directory. The parent directory must already exist.

This function updates the internal result code. The result code is also returned directly. Expected result codes are OK, AlreadyExists or AccessDenied.

FSResult makeDirs(StringView path) [code]

Recursive directory creation function. Like makeDir(), but creates all intermediate-level directories needed to contain the leaf directory. Similar to os.makedirs() in Python.

This function updates the internal result code. The result code is also returned directly. Expected result codes are OK or AccessDenied.

FSResult moveFile(StringView srcPath, StringView dstPath) [code]

Renames the file at srcPath to dstPath. If dstPath already exists and is a file, it will be silently replaced.

This function updates the internal result code. The result code is also returned directly.

FSResult deleteFile(StringView path) [code]

Deletes the file at path.

This function updates the internal result code. The result code is also returned directly.

FSResult removeDirTree(StringView dirPath) [code]

Removes the directory tree at path. The specified directory and all of its contents are deleted recursively.

This function updates the internal result code. The result code is also returned directly. Expected result codes are OK or AccessDenied.

FileStatus getFileStatus(StringView path) [code]

Returns a FileStatus object for the file located at path. FileStatus contains the following members:

struct FileStatus {
    FSResult result;            // Result of getFileStatus()
    u64 fileSize;               // Size of the file in bytes
    double creationTime;        // The file's POSIX creation time
    double accessTime;          // The file's POSIX access time
    double modificationTime;    // The file's POSIX modification time
};

creationTime, accessTime and modificationTime are expressed in POSIX time, which is the number of seconds since January 1, 1970.

This function updates the internal result code. The result code is also returned in the result member. Expected result codes are OK or NotFound.

Owned<InPipe> openPipeForRead(StringView path) [code]

Returns an InPipe that reads the raw contents of the specified file, or nullptr if the file could not be opened.

This function updates the internal result code. Expected result codes are OK, NotFound, AccessDenied or Locked.

InPipe is a low-level class that doesn't perform any application-level buffering. If you plan to read small amounts of data at a time, such when parsing a text or binary file format, it is suggested to wrap the returned InPipe in an InStream. The openStreamForRead() convenience function is provided for this.

Owned<OutPipe> openPipeForWrite(StringView path) [code]

Returns an OutPipe that writes raw data to the specified file, or nullptr if the file could not be opened.

This function updates the internal result code. Expected result codes are OK, NotFound, AccessDenied or Locked.

OutPipe is a low-level class that doesn't perform any application-level buffering. If you plan to write small amounts of data at a time, such when writing a text or binary file format, it is suggested to wrap the returned OutPipe in an OutStream. The openStreamForWrite() convenience function is provided for this.

Owned<InStream> openStreamForRead(StringView path) [code]

Returns an InStream that reads the raw contents of the specified file, or nullptr if the file could not be opened.

This function updates the internal result code. Expected result codes are OK, NotFound or Locked.

Owned<OutStream> openStreamForWrite(StringView path) [code]

Returns an OutStream that writes raw data to the specified file, or nullptr if the file could not be opened.

This function updates the internal result code. Expected result codes are OK, NotFound, AccessDenied or Locked.

Owned<StringReader> openTextForRead(StringView path, const TextFormat& textFormat) [code]

Returns a StringReader that reads raw data from the specified text file and converts it to UTF-8 with Unix-style newlines, or nullptr if the file could not be opened. The text file is expected to have the file format described by textFormat. Any byte order mark (BOM) in the file is skipped. See TextFormat for more information on supported text file formats.

This function updates the internal result code. Expected result codes are OK, NotFound, AccessDenied or Locked.

The openTextForRead() function is equivalent to the following:

Owned<InStream> ins = fs->openStreamForRead(path);
if (!ins)
    return nullptr;
return textFormat.createImporter(std::move(ins));
Tuple<Owned<StringReader>, TextFormat> openTextForReadAutodetect(StringView path) [code]

Returns a Tuple. The first tuple item is a StringReader that reads raw data from the specified text file and converts it to UTF-8 with Unix-style newlines, or nullptr if the file could not be opened. The text file format is detected automatically and returned as the second tuple item. Any byte order mark (BOM) in the text file is skipped. See TextFormat for more information on supported text file formats.

This function updates the internal result code. Expected result codes are OK, NotFound, AccessDenied or Locked.

The openTextForReadAutodetect() function is equivalent to the following:

Owned<InStream> ins = fs->openStreamForRead(path);
if (!ins)
    return {nullptr, TextFormat{}};
TextFormat textFormat = TextFormat::autodetect(ins);
return {textFormat.createImporter(std::move(ins)), textformat};
Buffer loadBinary(StringView path) [code]

Returns a Buffer containing the raw contents of the specified file, or an empty Buffer if the file could not be opened.

To check if the file was opened successfuly, call lastResult(). Expected result codes are OK, NotFound, AccessDenied or Locked.

String loadText(StringView path, const TextFormat& textFormat) [code]

Returns a String containing the contents of the specified text file converted to UTF-8 with Unix-style newlines, or an empty String if the file could not be opened. The text file is expected to have the file format described by textFormat. Any byte order mark (BOM) in the text file is skipped. See TextFormat for more information on supported text file formats.

To check if the file was opened successfuly, call lastResult(). Expected result codes are OK, NotFound, AccessDenied or Locked.

Tuple<String, TextFormat> loadTextAutodetect(StringView path) [code]

Returns a Tuple. The first tuple item is a String containing the contents of the specified text file converted to UTF-8 with Unix-style newlines, or an empty String if the file could not be opened. The text file format is detected automatically and returned as the second tuple item. Any byte order mark (BOM) in the text file is skipped. See TextFormat for more information on supported text file formats.

To check if the file was opened successfuly, call lastResult(). Expected result codes are OK, NotFound, AccessDenied or Locked.

Owned<StringWriter> openTextForWrite(StringView path, const TextFormat& textFormat) [code]

Returns a StringWriter that writes text to the specified text file in the specified format, or nullptr if the file could not be opened. The StringWriter expects UTF-8-encoded text. The StringWriter accepts both Windows and Unix-style newlines; all newlines will be converted to the format described by textFormat. See TextFormat for more information on supported text file formats.

This function updates the internal result code. Expected result codes are OK, NotFound, AccessDenied or Locked.

The openTextForWrite() function is equivalent to the following:

Owned<OutStream> outs = fs->openTextForWrite(path);
if (!outs)
    return nullptr;
return TextFormat::createExporter(std::move(outs), textFormat);
FSResult makeDirsAndSaveBinaryIfDifferent(StringView path, ConstBufferView contents) [code]

First, this function tries to load the raw contents of the specified file. If the load succeeds and its raw contents match contents exactly, the function returns Unchanged. Otherwise, if the parent directories of path don't exist, it attempts to create them. If that succeeds, it saves contents to a temporary file in the same folder as path. If that succeeds, it renames the temporary file to path, replacing any original contents.

In all cases, this function updates the internal result code. The result code is also returned directly.

FSResult makeDirsAndSaveTextIfDifferent(StringView path, StringView strContents, const TextFormat& textFormat) [code]

First, this function converts strContents to a raw memory buffer using the text file format specified by textFormat. Then, it tries to load the raw contents of the specified file. If the load succeeds and its raw contents match the contents of the raw memory buffer exactly, the function returns Unchanged. Otherwise, if the parent directories of path don't exist, it attempts to create them. If that succeeds, it saves the raw memory buffer to a temporary file in the same folder as path. If that succeeds, it renames the temporary file to path, replacing any original contents. See TextFormat for more information on supported text file formats.

In all cases, this function updates the internal result code. The result code is also returned directly.