Compare commits
3 Commits
fce7698b44
...
bb9617994f
Author | SHA1 | Date | |
---|---|---|---|
bb9617994f | |||
2b23424744 | |||
356d261214 |
0
source/coral.cpp
Normal file → Executable file
0
source/coral.cpp
Normal file → Executable file
56
source/coral/files.cpp
Normal file → Executable file
56
source/coral/files.cpp
Normal file → Executable 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) {}
|
||||
};
|
||||
|
0
source/coral/functional.cpp
Normal file → Executable file
0
source/coral/functional.cpp
Normal file → Executable file
0
source/coral/image.cpp
Normal file → Executable file
0
source/coral/image.cpp
Normal file → Executable file
0
source/coral/io.cpp
Normal file → Executable file
0
source/coral/io.cpp
Normal file → Executable file
0
source/coral/math.cpp
Normal file → Executable file
0
source/coral/math.cpp
Normal file → Executable file
0
source/coral/stack.cpp
Normal file → Executable file
0
source/coral/stack.cpp
Normal file → Executable file
111
source/oar.cpp
Normal file → Executable file
111
source/oar.cpp
Normal file → Executable 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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user