diff --git a/Utilities/File.cpp b/Utilities/File.cpp index be89628155..deaa13dbdc 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -9,6 +9,7 @@ #include #include "util/asm.hpp" +#include "util/coro.hpp" using namespace std::literals::string_literals; @@ -1182,7 +1183,7 @@ fs::file::file(const std::string& path, bs_t mode) for (const char* data = static_cast(buffer); count;) { - const DWORD size = static_cast(std::min(count, DWORD{umax} & -4096)); + const DWORD size = static_cast(std::min(count, DWORD{umax} & -4096)); DWORD nwritten = 0; ensure(WriteFile(m_handle, data, size, &nwritten, nullptr)); // "file::write" @@ -2039,6 +2040,30 @@ bool fs::pending_file::commit(bool overwrite) return false; } +stx::generator fs::list_dir_recursively(std::string path) +{ + for (auto& entry : fs::dir(path)) + { + if (entry.name == "." || entry.name == "..") + { + continue; + } + + std::string new_path = path_append(path, entry.name); + + if (entry.is_directory) + { + for (auto& nested : fs::list_dir_recursively(new_path)) + { + co_yield nested; + } + } + + entry.name = std::move(new_path); + co_yield entry; + } +} + template<> void fmt_class_string::format(std::string& out, u64 arg) { diff --git a/Utilities/File.h b/Utilities/File.h index f30a139e6c..8e019d8cc5 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -804,4 +804,6 @@ namespace fs } file make_gather(std::vector); + + stx::generator list_dir_recursively(std::string path); } diff --git a/rpcs3/util/coro.hpp b/rpcs3/util/coro.hpp new file mode 100644 index 0000000000..34072396ca --- /dev/null +++ b/rpcs3/util/coro.hpp @@ -0,0 +1,375 @@ +#pragma once + +#if __has_include() +#if defined(__clang__) && !defined(__cpp_impl_coroutine) +#define __cpp_impl_coroutine 123 +#endif + +#include + +#if defined(__clang__) && (__cpp_impl_coroutine == 123) +namespace std::experimental +{ + using ::std::coroutine_traits; + using ::std::coroutine_handle; +} +#endif +#elif __has_include() +#include + +namespace std +{ + using experimental::coroutine_traits; + using experimental::coroutine_handle; + using experimental::noop_coroutine_handle; + using experimental::suspend_always; + using experimental::suspend_never; +} +#endif + +#include + +namespace stx +{ + template + struct lazy; + + template + struct generator; + + namespace detail + { + struct lazy_promise_base + { + struct final_suspend_t + { + constexpr bool await_ready() const noexcept { return false; } + + template + std::coroutine_handle<> await_suspend(std::coroutine_handle

h) noexcept + { + return h.promise().m_handle; + } + + void await_resume() noexcept {} + }; + + constexpr lazy_promise_base() noexcept = default; + lazy_promise_base(const lazy_promise_base&) = delete; + lazy_promise_base& operator=(const lazy_promise_base&) = delete; + std::suspend_always initial_suspend() { return {}; } + final_suspend_t final_suspend() noexcept { return {}; } + void unhandled_exception() {} + + protected: + std::coroutine_handle<> m_handle{}; + }; + + template + struct lazy_promise final : lazy_promise_base + { + constexpr lazy_promise() noexcept = default; + + lazy get_return_object() noexcept; + + template + void return_value(U&& value) + { + m_value = std::forward(value); + } + + T& result() & + { + return m_value; + } + + T&& result() && + { + return m_value; + } + + private: + T m_value{}; + }; + + template + struct lazy_promise final : lazy_promise_base + { + constexpr lazy_promise() noexcept = default; + + lazy get_return_object() noexcept; + + void return_value(T& value) noexcept + { + m_value = std::addressof(value); + } + + T& result() + { + return *m_value; + } + + private: + T* m_value{}; + }; + + template <> + struct lazy_promise final : lazy_promise_base + { + constexpr lazy_promise() noexcept = default; + lazy get_return_object() noexcept; + void return_void() noexcept {} + void result() {} + }; + } + + template + struct [[nodiscard]] lazy + { + using promise_type = detail::lazy_promise; + using value_type = T; + + struct awaitable_base + { + std::coroutine_handle coro; + + bool await_ready() const noexcept + { + return !coro || coro.done(); + } + + std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) noexcept + { + coro.promise().m_handle = h; + return coro; + } + }; + + lazy(const lazy&) = delete; + + lazy(lazy&& rhs) noexcept + : m_handle(rhs.m_handle) + { + rhs.m_handle = nullptr; + } + + lazy& operator=(const lazy&) = delete; + + ~lazy() + { + if (m_handle) + { + m_handle.destroy(); + } + } + + bool is_ready() const noexcept + { + return !m_handle || m_handle.done(); + } + + auto operator co_await() const & noexcept + { + struct awaitable : awaitable_base + { + using awaitable_base::awaitable_base; + + decltype(auto) await_resume() + { + return this->m_handle.promise().result(); + } + }; + + return awaitable{m_handle}; + } + + auto operator co_await() const && noexcept + { + struct awaitable : awaitable_base + { + using awaitable_base::awaitable_base; + + decltype(auto) await_resume() + { + return std::move(this->m_handle.promise()).result(); + } + }; + + return awaitable{m_handle}; + } + + private: + friend promise_type; + + explicit constexpr lazy(std::coroutine_handle h) noexcept + : m_handle(h) + { + } + + std::coroutine_handle m_handle; + }; + + template + inline lazy detail::lazy_promise::get_return_object() noexcept + { + return lazy{std::coroutine_handle::from_promise(*this)}; + } + + template + inline lazy detail::lazy_promise::get_return_object() noexcept + { + return lazy{std::coroutine_handle::from_promise(*this)}; + } + + inline lazy detail::lazy_promise::get_return_object() noexcept + { + return lazy{std::coroutine_handle::from_promise(*this)}; + } + + namespace detail + { + template + struct generator_promise + { + using value_type = std::remove_reference_t; + using reference_type = std::conditional_t, T, T&>; + + generator_promise() = default; + + generator get_return_object() noexcept; + + constexpr std::suspend_always initial_suspend() const noexcept { return {}; } + constexpr std::suspend_always final_suspend() const noexcept { return {}; } + void unhandled_exception() {} + + std::suspend_always yield_value(std::remove_reference_t& value) noexcept + { + m_ptr = std::addressof(value); + return {}; + } + + std::suspend_always yield_value(std::remove_reference_t&& value) noexcept + { + m_ptr = std::addressof(value); + return {}; + } + + void return_void() {} + + reference_type value() const noexcept + { + return static_cast(*m_ptr); + } + + template + std::suspend_never await_transform(U&& value) = delete; + + private: + value_type* m_ptr; + }; + + struct generator_end + { + }; + + template + struct generator_iterator + { + using iterator_category = std::input_iterator_tag; + using value_type = typename generator_promise::value_type; + using reference = typename generator_promise::reference_type; + + constexpr generator_iterator() noexcept = default; + + explicit constexpr generator_iterator(std::coroutine_handle> coro) noexcept + : m_coro(coro) + { + } + + bool operator==(generator_end) const noexcept + { + return !m_coro || m_coro.done(); + } + + generator_iterator& operator++() + { + m_coro.resume(); + return *this; + } + + void operator++(int) + { + m_coro.resume(); + } + + reference operator*() const noexcept + { + return m_coro.promise().value(); + } + + value_type* operator->() const noexcept + { + return std::addressof(operator*()); + } + + private: + std::coroutine_handle> m_coro; + }; + } + + template + struct [[nodiscard]] generator + { + using promise_type = detail::generator_promise; + using iterator = detail::generator_iterator; + + generator(const generator&) = delete; + + generator(generator&& rhs) noexcept + : m_handle(rhs.m_handle) + { + rhs.m_handle = nullptr; + } + + generator& operator=(const generator&) = delete; + + ~generator() + { + if (m_handle) + { + m_handle.destroy(); + } + } + + iterator begin() + { + if (m_handle) + { + m_handle.resume(); + } + + return iterator{m_handle}; + } + + detail::generator_end end() noexcept + { + return detail::generator_end{}; + } + + private: + friend promise_type; + + explicit constexpr generator(std::coroutine_handle h) noexcept + : m_handle(h) + { + } + + std::coroutine_handle m_handle; + }; + + template + generator detail::generator_promise::get_return_object() noexcept + { + return generator{std::coroutine_handle>::from_promise(*this)}; + } +} diff --git a/rpcs3/util/types.hpp b/rpcs3/util/types.hpp index d045c3dfba..e4f7558ceb 100644 --- a/rpcs3/util/types.hpp +++ b/rpcs3/util/types.hpp @@ -131,6 +131,12 @@ namespace stx { template class se_t; + + template + struct lazy; + + template + struct generator; } using stx::se_t;