promises for async vfs operations

This commit is contained in:
2018-12-13 02:12:18 +07:00
parent ac885a51ef
commit af95e86764
9 changed files with 193 additions and 154 deletions

View File

@@ -178,16 +178,16 @@ namespace promise_hpp
then([
n = next,
f = std::forward<ResolveF>(on_resolve)
](const T& v) mutable {
](auto&& v) mutable {
auto np = invoke_hpp::invoke(
std::forward<decltype(f)>(f),
v);
np.then([n]() mutable {
std::forward<decltype(v)>(v));
std::move(np).then([n]() mutable {
n.resolve();
}, [n](std::exception_ptr e) mutable {
}).except([n](std::exception_ptr e) mutable {
n.reject(e);
});
}, [n = next](std::exception_ptr e) mutable {
}).except([n = next](std::exception_ptr e) mutable {
n.reject(e);
});
@@ -205,16 +205,16 @@ namespace promise_hpp
then([
n = next,
f = std::forward<ResolveF>(on_resolve)
](const T& v) mutable {
](auto&& v) mutable {
auto np = invoke_hpp::invoke(
std::forward<decltype(f)>(f),
v);
np.then([n](const typename ResolveFR::value_type& nv) mutable {
n.resolve(nv);
}, [n](std::exception_ptr e) mutable {
std::forward<decltype(v)>(v));
std::move(np).then([n](auto&& nv) mutable {
n.resolve(std::forward<decltype(nv)>(nv));
}).except([n](std::exception_ptr e) mutable {
n.reject(e);
});
}, [n = next](std::exception_ptr e) mutable {
}).except([n = next](std::exception_ptr e) mutable {
n.reject(e);
});
@@ -225,10 +225,10 @@ namespace promise_hpp
auto then_all(ResolveF&& on_resolve) {
return then([
f = std::forward<ResolveF>(on_resolve)
](const T& v) mutable {
](auto&& v) mutable {
auto r = invoke_hpp::invoke(
std::forward<decltype(f)>(f),
v);
std::forward<decltype(v)>(v));
return make_all_promise(std::move(r));
});
}
@@ -237,10 +237,10 @@ namespace promise_hpp
auto then_any(ResolveF&& on_resolve) {
return then([
f = std::forward<ResolveF>(on_resolve)
](const T& v) mutable {
](auto&& v) mutable {
auto r = invoke_hpp::invoke(
std::forward<decltype(f)>(f),
v);
std::forward<decltype(v)>(v));
return make_any_promise(std::move(r));
});
}
@@ -274,14 +274,13 @@ namespace promise_hpp
template < typename RejectF >
promise<T> except(RejectF&& on_reject) {
return then(
[](const T& value) { return value; },
[](auto&& v) { return std::forward<decltype(v)>(v); },
std::forward<RejectF>(on_reject));
}
template < typename U >
bool resolve(U&& value) {
return state_->resolve(
std::forward<U>(value));
return state_->resolve(std::forward<U>(value));
}
bool reject(std::exception_ptr e) noexcept {
@@ -298,6 +297,14 @@ namespace promise_hpp
return state_->get();
}
const T& get_or_default(const T& def) const noexcept {
try {
return get();
} catch (...) {
return def;
}
}
void wait() const noexcept {
state_->wait();
}
@@ -391,10 +398,10 @@ namespace promise_hpp
template < typename U, typename ResolveF, typename RejectF >
std::enable_if_t<std::is_void<U>::value, void>
attach(promise<U>& next, ResolveF&& resolve, RejectF&& reject) {
attach(promise<U>& next, ResolveF&& on_resolve, RejectF&& on_reject) {
auto reject_h = [
n = next,
f = std::forward<RejectF>(reject)
f = std::forward<RejectF>(on_reject)
](std::exception_ptr e) mutable {
try {
invoke_hpp::invoke(
@@ -408,18 +415,15 @@ namespace promise_hpp
auto resolve_h = [
n = next,
f = std::forward<ResolveF>(resolve),
j = reject_h
](const T& v) mutable {
f = std::forward<ResolveF>(on_resolve)
](auto&& v) mutable {
try {
invoke_hpp::invoke(
std::forward<decltype(f)>(f),
v);
std::forward<decltype(v)>(v));
n.resolve();
} catch (...) {
invoke_hpp::invoke(
std::move(j),
std::current_exception());
n.reject(std::current_exception());
}
};
@@ -429,10 +433,10 @@ namespace promise_hpp
template < typename U, typename ResolveF, typename RejectF >
std::enable_if_t<!std::is_void<U>::value, void>
attach(promise<U>& next, ResolveF&& resolve, RejectF&& reject) {
attach(promise<U>& next, ResolveF&& on_resolve, RejectF&& on_reject) {
auto reject_h = [
n = next,
f = std::forward<RejectF>(reject)
f = std::forward<RejectF>(on_reject)
](std::exception_ptr e) mutable {
try {
invoke_hpp::invoke(
@@ -446,18 +450,15 @@ namespace promise_hpp
auto resolve_h = [
n = next,
f = std::forward<ResolveF>(resolve),
j = reject_h
](const T& v) mutable {
f = std::forward<ResolveF>(on_resolve)
](auto&& v) mutable {
try {
auto r = invoke_hpp::invoke(
std::forward<decltype(f)>(f),
v);
std::forward<decltype(v)>(v));
n.resolve(std::move(r));
} catch (...) {
invoke_hpp::invoke(
std::move(j),
std::current_exception());
n.reject(std::current_exception());
}
};
@@ -576,12 +577,12 @@ namespace promise_hpp
]() mutable {
auto np = invoke_hpp::invoke(
std::forward<decltype(f)>(f));
np.then([n]() mutable {
std::move(np).then([n]() mutable {
n.resolve();
}, [n](std::exception_ptr e) mutable {
}).except([n](std::exception_ptr e) mutable {
n.reject(e);
});
}, [n = next](std::exception_ptr e) mutable {
}).except([n = next](std::exception_ptr e) mutable {
n.reject(e);
});
@@ -602,12 +603,12 @@ namespace promise_hpp
]() mutable {
auto np = invoke_hpp::invoke(
std::forward<decltype(f)>(f));
np.then([n](const typename ResolveFR::value_type& nv) mutable {
n.resolve(nv);
}, [n](std::exception_ptr e) mutable {
std::move(np).then([n](auto&& nv) mutable {
n.resolve(std::forward<decltype(nv)>(nv));
}).except([n](std::exception_ptr e) mutable {
n.reject(e);
});
}, [n = next](std::exception_ptr e) mutable {
}).except([n = next](std::exception_ptr e) mutable {
n.reject(e);
});
@@ -687,6 +688,14 @@ namespace promise_hpp
state_->get();
}
void get_or_default() const noexcept {
try {
return get();
} catch (...) {
// nothing
}
}
void wait() const noexcept {
state_->wait();
}
@@ -777,10 +786,10 @@ namespace promise_hpp
template < typename U, typename ResolveF, typename RejectF >
std::enable_if_t<std::is_void<U>::value, void>
attach(promise<U>& next, ResolveF&& resolve, RejectF&& reject) {
attach(promise<U>& next, ResolveF&& on_resolve, RejectF&& on_reject) {
auto reject_h = [
n = next,
f = std::forward<RejectF>(reject)
f = std::forward<RejectF>(on_reject)
](std::exception_ptr e) mutable {
try {
invoke_hpp::invoke(
@@ -794,17 +803,14 @@ namespace promise_hpp
auto resolve_h = [
n = next,
f = std::forward<ResolveF>(resolve),
j = reject_h
f = std::forward<ResolveF>(on_resolve)
]() mutable {
try {
invoke_hpp::invoke(
std::forward<decltype(f)>(f));
n.resolve();
} catch (...) {
invoke_hpp::invoke(
std::move(j),
std::current_exception());
n.reject(std::current_exception());
}
};
@@ -814,10 +820,10 @@ namespace promise_hpp
template < typename U, typename ResolveF, typename RejectF >
std::enable_if_t<!std::is_void<U>::value, void>
attach(promise<U>& next, ResolveF&& resolve, RejectF&& reject) {
attach(promise<U>& next, ResolveF&& on_resolve, RejectF&& on_reject) {
auto reject_h = [
n = next,
f = std::forward<RejectF>(reject)
f = std::forward<RejectF>(on_reject)
](std::exception_ptr e) mutable {
try {
invoke_hpp::invoke(
@@ -831,17 +837,14 @@ namespace promise_hpp
auto resolve_h = [
n = next,
f = std::forward<ResolveF>(resolve),
j = reject_h
f = std::forward<ResolveF>(on_resolve)
]() mutable {
try {
auto r = invoke_hpp::invoke(
std::forward<decltype(f)>(f));
n.resolve(std::move(r));
} catch (...) {
invoke_hpp::invoke(
std::move(j),
std::current_exception());
n.reject(std::current_exception());
}
};
@@ -1020,11 +1023,11 @@ namespace promise_hpp
context,
resolver,
result_index
](const child_promise_value_t& v) mutable {
if ( context->apply_result(result_index, v) ) {
](auto&& v) mutable {
if ( context->apply_result(result_index, std::forward<decltype(v)>(v)) ) {
resolver(std::move(context->results));
}
}, rejector);
}).except(rejector);
}
});
}
@@ -1051,7 +1054,7 @@ namespace promise_hpp
return make_promise<child_promise_value_t>([begin, end](auto&& resolver, auto&& rejector){
for ( auto iter = begin; iter != end; ++iter ) {
(*iter).then(resolver, rejector);
(*iter).then(resolver).except(rejector);
}
});
}

View File

@@ -17,6 +17,12 @@ namespace e2d
}
};
class vfs_load_async_exception final : public exception {
const char* what() const noexcept final {
return "vfs load async exception";
}
};
class vfs final : public module<vfs> {
public:
vfs();
@@ -27,8 +33,7 @@ namespace e2d
virtual ~file_source() noexcept = default;
virtual bool valid() const noexcept = 0;
virtual bool exists(str_view path) const = 0;
virtual input_stream_uptr open(str_view path) const = 0;
virtual std::pair<buffer,bool> load(str_view path) const = 0;
virtual input_stream_uptr read(str_view path) const = 0;
virtual output_stream_uptr write(str_view path, bool append) const = 0;
virtual bool trace(str_view path, filesystem::trace_func func) const = 0;
};
@@ -43,10 +48,15 @@ namespace e2d
bool unregister_scheme_alias(str_view scheme);
bool exists(const url& url) const;
input_stream_uptr open(const url& url) const;
std::pair<buffer,bool> load(const url& url) const;
input_stream_uptr read(const url& url) const;
output_stream_uptr write(const url& url, bool append) const;
stdex::promise<std::pair<buffer,bool>> load_async(const url& url) const;
bool load(const url& url, buffer& dst) const;
stdex::promise<buffer> load_async(const url& url) const;
bool load_as_string(const url& url, str& dst) const;
stdex::promise<str> load_as_string_async(const url& url) const;
template < typename Iter >
bool extract(const url& url, Iter result_iter) const;
@@ -64,8 +74,7 @@ namespace e2d
~archive_file_source() noexcept final;
bool valid() const noexcept final;
bool exists(str_view path) const final;
input_stream_uptr open(str_view path) const final;
std::pair<buffer,bool> load(str_view path) const final;
input_stream_uptr read(str_view path) const final;
output_stream_uptr write(str_view path, bool append) const final;
bool trace(str_view path, filesystem::trace_func func) const final;
private:
@@ -79,8 +88,7 @@ namespace e2d
~filesystem_file_source() noexcept final;
bool valid() const noexcept final;
bool exists(str_view path) const final;
input_stream_uptr open(str_view path) const final;
std::pair<buffer,bool> load(str_view path) const final;
input_stream_uptr read(str_view path) const final;
output_stream_uptr write(str_view path, bool append) const final;
bool trace(str_view path, filesystem::trace_func func) const final;
};

View File

@@ -95,7 +95,7 @@ namespace
bool initialize() final {
the<vfs>().register_scheme<archive_file_source>(
"piratepack",
the<vfs>().open(url("resources://bin/kenney_piratepack.zip")));
the<vfs>().read(url("resources://bin/kenney_piratepack.zip")));
the<vfs>().register_scheme_alias(
"ships",
@@ -104,9 +104,9 @@ namespace
shader_ = the<render>().create_shader(
vs_source_cstr, fs_source_cstr);
texture1_ = the<render>().create_texture(
the<vfs>().open(url("ships://ship (2).png")));
the<vfs>().read(url("ships://ship (2).png")));
texture2_ = the<render>().create_texture(
the<vfs>().open(url("ships://ship (19).png")));
the<vfs>().read(url("ships://ship (19).png")));
if ( !shader_ || !texture1_ || !texture2_ ) {
return false;

View File

@@ -155,7 +155,7 @@ namespace
bool initialize() final {
the<vfs>().register_scheme<archive_file_source>(
"piratepack",
the<vfs>().open(url("resources://bin/kenney_piratepack.zip")));
the<vfs>().read(url("resources://bin/kenney_piratepack.zip")));
the<vfs>().register_scheme_alias(
"ships",
@@ -165,7 +165,7 @@ namespace
vs_source_cstr, fs_source_cstr);
texture_ = the<render>().create_texture(
the<vfs>().open(url("ships://ship (3).png")));
the<vfs>().read(url("ships://ship (3).png")));
if ( !shader_ || !texture_ ) {
return false;

View File

@@ -110,7 +110,7 @@ namespace
bool initialize() final {
the<vfs>().register_scheme<archive_file_source>(
"piratepack",
the<vfs>().open(url("resources://bin/kenney_piratepack.zip")));
the<vfs>().read(url("resources://bin/kenney_piratepack.zip")));
the<vfs>().register_scheme_alias(
"ships",
@@ -120,7 +120,7 @@ namespace
vs_source_cstr, fs_source_cstr);
texture_ = the<render>().create_texture(
the<vfs>().open(url("ships://ship (3).png")));
the<vfs>().read(url("ships://ship (3).png")));
if ( !shader_ || !texture_ ) {
return false;

View File

@@ -98,7 +98,10 @@ namespace e2d
const auto resolved_url = resolve_url(url);
const auto scheme_iter = schemes.find(resolved_url.scheme());
return (scheme_iter != schemes.cend() && scheme_iter->second)
? stdex::invoke(std::forward<F>(f), scheme_iter->second, resolved_url.path())
? stdex::invoke(
std::forward<F>(f),
scheme_iter->second,
resolved_url.path())
: std::forward<R>(fallback_result);
}
};
@@ -140,22 +143,14 @@ namespace e2d
}, false);
}
input_stream_uptr vfs::open(const url& url) const {
input_stream_uptr vfs::read(const url& url) const {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->with_file_source(url,
[](const file_source_uptr& source, const str& path) {
return source->open(path);
return source->read(path);
}, input_stream_uptr());
}
std::pair<buffer,bool> vfs::load(const url& url) const {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->with_file_source(url,
[](const file_source_uptr& source, const str& path) {
return source->load(path);
}, std::make_pair(buffer(), false));
}
output_stream_uptr vfs::write(const url& url, bool append) const {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->with_file_source(url,
@@ -164,13 +159,40 @@ namespace e2d
}, output_stream_uptr());
}
stdex::promise<std::pair<buffer,bool>> vfs::load_async(const url& url) const {
return state_->worker.async([](input_stream_uptr stream){
buffer buf;
return streams::try_read_tail(buf, stream)
? std::make_pair(std::move(buf), true)
: std::make_pair(buffer(), false);
}, open(url));
bool vfs::load(const url& url, buffer& dst) const {
return load_async(url)
.then([&dst](auto&& src){
dst = std::forward<decltype(src)>(src);
return true;
}).get_or_default(false);
}
stdex::promise<buffer> vfs::load_async(const url& url) const {
return state_->worker.async([this](auto&& url_copy){
buffer content;
if ( !streams::try_read_tail(content, read(url_copy)) ) {
throw vfs_load_async_exception();
}
return content;
}, url);
}
bool vfs::load_as_string(const url& url, str& dst) const {
return load_as_string_async(url)
.then([&dst](auto&& src){
dst = std::forward<decltype(src)>(src);
return true;
}).get_or_default(false);
}
stdex::promise<str> vfs::load_as_string_async(const url& url) const {
return state_->worker.async([this](auto&& url_copy){
str content;
if ( !streams::try_read_tail(content, read(url_copy)) ) {
throw vfs_load_async_exception();
}
return content;
}, url);
}
bool vfs::trace(const url& url, filesystem::trace_func func) const {
@@ -250,7 +272,7 @@ namespace e2d
MZ_ZIP_FLAG_CASE_SENSITIVE);
}
input_stream_uptr archive_file_source::open(str_view path) const {
input_stream_uptr archive_file_source::read(str_view path) const {
try {
struct owned_state_t {
state::archive_ptr archive;
@@ -265,19 +287,6 @@ namespace e2d
}
}
std::pair<buffer,bool> archive_file_source::load(str_view path) const {
std::size_t mem_size = 0;
void* mem = mz_zip_reader_extract_file_to_heap(
state_->archive.get(),
make_utf8(path).c_str(),
&mem_size,
MZ_ZIP_FLAG_CASE_SENSITIVE);
std::unique_ptr<void, decltype(&mz_free)> mem_uptr(mem, mz_free);
return mem
? std::make_pair(buffer(mem, mem_size), true)
: std::make_pair(buffer(), false);
}
output_stream_uptr archive_file_source::write(str_view path, bool append) const {
E2D_UNUSED(path, append);
return nullptr;
@@ -338,17 +347,10 @@ namespace e2d
return filesystem::file_exists(path);
}
input_stream_uptr filesystem_file_source::open(str_view path) const {
input_stream_uptr filesystem_file_source::read(str_view path) const {
return make_read_file(path);
}
std::pair<buffer,bool> filesystem_file_source::load(str_view path) const {
buffer buf;
return filesystem::try_read_all(buf, path)
? std::make_pair(std::move(buf), true)
: std::make_pair(buffer(), false);
}
output_stream_uptr filesystem_file_source::write(str_view path, bool append) const {
if ( !filesystem::create_directory_recursive(path::parent_path(path)) ) {
return nullptr;

View File

@@ -1390,7 +1390,7 @@ namespace e2d
const auto asset_url = library.root() / address;
input_stream_uptr stream = modules::is_initialized<vfs>()
? the<vfs>().open(asset_url)
? the<vfs>().read(asset_url)
: input_stream_uptr();
if ( !stream ) {
@@ -1462,7 +1462,7 @@ namespace e2d
const auto asset_url = library.root() / address;
input_stream_uptr stream = modules::is_initialized<vfs>()
? the<vfs>().open(asset_url)
? the<vfs>().read(asset_url)
: input_stream_uptr();
if ( !stream ) {

View File

@@ -25,21 +25,27 @@ TEST_CASE("vfs"){
}
{
buffer b;
auto r = v.open({"file", file_path});
auto r = v.read({"file", file_path});
REQUIRE(r);
REQUIRE(streams::try_read_tail(b, r));
REQUIRE(b == buffer{"hello", 5});
REQUIRE(v.open({"file2", file_path}) == input_stream_uptr());
REQUIRE(v.open({"file", nofile_path}) == input_stream_uptr());
REQUIRE(v.read({"file2", file_path}) == input_stream_uptr());
REQUIRE(v.read({"file", nofile_path}) == input_stream_uptr());
}
{
auto r = v.load({"file", file_path});
REQUIRE(r.second);
REQUIRE(r.first == buffer{"hello", 5});
buffer b0;
REQUIRE(v.load({"file", file_path}, b0));
REQUIRE(b0 == buffer{"hello", 5});
auto r2 = v.load_async({"file", file_path}).get();
REQUIRE(r2.second);
REQUIRE(r2.first == buffer{"hello", 5});
auto b1 = v.load_async({"file", file_path}).get();
REQUIRE(b1 == buffer{"hello", 5});
str b2;
REQUIRE(v.load_as_string({"file", file_path}, b2));
REQUIRE(b2 == "hello");
auto b3 = v.load_as_string_async({"file", file_path}).get();
REQUIRE(b3 == "hello");
}
}
{
@@ -58,17 +64,17 @@ TEST_CASE("vfs"){
REQUIRE_FALSE(v.register_scheme<archive_file_source>(
"archive",
v.open(url("resources://bin/noresources.zip"))));
v.read(url("resources://bin/noresources.zip"))));
REQUIRE(v.register_scheme<filesystem_file_source>("file"));
REQUIRE_FALSE(v.register_scheme<archive_file_source>(
"archive",
v.open(url("resources://bin/noresources.zip"))));
v.read(url("resources://bin/noresources.zip"))));
REQUIRE(v.register_scheme<archive_file_source>(
"archive",
v.open(url("resources://bin/resources.zip"))));
v.read(url("resources://bin/resources.zip"))));
REQUIRE(v.exists({"archive", "test.txt"}));
REQUIRE_FALSE(v.exists({"archive", "TEst.txt"}));
@@ -122,50 +128,70 @@ TEST_CASE("vfs"){
REQUIRE_FALSE(v.extract(url("archive://test.txt"), std::back_inserter(result)));
}
{
auto f = v.open(url("archive://test.txt"));
auto f = v.read(url("archive://test.txt"));
REQUIRE(f);
buffer b;
REQUIRE(streams::try_read_tail(b, f));
REQUIRE(b == buffer("hello", 5));
}
{
auto r = v.load(url("archive://test.txt"));
REQUIRE(r.second);
REQUIRE(r.first == buffer("hello", 5));
buffer b0;
REQUIRE(v.load(url("archive://test.txt"), b0));
REQUIRE(b0 == buffer("hello", 5));
auto r2 = v.load_async(url("archive://test.txt")).get();
REQUIRE(r2.second);
REQUIRE(r2.first == buffer("hello", 5));
auto b1 = v.load_async(url("archive://test.txt")).get();
REQUIRE(b1 == buffer("hello", 5));
str b2;
REQUIRE(v.load_as_string(url("archive://test.txt"), b2));
REQUIRE(b2 == "hello");
auto b3 = v.load_as_string_async(url("archive://test.txt")).get();
REQUIRE(b3 == "hello");
}
{
auto f = v.open(url("archive://folder/file.txt"));
auto f = v.read(url("archive://folder/file.txt"));
REQUIRE(f);
buffer b;
REQUIRE(streams::try_read_tail(b, f));
REQUIRE(b == buffer("world", 5));
}
{
auto r = v.load(url("archive://folder/file.txt"));
REQUIRE(r.second);
REQUIRE(r.first == buffer("world", 5));
buffer b0;
REQUIRE(v.load(url("archive://folder/file.txt"), b0));
REQUIRE(b0 == buffer("world", 5));
auto r2 = v.load_async(url("archive://folder/file.txt")).get();
REQUIRE(r2.second);
REQUIRE(r2.first == buffer("world", 5));
auto b1 = v.load_async(url("archive://folder/file.txt")).get();
REQUIRE(b1 == buffer("world", 5));
str b2;
REQUIRE(v.load_as_string(url("archive://folder/file.txt"), b2));
REQUIRE(b2 == "world");
auto b3 = v.load_as_string_async(url("archive://folder/file.txt")).get();
REQUIRE(b3 == "world");
}
{
REQUIRE(v.open(url("archive://TEst.txt")) == input_stream_uptr());
REQUIRE(v.read(url("archive://TEst.txt")) == input_stream_uptr());
auto r = v.load(url("archive://TEst.txt"));
REQUIRE_FALSE(r.second);
REQUIRE(r.first.empty());
buffer b0;
REQUIRE_FALSE(v.load(url("archive://TEst.txt"), b0));
REQUIRE(b0.empty());
auto r2 = v.load_async(url("archive://TEst.txt")).get();
REQUIRE_FALSE(r2.second);
REQUIRE(r2.first.empty());
REQUIRE_THROWS_AS(
v.load_async(url("archive://TEst.txt")).get(),
vfs_load_async_exception);
str b2;
REQUIRE_FALSE(v.load_as_string(url("archive://TEst.txt"), b2));
REQUIRE(b2.empty());
REQUIRE_THROWS_AS(
v.load_as_string_async(url("archive://TEst.txt")).get(),
vfs_load_async_exception);
}
{
auto f = v.open(url("archive://test.txt"));
auto f = v.read(url("archive://test.txt"));
REQUIRE(f);
REQUIRE(v.unregister_scheme("archive"));
buffer b;