Replace coral::fs walker closure with coral::file_walker
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
kayomn 2023-02-28 01:16:19 +00:00
parent 2b23424744
commit bb9617994f
2 changed files with 91 additions and 76 deletions

View File

@ -123,6 +123,24 @@ export namespace coral {
virtual expected<u64, io_error> tell() = 0;
};
/**
* Enumerator a file tree recursively, returning the absolute path of each file within.
*/
struct file_walker {
virtual ~file_walker() {}
/**
* Returns `true` if there are paths pending enumeration, otherwise `false`.
*/
virtual bool has_next() = 0;
/**
* Attempts to enumerate over the next absolute [path] in the file tree, returning it or an [io_error] if the
* operation failed for whatever reason.
*/
virtual expected<path, io_error> next() = 0;
};
/**
* [writer] that has a known range of data and may attempt to traverse it freely.
*/
@ -144,47 +162,31 @@ export namespace coral {
* Platform-generalized file system interface.
*/
struct fs {
/**
* Errors that may occur while trying to walk a file tree.
*
* [walk_error::end_of_walk] reports that there are no more paths left in the file tree that to traverse.
*
* [walk_error::io_unavailable] indicates that an implementation-defined I/O error has occured during traversal
* of the file tree and failed to recover the next file tree path as a result.
*/
enum class walk_error {
end_of_walk,
io_unavailable,
};
/**
* Supplier of file paths from a file tree walking context created by calling [walk_files].
*
* Each subsequent invocation will produce either a valid path in the file tree from `target_path` or a
* [walk_error], with [walk_error::end_of_walk] signalling that there are no more paths left in the file tree to
* traverse. See [walk_error] for more details on potential errors.
*/
using walker = closure<expected<path, walk_error>()>;
virtual ~fs() {};
/**
* Attempts to read the file in `target_path`, calling `then` if it was successfully opened for reading and
* passing the [file_reader] context along.
*
* See [file_reader] for more information on how to read from the file.
*/
virtual void read_file(path const & target_path, closure<void(file_reader &)> const & then) {}
/**
* Attempts to walk the file tree from `target_path`, calling `then` if it was successfully opened for walking
* and passing the [walker] context along.
* and passing the [file_walker] context along.
*
* See [walker] for more information on how to use it to traverse the file tree.
* See [file_walker] for more information on how to traverse the file tree.
*/
virtual void walk_files(path const & target_path, closure<void(walker const &)> const & then) {}
virtual void walk_files(path const & target_path, closure<void(file_walker &)> const & then) {}
/**
* Attempts to write the file in the file system located at `target_path`, calling `then` if it was successfully
* opened for writing and passing the [file_writer] context along.
* Attempts to write a file in the file system located at `target_path`, calling `then` if it was successfully
* created and / or opened for writing and passing the [file_writer] context along.
*
* See [file_writer] for more information on how to write to the file.
*
* *Note*: Any file already existing at `target_path` will be overwritten to create a new file for writing.
*/
virtual void write_file(path const & target_path, closure<void(file_writer &)> const & then) {}
};

View File

@ -9,6 +9,7 @@ import coral.stack;
using coral::closure;
using coral::expected;
using coral::file_reader;
using coral::file_walker;
using coral::fs;
using coral::io_error;
using coral::path;
@ -86,7 +87,7 @@ static_assert(block::is_sizeof(512));
/**
* Archive entry access interface.
*/
struct entry : public file_reader {
struct entry final : public file_reader {
/**
* Results of a find operation performed on an [archive_file].
*
@ -213,6 +214,50 @@ struct entry : public file_reader {
u64 data_cursor {0};
};
struct walker final : public file_walker {
entry & archive_entry;
u64 index {0};
u64 count {0};
walker(entry & archive_entry) : archive_entry{archive_entry} {}
bool has_next() override {
return this->index < this->count;
}
expected<path, io_error> next() override {
constexpr usize path_size {sizeof(path)};
u8 path_buffer[path_size] {0};
// Read verify integrity.
{
expected const data_read {archive_entry.read(path_buffer)};
if (data_read.is_error()) return this->error();
switch (*data_read.ok()) {
case path_size: break;
case 0: return path{};
default: return this->error();
}
}
// Verify existence of zero terminator in path.
if (!coral::find_last(path_buffer, 0).has_value()) return this->error();
return path{}.joined(coral::slice{path_buffer}.as_chars());
}
private:
io_error error() {
this->index = this->count;
return io_error::unavailable;
}
};
export namespace oar {
struct archive : public fs {
archive(fs * backing_fs, path const & archive_path) {
@ -223,34 +268,15 @@ export namespace oar {
/**
* See [fs::walk_files].
*/
void walk_files(path const & target_path, closure<void(walker const &)> const & then) override {
void walk_files(path const & target_path, closure<void(file_walker &)> const & then) override {
this->backing_fs->read_file(this->archive_path, [&](file_reader & archive_reader) {
entry archive_entry{&archive_reader};
if (archive_entry.find(entry_kind::directory, target_path) != entry::find_result::ok) return;
then([&]() -> expected<path, walk_error> {
constexpr usize path_size {sizeof(path)};
u8 path_buffer[path_size] {0};
walker archive_walker {archive_entry};
// Read verify integrity.
{
expected const data_read {archive_entry.read(path_buffer)};
if (data_read.is_error()) return walk_error::io_unavailable;
switch (*data_read.ok()) {
case path_size: break;
case 0: return walk_error::end_of_walk;
default: return walk_error::io_unavailable;
}
}
// Verify existence of zero terminator in path.
if (!coral::find_last(path_buffer, 0).has_value()) return walk_error::io_unavailable;
return {*reinterpret_cast<path const *>(path_buffer)};
});
then(archive_walker);
});
}
@ -293,35 +319,22 @@ export namespace oar {
bool has_io_error {false};
bool is_out_of_memory {false};
input_fs.walk_files(input_path, [&](fs::walker const & walk) {
coral::expected walked_path {walk()};
input_fs.walk_files(input_path, [&](file_walker & walker) {
while (walker.has_next()) {
coral::expected const walked_path {walker.next()};
while (walked_path.is_ok()) {
is_out_of_memory = archive_blocks.push({.layout = {
.path = *walked_path.ok()
}}) != coral::push_result::ok;
if (walked_path.is_error()) {
has_io_error = true;
if (is_out_of_memory) return;
file_count += 1;
walked_path = walk();
}
walked_path.error().and_then([&](fs::walk_error walk_error) {
switch (walk_error) {
case fs::walk_error::io_unavailable: {
has_io_error = true;
return;
}
case fs::walk_error::end_of_walk: {
has_io_error = true;
return;
}
return;
}
});
if (archive_blocks.push({.layout = {.path = *walked_path.ok()}}) != coral::push_result::ok) {
is_out_of_memory = true;
return;
}
}
});
if (has_io_error) return bundle_result::io_error;