Compare commits

..

No commits in common. "bb9617994f7c979a343d63b38282570ee8d895b3" and "fce7698b444dfa7195da9a0832f4b7491c652286" have entirely different histories.

8 changed files with 75 additions and 90 deletions

0
source/coral.cpp Executable file → Normal file
View File

56
source/coral/files.cpp Executable file → Normal file
View File

@ -123,24 +123,6 @@ export namespace coral {
virtual expected<u64, io_error> tell() = 0; 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. * [writer] that has a known range of data and may attempt to traverse it freely.
*/ */
@ -162,31 +144,47 @@ export namespace coral {
* Platform-generalized file system interface. * Platform-generalized file system interface.
*/ */
struct fs { 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() {}; virtual ~fs() {};
/** /**
* Attempts to read the file in `target_path`, calling `then` if it was successfully opened for reading and * Attempts to read the file in `target_path`, calling `then` if it was successfully opened for reading and
* passing the [file_reader] context along. * 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) {} 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 * Attempts to walk the file tree from `target_path`, calling `then` if it was successfully opened for walking
* and passing the [file_walker] context along. * and passing the [walker] context along.
* *
* See [file_walker] for more information on how to traverse the file tree. * See [walker] for more information on how to use it to traverse the file tree.
*/ */
virtual void walk_files(path const & target_path, closure<void(file_walker &)> const & then) {} virtual void walk_files(path const & target_path, closure<void(walker const &)> const & then) {}
/** /**
* Attempts to write a file in the file system located at `target_path`, calling `then` if it was successfully * Attempts to write the 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. * 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) {} virtual void write_file(path const & target_path, closure<void(file_writer &)> const & then) {}
}; };

0
source/coral/functional.cpp Executable file → Normal file
View File

0
source/coral/image.cpp Executable file → Normal file
View File

0
source/coral/io.cpp Executable file → Normal file
View File

0
source/coral/math.cpp Executable file → Normal file
View File

0
source/coral/stack.cpp Executable file → Normal file
View File

97
source/oar.cpp Executable file → Normal file
View File

@ -9,7 +9,6 @@ import coral.stack;
using coral::closure; using coral::closure;
using coral::expected; using coral::expected;
using coral::file_reader; using coral::file_reader;
using coral::file_walker;
using coral::fs; using coral::fs;
using coral::io_error; using coral::io_error;
using coral::path; using coral::path;
@ -87,7 +86,7 @@ static_assert(block::is_sizeof(512));
/** /**
* Archive entry access interface. * Archive entry access interface.
*/ */
struct entry final : public file_reader { struct entry : public file_reader {
/** /**
* Results of a find operation performed on an [archive_file]. * Results of a find operation performed on an [archive_file].
* *
@ -214,50 +213,6 @@ struct entry final : public file_reader {
u64 data_cursor {0}; 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 { export namespace oar {
struct archive : public fs { struct archive : public fs {
archive(fs * backing_fs, path const & archive_path) { archive(fs * backing_fs, path const & archive_path) {
@ -268,15 +223,34 @@ export namespace oar {
/** /**
* See [fs::walk_files]. * See [fs::walk_files].
*/ */
void walk_files(path const & target_path, closure<void(file_walker &)> const & then) override { void walk_files(path const & target_path, closure<void(walker const &)> const & then) override {
this->backing_fs->read_file(this->archive_path, [&](file_reader & archive_reader) { this->backing_fs->read_file(this->archive_path, [&](file_reader & archive_reader) {
entry archive_entry{&archive_reader}; entry archive_entry{&archive_reader};
if (archive_entry.find(entry_kind::directory, target_path) != entry::find_result::ok) return; if (archive_entry.find(entry_kind::directory, target_path) != entry::find_result::ok) return;
walker archive_walker {archive_entry}; then([&]() -> expected<path, walk_error> {
constexpr usize path_size {sizeof(path)};
u8 path_buffer[path_size] {0};
then(archive_walker); // 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)};
});
}); });
} }
@ -319,23 +293,36 @@ export namespace oar {
bool has_io_error {false}; bool has_io_error {false};
bool is_out_of_memory {false}; bool is_out_of_memory {false};
input_fs.walk_files(input_path, [&](file_walker & walker) { input_fs.walk_files(input_path, [&](fs::walker const & walk) {
while (walker.has_next()) { coral::expected walked_path {walk()};
coral::expected const walked_path {walker.next()};
if (walked_path.is_error()) { while (walked_path.is_ok()) {
is_out_of_memory = archive_blocks.push({.layout = {
.path = *walked_path.ok()
}}) != coral::push_result::ok;
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; has_io_error = true;
return; return;
} }
if (archive_blocks.push({.layout = {.path = *walked_path.ok()}}) != coral::push_result::ok) { case fs::walk_error::end_of_walk: {
is_out_of_memory = true; has_io_error = true;
return; return;
} }
} }
}); });
});
if (has_io_error) return bundle_result::io_error; if (has_io_error) return bundle_result::io_error;