mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	core: backport some ResultCode updates (#6645)
Co-authored-by: Lioncash <mathew1800@gmail.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									0b37c1da57
								
							
						
					
					
						commit
						2126c240cd
					
				
					 34 changed files with 1204 additions and 277 deletions
				
			
		|  | @ -73,6 +73,7 @@ add_library(citra_common STATIC | |||
|     dynamic_library/ffmpeg.h | ||||
|     error.cpp | ||||
|     error.h | ||||
|     expected.h | ||||
|     file_util.cpp | ||||
|     file_util.h | ||||
|     hash.h | ||||
|  |  | |||
							
								
								
									
										987
									
								
								src/common/expected.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										987
									
								
								src/common/expected.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,987 @@ | |||
| // Copyright 2021 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| // This is based on the proposed implementation of std::expected (P0323)
 | ||||
| // https://github.com/TartanLlama/expected/blob/master/include/tl/expected.hpp
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| class Expected; | ||||
| 
 | ||||
| template <typename E> | ||||
| class Unexpected { | ||||
| public: | ||||
|     Unexpected() = delete; | ||||
| 
 | ||||
|     constexpr explicit Unexpected(const E& e) : m_val{e} {} | ||||
| 
 | ||||
|     constexpr explicit Unexpected(E&& e) : m_val{std::move(e)} {} | ||||
| 
 | ||||
|     constexpr E& value() & { | ||||
|         return m_val; | ||||
|     } | ||||
| 
 | ||||
|     constexpr const E& value() const& { | ||||
|         return m_val; | ||||
|     } | ||||
| 
 | ||||
|     constexpr E&& value() && { | ||||
|         return std::move(m_val); | ||||
|     } | ||||
| 
 | ||||
|     constexpr const E&& value() const&& { | ||||
|         return std::move(m_val); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     E m_val; | ||||
| }; | ||||
| 
 | ||||
| template <typename E> | ||||
| constexpr auto operator<=>(const Unexpected<E>& lhs, const Unexpected<E>& rhs) { | ||||
|     return lhs.value() <=> rhs.value(); | ||||
| } | ||||
| 
 | ||||
| struct unexpect_t { | ||||
|     constexpr explicit unexpect_t() = default; | ||||
| }; | ||||
| 
 | ||||
| namespace detail { | ||||
| 
 | ||||
| struct no_init_t { | ||||
|     constexpr explicit no_init_t() = default; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This specialization is for when T is not trivially destructible, | ||||
|  * so the destructor must be called on destruction of `expected' | ||||
|  * Additionally, this requires E to be trivially destructible | ||||
|  */ | ||||
| template <typename T, typename E, bool = std::is_trivially_destructible_v<T>> | ||||
| requires std::is_trivially_destructible_v<E> | ||||
| struct expected_storage_base { | ||||
|     constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} | ||||
| 
 | ||||
|     constexpr expected_storage_base(no_init_t) : m_has_val{false} {} | ||||
| 
 | ||||
|     template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr> | ||||
|     constexpr expected_storage_base(std::in_place_t, Args&&... args) | ||||
|         : m_val{std::forward<Args>(args)...}, m_has_val{true} {} | ||||
| 
 | ||||
|     template <typename U, typename... Args, | ||||
|               std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = | ||||
|                   nullptr> | ||||
|     constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args) | ||||
|         : m_val{il, std::forward<Args>(args)...}, m_has_val{true} {} | ||||
| 
 | ||||
|     template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr> | ||||
|     constexpr explicit expected_storage_base(unexpect_t, Args&&... args) | ||||
|         : m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {} | ||||
| 
 | ||||
|     template <typename U, typename... Args, | ||||
|               std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* = | ||||
|                   nullptr> | ||||
|     constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il, | ||||
|                                              Args&&... args) | ||||
|         : m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {} | ||||
| 
 | ||||
|     ~expected_storage_base() { | ||||
|         if (m_has_val) { | ||||
|             m_val.~T(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     union { | ||||
|         T m_val; | ||||
|         Unexpected<E> m_unexpect; | ||||
|     }; | ||||
| 
 | ||||
|     bool m_has_val; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This specialization is for when T is trivially destructible, | ||||
|  * so the destructor of `expected` can be trivial | ||||
|  * Additionally, this requires E to be trivially destructible | ||||
|  */ | ||||
| template <typename T, typename E> | ||||
| requires std::is_trivially_destructible_v<E> | ||||
| struct expected_storage_base<T, E, true> { | ||||
|     constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} | ||||
| 
 | ||||
|     constexpr expected_storage_base(no_init_t) : m_has_val{false} {} | ||||
| 
 | ||||
|     template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr> | ||||
|     constexpr expected_storage_base(std::in_place_t, Args&&... args) | ||||
|         : m_val{std::forward<Args>(args)...}, m_has_val{true} {} | ||||
| 
 | ||||
|     template <typename U, typename... Args, | ||||
|               std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = | ||||
|                   nullptr> | ||||
|     constexpr expected_storage_base(std::in_place_t, std::initializer_list<U> il, Args&&... args) | ||||
|         : m_val{il, std::forward<Args>(args)...}, m_has_val{true} {} | ||||
| 
 | ||||
|     template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr> | ||||
|     constexpr explicit expected_storage_base(unexpect_t, Args&&... args) | ||||
|         : m_unexpect{std::forward<Args>(args)...}, m_has_val{false} {} | ||||
| 
 | ||||
|     template <typename U, typename... Args, | ||||
|               std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* = | ||||
|                   nullptr> | ||||
|     constexpr explicit expected_storage_base(unexpect_t, std::initializer_list<U> il, | ||||
|                                              Args&&... args) | ||||
|         : m_unexpect{il, std::forward<Args>(args)...}, m_has_val{false} {} | ||||
| 
 | ||||
|     ~expected_storage_base() = default; | ||||
| 
 | ||||
|     union { | ||||
|         T m_val; | ||||
|         Unexpected<E> m_unexpect; | ||||
|     }; | ||||
| 
 | ||||
|     bool m_has_val; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| struct expected_operations_base : expected_storage_base<T, E> { | ||||
|     using expected_storage_base<T, E>::expected_storage_base; | ||||
| 
 | ||||
|     template <typename... Args> | ||||
|     void construct(Args&&... args) noexcept { | ||||
|         new (std::addressof(this->m_val)) T{std::forward<Args>(args)...}; | ||||
|         this->m_has_val = true; | ||||
|     } | ||||
| 
 | ||||
|     template <typename Rhs> | ||||
|     void construct_with(Rhs&& rhs) noexcept { | ||||
|         new (std::addressof(this->m_val)) T{std::forward<Rhs>(rhs).get()}; | ||||
|         this->m_has_val = true; | ||||
|     } | ||||
| 
 | ||||
|     template <typename... Args> | ||||
|     void construct_error(Args&&... args) noexcept { | ||||
|         new (std::addressof(this->m_unexpect)) Unexpected<E>{std::forward<Args>(args)...}; | ||||
|         this->m_has_val = false; | ||||
|     } | ||||
| 
 | ||||
|     void assign(const expected_operations_base& rhs) noexcept { | ||||
|         if (!this->m_has_val && rhs.m_has_val) { | ||||
|             geterr().~Unexpected<E>(); | ||||
|             construct(rhs.get()); | ||||
|         } else { | ||||
|             assign_common(rhs); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void assign(expected_operations_base&& rhs) noexcept { | ||||
|         if (!this->m_has_val && rhs.m_has_val) { | ||||
|             geterr().~Unexpected<E>(); | ||||
|             construct(std::move(rhs).get()); | ||||
|         } else { | ||||
|             assign_common(rhs); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename Rhs> | ||||
|     void assign_common(Rhs&& rhs) { | ||||
|         if (this->m_has_val) { | ||||
|             if (rhs.m_has_val) { | ||||
|                 get() = std::forward<Rhs>(rhs).get(); | ||||
|             } else { | ||||
|                 destroy_val(); | ||||
|                 construct_error(std::forward<Rhs>(rhs).geterr()); | ||||
|             } | ||||
|         } else { | ||||
|             if (!rhs.m_has_val) { | ||||
|                 geterr() = std::forward<Rhs>(rhs).geterr(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool has_value() const { | ||||
|         return this->m_has_val; | ||||
|     } | ||||
| 
 | ||||
|     constexpr T& get() & { | ||||
|         return this->m_val; | ||||
|     } | ||||
| 
 | ||||
|     constexpr const T& get() const& { | ||||
|         return this->m_val; | ||||
|     } | ||||
| 
 | ||||
|     constexpr T&& get() && { | ||||
|         return std::move(this->m_val); | ||||
|     } | ||||
| 
 | ||||
|     constexpr const T&& get() const&& { | ||||
|         return std::move(this->m_val); | ||||
|     } | ||||
| 
 | ||||
|     constexpr Unexpected<E>& geterr() & { | ||||
|         return this->m_unexpect; | ||||
|     } | ||||
| 
 | ||||
|     constexpr const Unexpected<E>& geterr() const& { | ||||
|         return this->m_unexpect; | ||||
|     } | ||||
| 
 | ||||
|     constexpr Unexpected<E>&& geterr() && { | ||||
|         return std::move(this->m_unexpect); | ||||
|     } | ||||
| 
 | ||||
|     constexpr const Unexpected<E>&& geterr() const&& { | ||||
|         return std::move(this->m_unexpect); | ||||
|     } | ||||
| 
 | ||||
|     constexpr void destroy_val() { | ||||
|         get().~T(); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This manages conditionally having a trivial copy constructor | ||||
|  * This specialization is for when T is trivially copy constructible | ||||
|  * Additionally, this requires E to be trivially copy constructible | ||||
|  */ | ||||
| template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>> | ||||
| requires std::is_trivially_copy_constructible_v<E> | ||||
| struct expected_copy_base : expected_operations_base<T, E> { | ||||
|     using expected_operations_base<T, E>::expected_operations_base; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This specialization is for when T is not trivially copy constructible | ||||
|  * Additionally, this requires E to be trivially copy constructible | ||||
|  */ | ||||
| template <typename T, typename E> | ||||
| requires std::is_trivially_copy_constructible_v<E> | ||||
| struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { | ||||
|     using expected_operations_base<T, E>::expected_operations_base; | ||||
| 
 | ||||
|     expected_copy_base() = default; | ||||
| 
 | ||||
|     expected_copy_base(const expected_copy_base& rhs) | ||||
|         : expected_operations_base<T, E>{no_init_t{}} { | ||||
|         if (rhs.has_value()) { | ||||
|             this->construct_with(rhs); | ||||
|         } else { | ||||
|             this->construct_error(rhs.geterr()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     expected_copy_base(expected_copy_base&&) = default; | ||||
| 
 | ||||
|     expected_copy_base& operator=(const expected_copy_base&) = default; | ||||
| 
 | ||||
|     expected_copy_base& operator=(expected_copy_base&&) = default; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This manages conditionally having a trivial move constructor | ||||
|  * This specialization is for when T is trivially move constructible | ||||
|  * Additionally, this requires E to be trivially move constructible | ||||
|  */ | ||||
| template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>> | ||||
| requires std::is_trivially_move_constructible_v<E> | ||||
| struct expected_move_base : expected_copy_base<T, E> { | ||||
|     using expected_copy_base<T, E>::expected_copy_base; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This specialization is for when T is not trivially move constructible | ||||
|  * Additionally, this requires E to be trivially move constructible | ||||
|  */ | ||||
| template <typename T, typename E> | ||||
| requires std::is_trivially_move_constructible_v<E> | ||||
| struct expected_move_base<T, E, false> : expected_copy_base<T, E> { | ||||
|     using expected_copy_base<T, E>::expected_copy_base; | ||||
| 
 | ||||
|     expected_move_base() = default; | ||||
| 
 | ||||
|     expected_move_base(const expected_move_base&) = default; | ||||
| 
 | ||||
|     expected_move_base(expected_move_base&& rhs) noexcept(std::is_nothrow_move_constructible_v<T>) | ||||
|         : expected_copy_base<T, E>{no_init_t{}} { | ||||
|         if (rhs.has_value()) { | ||||
|             this->construct_with(std::move(rhs)); | ||||
|         } else { | ||||
|             this->construct_error(std::move(rhs.geterr())); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     expected_move_base& operator=(const expected_move_base&) = default; | ||||
| 
 | ||||
|     expected_move_base& operator=(expected_move_base&&) = default; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This manages conditionally having a trivial copy assignment operator | ||||
|  * This specialization is for when T is trivially copy assignable | ||||
|  * Additionally, this requires E to be trivially copy assignable | ||||
|  */ | ||||
| template <typename T, typename E, | ||||
|           bool = std::conjunction_v<std::is_trivially_copy_assignable<T>, | ||||
|                                     std::is_trivially_copy_constructible<T>, | ||||
|                                     std::is_trivially_destructible<T>>> | ||||
| requires std::conjunction_v<std::is_trivially_copy_assignable<E>, | ||||
|                             std::is_trivially_copy_constructible<E>, | ||||
|                             std::is_trivially_destructible<E>> | ||||
| struct expected_copy_assign_base : expected_move_base<T, E> { | ||||
|     using expected_move_base<T, E>::expected_move_base; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This specialization is for when T is not trivially copy assignable | ||||
|  * Additionally, this requires E to be trivially copy assignable | ||||
|  */ | ||||
| template <typename T, typename E> | ||||
| requires std::conjunction_v<std::is_trivially_copy_assignable<E>, | ||||
|                             std::is_trivially_copy_constructible<E>, | ||||
|                             std::is_trivially_destructible<E>> | ||||
| struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> { | ||||
|     using expected_move_base<T, E>::expected_move_base; | ||||
| 
 | ||||
|     expected_copy_assign_base() = default; | ||||
| 
 | ||||
|     expected_copy_assign_base(const expected_copy_assign_base&) = default; | ||||
| 
 | ||||
|     expected_copy_assign_base(expected_copy_assign_base&&) = default; | ||||
| 
 | ||||
|     expected_copy_assign_base& operator=(const expected_copy_assign_base& rhs) { | ||||
|         this->assign(rhs); | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     expected_copy_assign_base& operator=(expected_copy_assign_base&&) = default; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This manages conditionally having a trivial move assignment operator | ||||
|  * This specialization is for when T is trivially move assignable | ||||
|  * Additionally, this requires E to be trivially move assignable | ||||
|  */ | ||||
| template <typename T, typename E, | ||||
|           bool = std::conjunction_v<std::is_trivially_move_assignable<T>, | ||||
|                                     std::is_trivially_move_constructible<T>, | ||||
|                                     std::is_trivially_destructible<T>>> | ||||
| requires std::conjunction_v<std::is_trivially_move_assignable<E>, | ||||
|                             std::is_trivially_move_constructible<E>, | ||||
|                             std::is_trivially_destructible<E>> | ||||
| struct expected_move_assign_base : expected_copy_assign_base<T, E> { | ||||
|     using expected_copy_assign_base<T, E>::expected_copy_assign_base; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This specialization is for when T is not trivially move assignable | ||||
|  * Additionally, this requires E to be trivially move assignable | ||||
|  */ | ||||
| template <typename T, typename E> | ||||
| requires std::conjunction_v<std::is_trivially_move_assignable<E>, | ||||
|                             std::is_trivially_move_constructible<E>, | ||||
|                             std::is_trivially_destructible<E>> | ||||
| struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> { | ||||
|     using expected_copy_assign_base<T, E>::expected_copy_assign_base; | ||||
| 
 | ||||
|     expected_move_assign_base() = default; | ||||
| 
 | ||||
|     expected_move_assign_base(const expected_move_assign_base&) = default; | ||||
| 
 | ||||
|     expected_move_assign_base(expected_move_assign_base&&) = default; | ||||
| 
 | ||||
|     expected_move_assign_base& operator=(const expected_move_assign_base&) = default; | ||||
| 
 | ||||
|     expected_move_assign_base& operator=(expected_move_assign_base&& rhs) noexcept( | ||||
|         std::conjunction_v<std::is_nothrow_move_constructible<T>, | ||||
|                            std::is_nothrow_move_assignable<T>>) { | ||||
|         this->assign(std::move(rhs)); | ||||
|         return *this; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * expected_delete_ctor_base will conditionally delete copy and move constructors | ||||
|  * depending on whether T is copy/move constructible | ||||
|  * Additionally, this requires E to be copy/move constructible | ||||
|  */ | ||||
| template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>, | ||||
|           bool EnableMove = std::is_move_constructible_v<T>> | ||||
| requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> | ||||
| struct expected_delete_ctor_base { | ||||
|     expected_delete_ctor_base() = default; | ||||
|     expected_delete_ctor_base(const expected_delete_ctor_base&) = default; | ||||
|     expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default; | ||||
|     expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; | ||||
|     expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> | ||||
| struct expected_delete_ctor_base<T, E, true, false> { | ||||
|     expected_delete_ctor_base() = default; | ||||
|     expected_delete_ctor_base(const expected_delete_ctor_base&) = default; | ||||
|     expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete; | ||||
|     expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; | ||||
|     expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> | ||||
| struct expected_delete_ctor_base<T, E, false, true> { | ||||
|     expected_delete_ctor_base() = default; | ||||
|     expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; | ||||
|     expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = default; | ||||
|     expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; | ||||
|     expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> | ||||
| struct expected_delete_ctor_base<T, E, false, false> { | ||||
|     expected_delete_ctor_base() = default; | ||||
|     expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; | ||||
|     expected_delete_ctor_base(expected_delete_ctor_base&&) noexcept = delete; | ||||
|     expected_delete_ctor_base& operator=(const expected_delete_ctor_base&) = default; | ||||
|     expected_delete_ctor_base& operator=(expected_delete_ctor_base&&) noexcept = default; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * expected_delete_assign_base will conditionally delete copy and move assignment operators | ||||
|  * depending on whether T is copy/move constructible + assignable | ||||
|  * Additionally, this requires E to be copy/move constructible + assignable | ||||
|  */ | ||||
| template < | ||||
|     typename T, typename E, | ||||
|     bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>, | ||||
|     bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>> | ||||
| requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, | ||||
|                             std::is_copy_assignable<E>, std::is_move_assignable<E>> | ||||
| struct expected_delete_assign_base { | ||||
|     expected_delete_assign_base() = default; | ||||
|     expected_delete_assign_base(const expected_delete_assign_base&) = default; | ||||
|     expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; | ||||
|     expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default; | ||||
|     expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, | ||||
|                             std::is_copy_assignable<E>, std::is_move_assignable<E>> | ||||
| struct expected_delete_assign_base<T, E, true, false> { | ||||
|     expected_delete_assign_base() = default; | ||||
|     expected_delete_assign_base(const expected_delete_assign_base&) = default; | ||||
|     expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; | ||||
|     expected_delete_assign_base& operator=(const expected_delete_assign_base&) = default; | ||||
|     expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, | ||||
|                             std::is_copy_assignable<E>, std::is_move_assignable<E>> | ||||
| struct expected_delete_assign_base<T, E, false, true> { | ||||
|     expected_delete_assign_base() = default; | ||||
|     expected_delete_assign_base(const expected_delete_assign_base&) = default; | ||||
|     expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; | ||||
|     expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete; | ||||
|     expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = default; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, | ||||
|                             std::is_copy_assignable<E>, std::is_move_assignable<E>> | ||||
| struct expected_delete_assign_base<T, E, false, false> { | ||||
|     expected_delete_assign_base() = default; | ||||
|     expected_delete_assign_base(const expected_delete_assign_base&) = default; | ||||
|     expected_delete_assign_base(expected_delete_assign_base&&) noexcept = default; | ||||
|     expected_delete_assign_base& operator=(const expected_delete_assign_base&) = delete; | ||||
|     expected_delete_assign_base& operator=(expected_delete_assign_base&&) noexcept = delete; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This is needed to be able to construct the expected_default_ctor_base which follows, | ||||
|  * while still conditionally deleting the default constructor. | ||||
|  */ | ||||
| struct default_constructor_tag { | ||||
|     constexpr explicit default_constructor_tag() = default; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * expected_default_ctor_base will ensure that expected | ||||
|  * has a deleted default constructor if T is not default constructible | ||||
|  * This specialization is for when T is default constructible | ||||
|  */ | ||||
| template <typename T, typename E, bool Enable = std::is_default_constructible_v<T>> | ||||
| struct expected_default_ctor_base { | ||||
|     constexpr expected_default_ctor_base() noexcept = default; | ||||
|     constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default; | ||||
|     constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default; | ||||
|     expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default; | ||||
|     expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default; | ||||
| 
 | ||||
|     constexpr explicit expected_default_ctor_base(default_constructor_tag) {} | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| struct expected_default_ctor_base<T, E, false> { | ||||
|     constexpr expected_default_ctor_base() noexcept = delete; | ||||
|     constexpr expected_default_ctor_base(expected_default_ctor_base const&) noexcept = default; | ||||
|     constexpr expected_default_ctor_base(expected_default_ctor_base&&) noexcept = default; | ||||
|     expected_default_ctor_base& operator=(expected_default_ctor_base const&) noexcept = default; | ||||
|     expected_default_ctor_base& operator=(expected_default_ctor_base&&) noexcept = default; | ||||
| 
 | ||||
|     constexpr explicit expected_default_ctor_base(default_constructor_tag) {} | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E, typename U> | ||||
| using expected_enable_forward_value = | ||||
|     std::enable_if_t<std::is_constructible_v<T, U&&> && | ||||
|                      !std::is_same_v<std::remove_cvref_t<U>, std::in_place_t> && | ||||
|                      !std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> && | ||||
|                      !std::is_same_v<Unexpected<E>, std::remove_cvref_t<U>>>; | ||||
| 
 | ||||
| template <typename T, typename E, typename U, typename G, typename UR, typename GR> | ||||
| using expected_enable_from_other = std::enable_if_t< | ||||
|     std::is_constructible_v<T, UR> && std::is_constructible_v<E, GR> && | ||||
|     !std::is_constructible_v<T, Expected<U, G>&> && !std::is_constructible_v<T, Expected<U, G>&&> && | ||||
|     !std::is_constructible_v<T, const Expected<U, G>&> && | ||||
|     !std::is_constructible_v<T, const Expected<U, G>&&> && | ||||
|     !std::is_convertible_v<Expected<U, G>&, T> && !std::is_convertible_v<Expected<U, G>&&, T> && | ||||
|     !std::is_convertible_v<const Expected<U, G>&, T> && | ||||
|     !std::is_convertible_v<const Expected<U, G>&&, T>>; | ||||
| 
 | ||||
| } // namespace detail
 | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| class Expected : private detail::expected_move_assign_base<T, E>, | ||||
|                  private detail::expected_delete_ctor_base<T, E>, | ||||
|                  private detail::expected_delete_assign_base<T, E>, | ||||
|                  private detail::expected_default_ctor_base<T, E> { | ||||
| public: | ||||
|     using value_type = T; | ||||
|     using error_type = E; | ||||
|     using unexpected_type = Unexpected<E>; | ||||
| 
 | ||||
|     constexpr Expected() = default; | ||||
|     constexpr Expected(const Expected&) = default; | ||||
|     constexpr Expected(Expected&&) = default; | ||||
|     Expected& operator=(const Expected&) = default; | ||||
|     Expected& operator=(Expected&&) = default; | ||||
| 
 | ||||
|     template <typename... Args, std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr> | ||||
|     constexpr Expected(std::in_place_t, Args&&... args) | ||||
|         : impl_base{std::in_place, std::forward<Args>(args)...}, | ||||
|           ctor_base{detail::default_constructor_tag{}} {} | ||||
| 
 | ||||
|     template <typename U, typename... Args, | ||||
|               std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = | ||||
|                   nullptr> | ||||
|     constexpr Expected(std::in_place_t, std::initializer_list<U> il, Args&&... args) | ||||
|         : impl_base{std::in_place, il, std::forward<Args>(args)...}, | ||||
|           ctor_base{detail::default_constructor_tag{}} {} | ||||
| 
 | ||||
|     template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr, | ||||
|               std::enable_if_t<!std::is_convertible_v<const G&, E>>* = nullptr> | ||||
|     constexpr explicit Expected(const Unexpected<G>& e) | ||||
|         : impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {} | ||||
| 
 | ||||
|     template <typename G = E, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr, | ||||
|               std::enable_if_t<std::is_convertible_v<const G&, E>>* = nullptr> | ||||
|     constexpr Expected(Unexpected<G> const& e) | ||||
|         : impl_base{unexpect_t{}, e.value()}, ctor_base{detail::default_constructor_tag{}} {} | ||||
| 
 | ||||
|     template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr, | ||||
|               std::enable_if_t<!std::is_convertible_v<G&&, E>>* = nullptr> | ||||
|     constexpr explicit Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>) | ||||
|         : impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{ | ||||
|                                                              detail::default_constructor_tag{}} {} | ||||
| 
 | ||||
|     template <typename G = E, std::enable_if_t<std::is_constructible_v<E, G&&>>* = nullptr, | ||||
|               std::enable_if_t<std::is_convertible_v<G&&, E>>* = nullptr> | ||||
|     constexpr Expected(Unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G&&>) | ||||
|         : impl_base{unexpect_t{}, std::move(e.value())}, ctor_base{ | ||||
|                                                              detail::default_constructor_tag{}} {} | ||||
| 
 | ||||
|     template <typename... Args, std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr> | ||||
|     constexpr explicit Expected(unexpect_t, Args&&... args) | ||||
|         : impl_base{unexpect_t{}, std::forward<Args>(args)...}, | ||||
|           ctor_base{detail::default_constructor_tag{}} {} | ||||
| 
 | ||||
|     template <typename U, typename... Args, | ||||
|               std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args&&...>>* = | ||||
|                   nullptr> | ||||
|     constexpr explicit Expected(unexpect_t, std::initializer_list<U> il, Args&&... args) | ||||
|         : impl_base{unexpect_t{}, il, std::forward<Args>(args)...}, | ||||
|           ctor_base{detail::default_constructor_tag{}} {} | ||||
| 
 | ||||
|     template <typename U, typename G, | ||||
|               std::enable_if_t<!(std::is_convertible_v<U const&, T> && | ||||
|                                  std::is_convertible_v<G const&, E>)>* = nullptr, | ||||
|               detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr> | ||||
|     constexpr explicit Expected(const Expected<U, G>& rhs) | ||||
|         : ctor_base{detail::default_constructor_tag{}} { | ||||
|         if (rhs.has_value()) { | ||||
|             this->construct(*rhs); | ||||
|         } else { | ||||
|             this->construct_error(rhs.error()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename U, typename G, | ||||
|               std::enable_if_t<(std::is_convertible_v<U const&, T> && | ||||
|                                 std::is_convertible_v<G const&, E>)>* = nullptr, | ||||
|               detail::expected_enable_from_other<T, E, U, G, const U&, const G&>* = nullptr> | ||||
|     constexpr Expected(const Expected<U, G>& rhs) : ctor_base{detail::default_constructor_tag{}} { | ||||
|         if (rhs.has_value()) { | ||||
|             this->construct(*rhs); | ||||
|         } else { | ||||
|             this->construct_error(rhs.error()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename U, typename G, | ||||
|               std::enable_if_t<!(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* = | ||||
|                   nullptr, | ||||
|               detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr> | ||||
|     constexpr explicit Expected(Expected<U, G>&& rhs) | ||||
|         : ctor_base{detail::default_constructor_tag{}} { | ||||
|         if (rhs.has_value()) { | ||||
|             this->construct(std::move(*rhs)); | ||||
|         } else { | ||||
|             this->construct_error(std::move(rhs.error())); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename U, typename G, | ||||
|               std::enable_if_t<(std::is_convertible_v<U&&, T> && std::is_convertible_v<G&&, E>)>* = | ||||
|                   nullptr, | ||||
|               detail::expected_enable_from_other<T, E, U, G, U&&, G&&>* = nullptr> | ||||
|     constexpr Expected(Expected<U, G>&& rhs) : ctor_base{detail::default_constructor_tag{}} { | ||||
|         if (rhs.has_value()) { | ||||
|             this->construct(std::move(*rhs)); | ||||
|         } else { | ||||
|             this->construct_error(std::move(rhs.error())); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T, std::enable_if_t<!std::is_convertible_v<U&&, T>>* = nullptr, | ||||
|               detail::expected_enable_forward_value<T, E, U>* = nullptr> | ||||
|     constexpr explicit Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {} | ||||
| 
 | ||||
|     template <typename U = T, std::enable_if_t<std::is_convertible_v<U&&, T>>* = nullptr, | ||||
|               detail::expected_enable_forward_value<T, E, U>* = nullptr> | ||||
|     constexpr Expected(U&& v) : Expected{std::in_place, std::forward<U>(v)} {} | ||||
| 
 | ||||
|     template <typename U = T, typename G = T, | ||||
|               std::enable_if_t<std::is_nothrow_constructible_v<T, U&&>>* = nullptr, | ||||
|               std::enable_if_t<( | ||||
|                   !std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> && | ||||
|                   !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> && | ||||
|                   std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> && | ||||
|                   std::is_nothrow_move_constructible_v<E>)>* = nullptr> | ||||
|     Expected& operator=(U&& v) { | ||||
|         if (has_value()) { | ||||
|             val() = std::forward<U>(v); | ||||
|         } else { | ||||
|             err().~Unexpected<E>(); | ||||
|             new (valptr()) T{std::forward<U>(v)}; | ||||
|             this->m_has_val = true; | ||||
|         } | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T, typename G = T, | ||||
|               std::enable_if_t<!std::is_nothrow_constructible_v<T, U&&>>* = nullptr, | ||||
|               std::enable_if_t<( | ||||
|                   !std::is_same_v<Expected<T, E>, std::remove_cvref_t<U>> && | ||||
|                   !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::remove_cvref_t<U>>> && | ||||
|                   std::is_constructible_v<T, U> && std::is_assignable_v<G&, U> && | ||||
|                   std::is_nothrow_move_constructible_v<E>)>* = nullptr> | ||||
|     Expected& operator=(U&& v) { | ||||
|         if (has_value()) { | ||||
|             val() = std::forward<U>(v); | ||||
|         } else { | ||||
|             auto tmp = std::move(err()); | ||||
|             err().~Unexpected<E>(); | ||||
|             new (valptr()) T{std::forward<U>(v)}; | ||||
|             this->m_has_val = true; | ||||
|         } | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     template <typename G = E, std::enable_if_t<std::is_nothrow_copy_constructible_v<G> && | ||||
|                                                std::is_assignable_v<G&, G>>* = nullptr> | ||||
|     Expected& operator=(const Unexpected<G>& rhs) { | ||||
|         if (!has_value()) { | ||||
|             err() = rhs; | ||||
|         } else { | ||||
|             this->destroy_val(); | ||||
|             new (errptr()) Unexpected<E>{rhs}; | ||||
|             this->m_has_val = false; | ||||
|         } | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     template <typename G = E, std::enable_if_t<std::is_nothrow_move_constructible_v<G> && | ||||
|                                                std::is_move_assignable_v<G>>* = nullptr> | ||||
|     Expected& operator=(Unexpected<G>&& rhs) noexcept { | ||||
|         if (!has_value()) { | ||||
|             err() = std::move(rhs); | ||||
|         } else { | ||||
|             this->destroy_val(); | ||||
|             new (errptr()) Unexpected<E>{std::move(rhs)}; | ||||
|             this->m_has_val = false; | ||||
|         } | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     template <typename... Args, | ||||
|               std::enable_if_t<std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr> | ||||
|     void emplace(Args&&... args) { | ||||
|         if (has_value()) { | ||||
|             val() = T{std::forward<Args>(args)...}; | ||||
|         } else { | ||||
|             err().~Unexpected<E>(); | ||||
|             new (valptr()) T{std::forward<Args>(args)...}; | ||||
|             this->m_has_val = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename... Args, | ||||
|               std::enable_if_t<!std::is_nothrow_constructible_v<T, Args&&...>>* = nullptr> | ||||
|     void emplace(Args&&... args) { | ||||
|         if (has_value()) { | ||||
|             val() = T{std::forward<Args>(args)...}; | ||||
|         } else { | ||||
|             auto tmp = std::move(err()); | ||||
|             err().~Unexpected<E>(); | ||||
|             new (valptr()) T{std::forward<Args>(args)...}; | ||||
|             this->m_has_val = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename U, typename... Args, | ||||
|               std::enable_if_t<std::is_nothrow_constructible_v<T, std::initializer_list<U>&, | ||||
|                                                                Args&&...>>* = nullptr> | ||||
|     void emplace(std::initializer_list<U> il, Args&&... args) { | ||||
|         if (has_value()) { | ||||
|             T t{il, std::forward<Args>(args)...}; | ||||
|             val() = std::move(t); | ||||
|         } else { | ||||
|             err().~Unexpected<E>(); | ||||
|             new (valptr()) T{il, std::forward<Args>(args)...}; | ||||
|             this->m_has_val = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename U, typename... Args, | ||||
|               std::enable_if_t<!std::is_nothrow_constructible_v<T, std::initializer_list<U>&, | ||||
|                                                                 Args&&...>>* = nullptr> | ||||
|     void emplace(std::initializer_list<U> il, Args&&... args) { | ||||
|         if (has_value()) { | ||||
|             T t{il, std::forward<Args>(args)...}; | ||||
|             val() = std::move(t); | ||||
|         } else { | ||||
|             auto tmp = std::move(err()); | ||||
|             err().~Unexpected<E>(); | ||||
|             new (valptr()) T{il, std::forward<Args>(args)...}; | ||||
|             this->m_has_val = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     constexpr T* operator->() { | ||||
|         return valptr(); | ||||
|     } | ||||
| 
 | ||||
|     constexpr const T* operator->() const { | ||||
|         return valptr(); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr U& operator*() & { | ||||
|         return val(); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr const U& operator*() const& { | ||||
|         return val(); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr U&& operator*() && { | ||||
|         return std::move(val()); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr const U&& operator*() const&& { | ||||
|         return std::move(val()); | ||||
|     } | ||||
| 
 | ||||
|     constexpr bool has_value() const noexcept { | ||||
|         return this->m_has_val; | ||||
|     } | ||||
| 
 | ||||
|     constexpr explicit operator bool() const noexcept { | ||||
|         return this->m_has_val; | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr U& value() & { | ||||
|         return val(); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr const U& value() const& { | ||||
|         return val(); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr U&& value() && { | ||||
|         return std::move(val()); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr const U&& value() const&& { | ||||
|         return std::move(val()); | ||||
|     } | ||||
| 
 | ||||
|     constexpr E& error() & { | ||||
|         return err().value(); | ||||
|     } | ||||
| 
 | ||||
|     constexpr const E& error() const& { | ||||
|         return err().value(); | ||||
|     } | ||||
| 
 | ||||
|     constexpr E&& error() && { | ||||
|         return std::move(err().value()); | ||||
|     } | ||||
| 
 | ||||
|     constexpr const E&& error() const&& { | ||||
|         return std::move(err().value()); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U> | ||||
|     constexpr T value_or(U&& v) const& { | ||||
|         static_assert(std::is_copy_constructible_v<T> && std::is_convertible_v<U&&, T>, | ||||
|                       "T must be copy-constructible and convertible from U&&"); | ||||
|         return bool(*this) ? **this : static_cast<T>(std::forward<U>(v)); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U> | ||||
|     constexpr T value_or(U&& v) && { | ||||
|         static_assert(std::is_move_constructible_v<T> && std::is_convertible_v<U&&, T>, | ||||
|                       "T must be move-constructible and convertible from U&&"); | ||||
|         return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v)); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     static_assert(!std::is_reference_v<T>, "T must not be a reference"); | ||||
|     static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>, | ||||
|                   "T must not be std::in_place_t"); | ||||
|     static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>, "T must not be unexpect_t"); | ||||
|     static_assert(!std::is_same_v<T, std::remove_cv_t<Unexpected<E>>>, | ||||
|                   "T must not be Unexpected<E>"); | ||||
|     static_assert(!std::is_reference_v<E>, "E must not be a reference"); | ||||
| 
 | ||||
|     T* valptr() { | ||||
|         return std::addressof(this->m_val); | ||||
|     } | ||||
| 
 | ||||
|     const T* valptr() const { | ||||
|         return std::addressof(this->m_val); | ||||
|     } | ||||
| 
 | ||||
|     Unexpected<E>* errptr() { | ||||
|         return std::addressof(this->m_unexpect); | ||||
|     } | ||||
| 
 | ||||
|     const Unexpected<E>* errptr() const { | ||||
|         return std::addressof(this->m_unexpect); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr U& val() { | ||||
|         return this->m_val; | ||||
|     } | ||||
| 
 | ||||
|     template <typename U = T> | ||||
|     constexpr const U& val() const { | ||||
|         return this->m_val; | ||||
|     } | ||||
| 
 | ||||
|     constexpr Unexpected<E>& err() { | ||||
|         return this->m_unexpect; | ||||
|     } | ||||
| 
 | ||||
|     constexpr const Unexpected<E>& err() const { | ||||
|         return this->m_unexpect; | ||||
|     } | ||||
| 
 | ||||
|     using impl_base = detail::expected_move_assign_base<T, E>; | ||||
|     using ctor_base = detail::expected_default_ctor_base<T, E>; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename E, typename U, typename F> | ||||
| constexpr bool operator==(const Expected<T, E>& lhs, const Expected<U, F>& rhs) { | ||||
|     return (lhs.has_value() != rhs.has_value()) | ||||
|                ? false | ||||
|                : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E, typename U, typename F> | ||||
| constexpr bool operator!=(const Expected<T, E>& lhs, const Expected<U, F>& rhs) { | ||||
|     return !operator==(lhs, rhs); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E, typename U> | ||||
| constexpr bool operator==(const Expected<T, E>& x, const U& v) { | ||||
|     return x.has_value() ? *x == v : false; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E, typename U> | ||||
| constexpr bool operator==(const U& v, const Expected<T, E>& x) { | ||||
|     return x.has_value() ? *x == v : false; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E, typename U> | ||||
| constexpr bool operator!=(const Expected<T, E>& x, const U& v) { | ||||
|     return !operator==(x, v); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E, typename U> | ||||
| constexpr bool operator!=(const U& v, const Expected<T, E>& x) { | ||||
|     return !operator==(v, x); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| constexpr bool operator==(const Expected<T, E>& x, const Unexpected<E>& e) { | ||||
|     return x.has_value() ? false : x.error() == e.value(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| constexpr bool operator==(const Unexpected<E>& e, const Expected<T, E>& x) { | ||||
|     return x.has_value() ? false : x.error() == e.value(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| constexpr bool operator!=(const Expected<T, E>& x, const Unexpected<E>& e) { | ||||
|     return !operator==(x, e); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename E> | ||||
| constexpr bool operator!=(const Unexpected<E>& e, const Expected<T, E>& x) { | ||||
|     return !operator==(e, x); | ||||
| } | ||||
| 
 | ||||
| } // namespace Common
 | ||||
|  | @ -42,7 +42,7 @@ public: | |||
|         if (offset > size) { | ||||
|             return ERR_WRITE_BEYOND_END; | ||||
|         } else if (offset == size) { | ||||
|             return MakeResult<std::size_t>(0); | ||||
|             return 0ULL; | ||||
|         } | ||||
| 
 | ||||
|         if (offset + length > size) { | ||||
|  | @ -150,11 +150,9 @@ public: | |||
|         Mode rwmode; | ||||
|         rwmode.write_flag.Assign(1); | ||||
|         rwmode.read_flag.Assign(1); | ||||
|         std::unique_ptr<DelayGenerator> delay_generator = | ||||
|             std::make_unique<ExtSaveDataDelayGenerator>(); | ||||
|         auto disk_file = | ||||
|             std::make_unique<FixSizeDiskFile>(std::move(file), rwmode, std::move(delay_generator)); | ||||
|         return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); | ||||
|         auto delay_generator = std::make_unique<ExtSaveDataDelayGenerator>(); | ||||
|         return std::make_unique<FixSizeDiskFile>(std::move(file), rwmode, | ||||
|                                                  std::move(delay_generator)); | ||||
|     } | ||||
| 
 | ||||
|     ResultCode CreateFile(const Path& path, u64 size) const override { | ||||
|  | @ -255,8 +253,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons | |||
|         } | ||||
|     } | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<ExtSaveDataDelayGenerator>(); | ||||
|     auto archive = std::make_unique<ExtSaveDataArchive>(fullpath, std::move(delay_generator)); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
|     return std::make_unique<ExtSaveDataArchive>(fullpath, std::move(delay_generator)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path, | ||||
|  | @ -276,7 +273,7 @@ ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path, | |||
| 
 | ||||
|     if (!file.IsOpen()) { | ||||
|         // TODO(Subv): Find the correct error code
 | ||||
|         return ResultCode(-1); | ||||
|         return RESULT_UNKNOWN; | ||||
|     } | ||||
| 
 | ||||
|     file.WriteBytes(&format_info, sizeof(format_info)); | ||||
|  | @ -296,7 +293,7 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Pat | |||
| 
 | ||||
|     ArchiveFormatInfo info = {}; | ||||
|     file.ReadBytes(&info, sizeof(info)); | ||||
|     return MakeResult<ArchiveFormatInfo>(info); | ||||
|     return info; | ||||
| } | ||||
| 
 | ||||
| void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data, | ||||
|  |  | |||
|  | @ -170,16 +170,14 @@ ResultVal<std::unique_ptr<FileBackend>> NCCHArchive::OpenFile(const Path& path, | |||
|         if (!archive_data.empty()) { | ||||
|             u64 romfs_offset = 0; | ||||
|             u64 romfs_size = archive_data.size(); | ||||
|             std::unique_ptr<DelayGenerator> delay_generator = | ||||
|                 std::make_unique<RomFSDelayGenerator>(); | ||||
|             file = std::make_unique<IVFCFileInMemory>(std::move(archive_data), romfs_offset, | ||||
|             auto delay_generator = std::make_unique<RomFSDelayGenerator>(); | ||||
|             return std::make_unique<IVFCFileInMemory>(std::move(archive_data), romfs_offset, | ||||
|                                                       romfs_size, std::move(delay_generator)); | ||||
|             return MakeResult<std::unique_ptr<FileBackend>>(std::move(file)); | ||||
|         } | ||||
|         return ERROR_NOT_FOUND; | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<std::unique_ptr<FileBackend>>(std::move(file)); | ||||
|     return file; | ||||
| } | ||||
| 
 | ||||
| ResultCode NCCHArchive::DeleteFile(const Path& path) const { | ||||
|  | @ -192,21 +190,21 @@ ResultCode NCCHArchive::DeleteFile(const Path& path) const { | |||
| ResultCode NCCHArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to rename a file within an NCCH archive ({}).", GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultCode NCCHArchive::DeleteDirectory(const Path& path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an NCCH archive ({}).", | ||||
|                  GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultCode NCCHArchive::DeleteDirectoryRecursively(const Path& path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an NCCH archive ({}).", | ||||
|                  GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultCode NCCHArchive::CreateFile(const Path& path, u64 size) const { | ||||
|  | @ -219,20 +217,20 @@ ResultCode NCCHArchive::CreateFile(const Path& path, u64 size) const { | |||
| ResultCode NCCHArchive::CreateDirectory(const Path& path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to create a directory in an NCCH archive ({}).", GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultCode NCCHArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to rename a file within an NCCH archive ({}).", GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::unique_ptr<DirectoryBackend>> NCCHArchive::OpenDirectory(const Path& path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to open a directory within an NCCH archive ({}).", | ||||
|                  GetName().c_str()); | ||||
|     // TODO(shinyquagsire23): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| u64 NCCHArchive::GetFreeBytes() const { | ||||
|  | @ -253,14 +251,14 @@ ResultVal<std::size_t> NCCHFile::Read(const u64 offset, const std::size_t length | |||
|     std::size_t copy_size = std::min(length, available_size); | ||||
|     memcpy(buffer, file_buffer.data() + offset, copy_size); | ||||
| 
 | ||||
|     return MakeResult<std::size_t>(copy_size); | ||||
|     return copy_size; | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::size_t> NCCHFile::Write(const u64 offset, const std::size_t length, const bool flush, | ||||
|                                        const u8* buffer) { | ||||
|     LOG_ERROR(Service_FS, "Attempted to write to NCCH file"); | ||||
|     // TODO(shinyquagsire23): Find error code
 | ||||
|     return MakeResult<std::size_t>(0); | ||||
|     return 0ULL; | ||||
| } | ||||
| 
 | ||||
| u64 NCCHFile::GetSize() const { | ||||
|  | @ -290,9 +288,8 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& | |||
|     NCCHArchivePath open_path; | ||||
|     std::memcpy(&open_path, binary.data(), sizeof(NCCHArchivePath)); | ||||
| 
 | ||||
|     auto archive = std::make_unique<NCCHArchive>( | ||||
|     return std::make_unique<NCCHArchive>( | ||||
|         open_path.tid, static_cast<Service::FS::MediaType>(open_path.media_type & 0xFF)); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveFactory_NCCH::Format(const Path& path, | ||||
|  | @ -308,7 +305,7 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_NCCH::GetFormatInfo(const Path& path | |||
|                                                                 u64 program_id) const { | ||||
|     // TODO(Subv): Implement
 | ||||
|     LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ ResultVal<std::tuple<MediaType, u64>> ParsePath(const Path& path, T program_id_r | |||
|         return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<std::tuple<MediaType, u64>>(media_type, program_id_reader(data)); | ||||
|     return std::make_tuple(media_type, program_id_reader(data)); | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::tuple<MediaType, u64>> ParsePathPermitted(const Path& path) { | ||||
|  |  | |||
|  | @ -108,8 +108,7 @@ ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& pa | |||
|     } | ||||
| 
 | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<SDMCDelayGenerator>(); | ||||
|     auto disk_file = std::make_unique<DiskFile>(std::move(file), mode, std::move(delay_generator)); | ||||
|     return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); | ||||
|     return std::make_unique<DiskFile>(std::move(file), mode, std::move(delay_generator)); | ||||
| } | ||||
| 
 | ||||
| ResultCode SDMCArchive::DeleteFile(const Path& path) const { | ||||
|  | @ -358,8 +357,7 @@ ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Pa | |||
|         break; // Expected 'success' case
 | ||||
|     } | ||||
| 
 | ||||
|     auto directory = std::make_unique<DiskDirectory>(full_path); | ||||
|     return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); | ||||
|     return std::make_unique<DiskDirectory>(full_path); | ||||
| } | ||||
| 
 | ||||
| u64 SDMCArchive::GetFreeBytes() const { | ||||
|  | @ -390,8 +388,7 @@ bool ArchiveFactory_SDMC::Initialize() { | |||
| ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path, | ||||
|                                                                      u64 program_id) { | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<SDMCDelayGenerator>(); | ||||
|     auto archive = std::make_unique<SDMCArchive>(sdmc_directory, std::move(delay_generator)); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
|     return std::make_unique<SDMCArchive>(sdmc_directory, std::move(delay_generator)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveFactory_SDMC::Format(const Path& path, | ||||
|  | @ -405,7 +402,7 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path | |||
|                                                                 u64 program_id) const { | ||||
|     // TODO(Subv): Implement
 | ||||
|     LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| } // namespace FileSys
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -78,9 +78,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(co | |||
|                                                                               u64 program_id) { | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = | ||||
|         std::make_unique<SDMCWriteOnlyDelayGenerator>(); | ||||
|     auto archive = | ||||
|         std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory, std::move(delay_generator)); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
|     return std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory, std::move(delay_generator)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveFactory_SDMCWriteOnly::Format(const Path& path, | ||||
|  | @ -88,14 +86,14 @@ ResultCode ArchiveFactory_SDMCWriteOnly::Format(const Path& path, | |||
|                                                 u64 program_id) { | ||||
|     // TODO(wwylele): hwtest this
 | ||||
|     LOG_ERROR(Service_FS, "Attempted to format a SDMC write-only archive."); | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const Path& path, | ||||
|                                                                          u64 program_id) const { | ||||
|     // TODO(Subv): Implement
 | ||||
|     LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  |  | |||
|  | @ -48,7 +48,7 @@ public: | |||
|         } | ||||
| 
 | ||||
|         std::memcpy(buffer, data->data(), data->size()); | ||||
|         return MakeResult<std::size_t>(data->size()); | ||||
|         return data->size(); | ||||
|     } | ||||
| 
 | ||||
|     ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush, | ||||
|  | @ -183,8 +183,7 @@ private: | |||
|         if (ncch_data.romfs_file) { | ||||
|             std::unique_ptr<DelayGenerator> delay_generator = | ||||
|                 std::make_unique<RomFSDelayGenerator>(); | ||||
|             return MakeResult<std::unique_ptr<FileBackend>>( | ||||
|                 std::make_unique<IVFCFile>(ncch_data.romfs_file, std::move(delay_generator))); | ||||
|             return std::make_unique<IVFCFile>(ncch_data.romfs_file, std::move(delay_generator)); | ||||
|         } else { | ||||
|             LOG_INFO(Service_FS, "Unable to read RomFS"); | ||||
|             return ERROR_ROMFS_NOT_FOUND; | ||||
|  | @ -195,8 +194,8 @@ private: | |||
|         if (ncch_data.update_romfs_file) { | ||||
|             std::unique_ptr<DelayGenerator> delay_generator = | ||||
|                 std::make_unique<RomFSDelayGenerator>(); | ||||
|             return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>( | ||||
|                 ncch_data.update_romfs_file, std::move(delay_generator))); | ||||
|             return std::make_unique<IVFCFile>(ncch_data.update_romfs_file, | ||||
|                                               std::move(delay_generator)); | ||||
|         } else { | ||||
|             LOG_INFO(Service_FS, "Unable to read update RomFS"); | ||||
|             return ERROR_ROMFS_NOT_FOUND; | ||||
|  | @ -206,8 +205,7 @@ private: | |||
|     ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const { | ||||
|         if (filename == "icon") { | ||||
|             if (ncch_data.icon) { | ||||
|                 return MakeResult<std::unique_ptr<FileBackend>>( | ||||
|                     std::make_unique<ExeFSSectionFile>(ncch_data.icon)); | ||||
|                 return std::make_unique<ExeFSSectionFile>(ncch_data.icon); | ||||
|             } | ||||
| 
 | ||||
|             LOG_WARNING(Service_FS, "Unable to read icon"); | ||||
|  | @ -216,8 +214,7 @@ private: | |||
| 
 | ||||
|         if (filename == "logo") { | ||||
|             if (ncch_data.logo) { | ||||
|                 return MakeResult<std::unique_ptr<FileBackend>>( | ||||
|                     std::make_unique<ExeFSSectionFile>(ncch_data.logo)); | ||||
|                 return std::make_unique<ExeFSSectionFile>(ncch_data.logo); | ||||
|             } | ||||
| 
 | ||||
|             LOG_WARNING(Service_FS, "Unable to read logo"); | ||||
|  | @ -226,8 +223,7 @@ private: | |||
| 
 | ||||
|         if (filename == "banner") { | ||||
|             if (ncch_data.banner) { | ||||
|                 return MakeResult<std::unique_ptr<FileBackend>>( | ||||
|                     std::make_unique<ExeFSSectionFile>(ncch_data.banner)); | ||||
|                 return std::make_unique<ExeFSSectionFile>(ncch_data.banner); | ||||
|             } | ||||
| 
 | ||||
|             LOG_WARNING(Service_FS, "Unable to read banner"); | ||||
|  | @ -297,8 +293,7 @@ void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) { | |||
| 
 | ||||
| ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path, | ||||
|                                                                          u64 program_id) { | ||||
|     auto archive = std::make_unique<SelfNCCHArchive>(ncch_data[program_id]); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
|     return std::make_unique<SelfNCCHArchive>(ncch_data[program_id]); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&, | ||||
|  |  | |||
|  | @ -50,8 +50,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 pr | |||
|         return ERR_NOT_FORMATTED; | ||||
|     } | ||||
| 
 | ||||
|     auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point)); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
|     return std::make_unique<SaveDataArchive>(std::move(concrete_mount_point)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveSource_SDSaveData::Format(u64 program_id, | ||||
|  | @ -83,7 +82,7 @@ ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program | |||
| 
 | ||||
|     ArchiveFormatInfo info = {}; | ||||
|     file.ReadBytes(&info, sizeof(info)); | ||||
|     return MakeResult<ArchiveFormatInfo>(info); | ||||
|     return info; | ||||
| } | ||||
| 
 | ||||
| std::string ArchiveSource_SDSaveData::GetSaveDataPathFor(const std::string& mount_point, | ||||
|  |  | |||
|  | @ -59,8 +59,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c | |||
|         // TODO(Subv): Check error code, this one is probably wrong
 | ||||
|         return ERROR_NOT_FOUND; | ||||
|     } | ||||
|     auto archive = std::make_unique<SaveDataArchive>(fullpath); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
|     return std::make_unique<SaveDataArchive>(fullpath); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path, | ||||
|  | @ -76,7 +75,7 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const | |||
|                                                                           u64 program_id) const { | ||||
|     // TODO(Subv): Implement
 | ||||
|     LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| } // namespace FileSys
 | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ ResultVal<std::size_t> DiskFile::Read(const u64 offset, const std::size_t length | |||
|         return ERROR_INVALID_OPEN_FLAGS; | ||||
| 
 | ||||
|     file->Seek(offset, SEEK_SET); | ||||
|     return MakeResult<std::size_t>(file->ReadBytes(buffer, length)); | ||||
|     return file->ReadBytes(buffer, length); | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::size_t> DiskFile::Write(const u64 offset, const std::size_t length, const bool flush, | ||||
|  | @ -34,7 +34,7 @@ ResultVal<std::size_t> DiskFile::Write(const u64 offset, const std::size_t lengt | |||
|     std::size_t written = file->WriteBytes(buffer, length); | ||||
|     if (flush) | ||||
|         file->Flush(); | ||||
|     return MakeResult<std::size_t>(written); | ||||
|     return written; | ||||
| } | ||||
| 
 | ||||
| u64 DiskFile::GetSize() const { | ||||
|  |  | |||
|  | @ -31,8 +31,7 @@ std::string IVFCArchive::GetName() const { | |||
| ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, | ||||
|                                                               const Mode& mode) const { | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<IVFCDelayGenerator>(); | ||||
|     return MakeResult<std::unique_ptr<FileBackend>>( | ||||
|         std::make_unique<IVFCFile>(romfs_file, std::move(delay_generator))); | ||||
|     return std::make_unique<IVFCFile>(romfs_file, std::move(delay_generator)); | ||||
| } | ||||
| 
 | ||||
| ResultCode IVFCArchive::DeleteFile(const Path& path) const { | ||||
|  | @ -45,21 +44,21 @@ ResultCode IVFCArchive::DeleteFile(const Path& path) const { | |||
| ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive ({}).", GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultCode IVFCArchive::DeleteDirectory(const Path& path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive ({}).", | ||||
|                  GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive ({}).", | ||||
|                  GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { | ||||
|  | @ -72,17 +71,17 @@ ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { | |||
| ResultCode IVFCArchive::CreateDirectory(const Path& path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive ({}).", GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||||
|     LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive ({}).", GetName()); | ||||
|     // TODO(wwylele): Use correct error code
 | ||||
|     return ResultCode(-1); | ||||
|     return RESULT_UNKNOWN; | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const { | ||||
|     return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>()); | ||||
|     return std::make_unique<IVFCDirectory>(); | ||||
| } | ||||
| 
 | ||||
| u64 IVFCArchive::GetFreeBytes() const { | ||||
|  | @ -99,14 +98,14 @@ IVFCFile::IVFCFile(std::shared_ptr<RomFSReader> file, | |||
| ResultVal<std::size_t> IVFCFile::Read(const u64 offset, const std::size_t length, | ||||
|                                       u8* buffer) const { | ||||
|     LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); | ||||
|     return MakeResult<std::size_t>(romfs_file->ReadFile(offset, length, buffer)); | ||||
|     return romfs_file->ReadFile(offset, length, buffer); | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::size_t> IVFCFile::Write(const u64 offset, const std::size_t length, const bool flush, | ||||
|                                        const u8* buffer) { | ||||
|     LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); | ||||
|     // TODO(Subv): Find error code
 | ||||
|     return MakeResult<std::size_t>(0); | ||||
|     return 0ULL; | ||||
| } | ||||
| 
 | ||||
| u64 IVFCFile::GetSize() const { | ||||
|  | @ -130,14 +129,14 @@ ResultVal<std::size_t> IVFCFileInMemory::Read(const u64 offset, const std::size_ | |||
|     std::size_t read_length = (std::size_t)std::min((u64)length, data_size - offset); | ||||
| 
 | ||||
|     std::memcpy(buffer, romfs_file.data() + data_offset + offset, read_length); | ||||
|     return MakeResult<std::size_t>(read_length); | ||||
|     return read_length; | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::size_t> IVFCFileInMemory::Write(const u64 offset, const std::size_t length, | ||||
|                                                const bool flush, const u8* buffer) { | ||||
|     LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); | ||||
|     // TODO(Subv): Find error code
 | ||||
|     return MakeResult<std::size_t>(0); | ||||
|     return 0ULL; | ||||
| } | ||||
| 
 | ||||
| u64 IVFCFileInMemory::GetSize() const { | ||||
|  |  | |||
|  | @ -90,8 +90,7 @@ ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& pa | |||
|     } | ||||
| 
 | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<SaveDataDelayGenerator>(); | ||||
|     auto disk_file = std::make_unique<DiskFile>(std::move(file), mode, std::move(delay_generator)); | ||||
|     return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); | ||||
|     return std::make_unique<DiskFile>(std::move(file), mode, std::move(delay_generator)); | ||||
| } | ||||
| 
 | ||||
| ResultCode SaveDataArchive::DeleteFile(const Path& path) const { | ||||
|  | @ -343,8 +342,7 @@ ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory( | |||
|         break; // Expected 'success' case
 | ||||
|     } | ||||
| 
 | ||||
|     auto directory = std::make_unique<DiskDirectory>(full_path); | ||||
|     return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); | ||||
|     return std::make_unique<DiskDirectory>(full_path); | ||||
| } | ||||
| 
 | ||||
| u64 SaveDataArchive::GetFreeBytes() const { | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() { | |||
|     // Wake the threads waiting on the ServerPort
 | ||||
|     server_port->WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     return MakeResult(client); | ||||
|     return client; | ||||
| } | ||||
| 
 | ||||
| void ClientPort::ConnectionClosed() { | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ ResultVal<Handle> HandleTable::Create(std::shared_ptr<Object> obj) { | |||
|     objects[slot] = std::move(obj); | ||||
| 
 | ||||
|     Handle handle = generation | (slot << 15); | ||||
|     return MakeResult<Handle>(handle); | ||||
|     return handle; | ||||
| } | ||||
| 
 | ||||
| ResultVal<Handle> HandleTable::Duplicate(Handle handle) { | ||||
|  |  | |||
|  | @ -271,7 +271,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission per | |||
|     memory_used += size; | ||||
|     resource_limit->current_commit += size; | ||||
| 
 | ||||
|     return MakeResult<VAddr>(target); | ||||
|     return target; | ||||
| } | ||||
| 
 | ||||
| ResultCode Process::HeapFree(VAddr target, u32 size) { | ||||
|  | @ -344,7 +344,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p | |||
|     resource_limit->current_commit += size; | ||||
| 
 | ||||
|     LOG_DEBUG(Kernel, "Allocated at target={:08X}", target); | ||||
|     return MakeResult<VAddr>(target); | ||||
|     return target; | ||||
| } | ||||
| 
 | ||||
| ResultCode Process::LinearFree(VAddr target, u32 size) { | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ ResultVal<std::shared_ptr<Semaphore>> KernelSystem::CreateSemaphore(s32 initial_ | |||
|     semaphore->available_count = initial_count; | ||||
|     semaphore->name = std::move(name); | ||||
| 
 | ||||
|     return MakeResult<std::shared_ptr<Semaphore>>(std::move(semaphore)); | ||||
|     return semaphore; | ||||
| } | ||||
| 
 | ||||
| bool Semaphore::ShouldWait(const Thread* thread) const { | ||||
|  | @ -53,7 +53,7 @@ ResultVal<s32> Semaphore::Release(s32 release_count) { | |||
| 
 | ||||
|     WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     return MakeResult<s32>(previous_count); | ||||
|     return previous_count; | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { | |||
| 
 | ||||
|     auto session = std::move(pending_sessions.back()); | ||||
|     pending_sessions.pop_back(); | ||||
|     return MakeResult(std::move(session)); | ||||
|     return session; | ||||
| } | ||||
| 
 | ||||
| bool ServerPort::ShouldWait(const Thread* thread) const { | ||||
|  |  | |||
|  | @ -52,7 +52,7 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelSystem& ke | |||
|     server_session->name = std::move(name); | ||||
|     server_session->parent = nullptr; | ||||
| 
 | ||||
|     return MakeResult(std::move(server_session)); | ||||
|     return server_session; | ||||
| } | ||||
| 
 | ||||
| bool ServerSession::ShouldWait(const Thread* thread) const { | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ ResultVal<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory( | |||
|     } | ||||
| 
 | ||||
|     shared_memory->base_address = address; | ||||
|     return MakeResult(shared_memory); | ||||
|     return shared_memory; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<SharedMemory> KernelSystem::CreateSharedMemoryForApplet( | ||||
|  |  | |||
|  | @ -674,7 +674,7 @@ ResultCode SVC::OpenProcess(Handle* out_handle, u32 process_id) { | |||
|         return ResultCode(24, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||||
|     } | ||||
|     auto result_handle = kernel.GetCurrentProcess()->handle_table.Create(process); | ||||
|     if (result_handle.empty()) { | ||||
|     if (!result_handle) { | ||||
|         return result_handle.Code(); | ||||
|     } | ||||
|     *out_handle = result_handle.Unwrap(); | ||||
|  | @ -699,7 +699,7 @@ ResultCode SVC::OpenThread(Handle* out_handle, Handle process_handle, u32 thread | |||
|         for (auto& thread : thread_list) { | ||||
|             if (thread->owner_process.lock() == process && thread.get()->thread_id == thread_id) { | ||||
|                 auto result_handle = kernel.GetCurrentProcess()->handle_table.Create(thread); | ||||
|                 if (result_handle.empty()) { | ||||
|                 if (!result_handle) { | ||||
|                     return result_handle.Code(); | ||||
|                 } | ||||
|                 *out_handle = result_handle.Unwrap(); | ||||
|  |  | |||
|  | @ -423,7 +423,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread( | |||
|     thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, thread.get()); | ||||
|     thread->status = ThreadStatus::Ready; | ||||
| 
 | ||||
|     return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); | ||||
|     return thread; | ||||
| } | ||||
| 
 | ||||
| void Thread::SetPriority(u32 priority) { | ||||
|  |  | |||
|  | @ -96,7 +96,7 @@ ResultVal<VAddr> VMManager::MapBackingMemoryToBase(VAddr base, u32 region_size, | |||
|     if (result.Failed()) | ||||
|         return result.Code(); | ||||
| 
 | ||||
|     return MakeResult<VAddr>(target); | ||||
|     return target; | ||||
| } | ||||
| 
 | ||||
| ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, MemoryRef memory, | ||||
|  | @ -115,7 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, Memory | |||
|     final_vma.backing_memory = memory; | ||||
|     UpdatePageTableForVMA(final_vma); | ||||
| 
 | ||||
|     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | ||||
|     return MergeAdjacent(vma_handle); | ||||
| } | ||||
| 
 | ||||
| ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, | ||||
|  | @ -135,7 +135,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3 | |||
|     final_vma.mmio_handler = mmio_handler; | ||||
|     UpdatePageTableForVMA(final_vma); | ||||
| 
 | ||||
|     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | ||||
|     return MergeAdjacent(vma_handle); | ||||
| } | ||||
| 
 | ||||
| ResultCode VMManager::ChangeMemoryState(VAddr target, u32 size, MemoryState expected_state, | ||||
|  | @ -294,7 +294,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | |||
|         vma_handle = SplitVMA(vma_handle, start_in_vma); | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<VMAIter>(vma_handle); | ||||
|     return vma_handle; | ||||
| } | ||||
| 
 | ||||
| ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) { | ||||
|  | @ -322,7 +322,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) { | |||
|         end_vma = SplitVMA(end_vma, target_end - end_vma->second.base); | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<VMAIter>(begin_vma); | ||||
|     return begin_vma; | ||||
| } | ||||
| 
 | ||||
| VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) { | ||||
|  | @ -409,6 +409,6 @@ ResultVal<std::vector<std::pair<MemoryRef, u32>>> VMManager::GetBackingBlocksFor | |||
| 
 | ||||
|         interval_target += interval_size; | ||||
|     } | ||||
|     return MakeResult(backing_blocks); | ||||
|     return backing_blocks; | ||||
| } | ||||
| } // namespace Kernel
 | ||||
|  |  | |||
|  | @ -4,13 +4,12 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <new> | ||||
| #include <utility> | ||||
| #include <boost/serialization/access.hpp> | ||||
| #include "common/assert.h" | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/expected.h" | ||||
| 
 | ||||
| // All the constants in this file come from http://3dbrew.org/wiki/Error_codes
 | ||||
| 
 | ||||
|  | @ -255,12 +254,19 @@ constexpr ResultCode UnimplementedFunction(ErrorModule module) { | |||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * This is an optional value type. It holds a `ResultCode` and, if that code is a success code, | ||||
|  * also holds a result of type `T`. If the code is an error code then trying to access the inner | ||||
|  * value fails, thus ensuring that the ResultCode of functions is always checked properly before | ||||
|  * their return value is used.  It is similar in concept to the `std::optional` type | ||||
|  * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in
 | ||||
|  * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html).
 | ||||
|  * Placeholder result code used for unknown error codes. | ||||
|  * | ||||
|  * @note This should only be used when a particular error code | ||||
|  *       is not known yet. | ||||
|  */ | ||||
| constexpr ResultCode RESULT_UNKNOWN(UINT32_MAX); | ||||
| 
 | ||||
| /**
 | ||||
|  * This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it | ||||
|  * also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying | ||||
|  * to access the inner value with operator* is undefined behavior and will assert with Unwrap(). | ||||
|  * Users of this class must be cognizant to check the status of the ResultVal with operator bool(), | ||||
|  * Code(), Succeeded() or Failed() prior to accessing the inner value. | ||||
|  * | ||||
|  * An example of how it could be used: | ||||
|  * \code | ||||
|  | @ -271,166 +277,117 @@ constexpr ResultCode UnimplementedFunction(ErrorModule module) { | |||
|  *             ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||||
|  *     } else { | ||||
|  *         // Frobnicated! Give caller a cookie
 | ||||
|  *         return MakeResult<int>(42); | ||||
|  *         return 42; | ||||
|  *     } | ||||
|  * } | ||||
|  * \endcode | ||||
|  * | ||||
|  * \code | ||||
|  * ResultVal<int> frob_result = Frobnicate(0.75f); | ||||
|  * auto frob_result = Frobnicate(0.75f); | ||||
|  * if (frob_result) { | ||||
|  *     // Frobbed ok
 | ||||
|  *     printf("My cookie is %d\n", *frob_result); | ||||
|  * } else { | ||||
|  *     printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex); | ||||
|  *     printf("Guess I overdid it. :( Error code: %ux\n", frob_result.Code().raw); | ||||
|  * } | ||||
|  * \endcode | ||||
|  */ | ||||
| template <typename T> | ||||
| class ResultVal { | ||||
| public: | ||||
|     /// Constructs an empty `ResultVal` with the given error code. The code must not be a success
 | ||||
|     /// code.
 | ||||
|     ResultVal(ResultCode error_code = ResultCode(-1)) : result_code(error_code) { | ||||
|         ASSERT(error_code.IsError()); | ||||
|     } | ||||
|     constexpr ResultVal() : expected{} {} | ||||
| 
 | ||||
|     /**
 | ||||
|      * Similar to the non-member function `MakeResult`, with the exception that you can manually | ||||
|      * specify the success code. `success_code` must not be an error code. | ||||
|      */ | ||||
|     template <typename... Args> | ||||
|     static ResultVal WithCode(ResultCode success_code, Args&&... args) { | ||||
|         ResultVal<T> result; | ||||
|         result.emplace(success_code, std::forward<Args>(args)...); | ||||
|         return result; | ||||
|     } | ||||
|     constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {} | ||||
| 
 | ||||
|     ResultVal(const ResultVal& o) : result_code(o.result_code) { | ||||
|         if (!o.empty()) { | ||||
|             new (&object) T(o.object); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ResultVal(ResultVal&& o) : result_code(o.result_code) { | ||||
|         if (!o.empty()) { | ||||
|             new (&object) T(std::move(o.object)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ~ResultVal() { | ||||
|         if (!empty()) { | ||||
|             object.~T(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ResultVal& operator=(const ResultVal& o) { | ||||
|         if (!empty()) { | ||||
|             if (!o.empty()) { | ||||
|                 object = o.object; | ||||
|             } else { | ||||
|                 object.~T(); | ||||
|             } | ||||
|         } else { | ||||
|             if (!o.empty()) { | ||||
|                 new (&object) T(o.object); | ||||
|             } | ||||
|         } | ||||
|         result_code = o.result_code; | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Replaces the current result with a new constructed result value in-place. The code must not | ||||
|      * be an error code. | ||||
|      */ | ||||
|     template <typename... Args> | ||||
|     void emplace(ResultCode success_code, Args&&... args) { | ||||
|         ASSERT(success_code.IsSuccess()); | ||||
|         if (!empty()) { | ||||
|             object.~T(); | ||||
|         } | ||||
|         new (&object) T(std::forward<Args>(args)...); | ||||
|         result_code = success_code; | ||||
|     } | ||||
| 
 | ||||
|     /// Returns true if the `ResultVal` contains an error code and no value.
 | ||||
|     bool empty() const { | ||||
|         return result_code.IsError(); | ||||
|     } | ||||
| 
 | ||||
|     /// Returns true if the `ResultVal` contains a return value.
 | ||||
|     bool Succeeded() const { | ||||
|         return result_code.IsSuccess(); | ||||
|     } | ||||
|     /// Returns true if the `ResultVal` contains an error code and no value.
 | ||||
|     bool Failed() const { | ||||
|         return empty(); | ||||
|     } | ||||
| 
 | ||||
|     ResultCode Code() const { | ||||
|         return result_code; | ||||
|     } | ||||
| 
 | ||||
|     const T& operator*() const { | ||||
|         return object; | ||||
|     } | ||||
|     T& operator*() { | ||||
|         return object; | ||||
|     } | ||||
|     const T* operator->() const { | ||||
|         return &object; | ||||
|     } | ||||
|     T* operator->() { | ||||
|         return &object; | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
 | ||||
|     template <typename U> | ||||
|     T ValueOr(U&& value) const { | ||||
|         return !empty() ? object : std::move(value); | ||||
|     constexpr ResultVal(U&& val) : expected{std::forward<U>(val)} {} | ||||
| 
 | ||||
|     template <typename... Args> | ||||
|     constexpr ResultVal(Args&&... args) : expected{std::in_place, std::forward<Args>(args)...} {} | ||||
| 
 | ||||
|     ~ResultVal() = default; | ||||
| 
 | ||||
|     constexpr ResultVal(const ResultVal&) = default; | ||||
|     constexpr ResultVal(ResultVal&&) = default; | ||||
| 
 | ||||
|     ResultVal& operator=(const ResultVal&) = default; | ||||
|     ResultVal& operator=(ResultVal&&) = default; | ||||
| 
 | ||||
|     [[nodiscard]] constexpr explicit operator bool() const noexcept { | ||||
|         return expected.has_value(); | ||||
|     } | ||||
| 
 | ||||
|     /// Asserts that the result succeeded and returns a reference to it.
 | ||||
|     T& Unwrap() & { | ||||
|         ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | ||||
|         return **this; | ||||
|     [[nodiscard]] constexpr ResultCode Code() const { | ||||
|         return expected.has_value() ? RESULT_SUCCESS : expected.error(); | ||||
|     } | ||||
| 
 | ||||
|     T&& Unwrap() && { | ||||
|     [[nodiscard]] constexpr bool Succeeded() const { | ||||
|         return expected.has_value(); | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr bool Failed() const { | ||||
|         return !expected.has_value(); | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr T* operator->() { | ||||
|         return std::addressof(expected.value()); | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr const T* operator->() const { | ||||
|         return std::addressof(expected.value()); | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr T& operator*() & { | ||||
|         return *expected; | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr const T& operator*() const& { | ||||
|         return *expected; | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr T&& operator*() && { | ||||
|         return *expected; | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr const T&& operator*() const&& { | ||||
|         return *expected; | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr T& Unwrap() & { | ||||
|         ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | ||||
|         return std::move(**this); | ||||
|         return expected.value(); | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr const T& Unwrap() const& { | ||||
|         ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | ||||
|         return expected.value(); | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr T&& Unwrap() && { | ||||
|         ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | ||||
|         return std::move(expected.value()); | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] constexpr const T&& Unwrap() const&& { | ||||
|         ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); | ||||
|         return std::move(expected.value()); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U> | ||||
|     [[nodiscard]] constexpr T ValueOr(U&& v) const& { | ||||
|         return expected.value_or(v); | ||||
|     } | ||||
| 
 | ||||
|     template <typename U> | ||||
|     [[nodiscard]] constexpr T ValueOr(U&& v) && { | ||||
|         return expected.value_or(v); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     // A union is used to allocate the storage for the value, while allowing us to construct and
 | ||||
|     // destruct it at will.
 | ||||
|     union { | ||||
|         T object; | ||||
|     }; | ||||
|     ResultCode result_code; | ||||
|     // TODO: Replace this with std::expected once it is standardized in the STL.
 | ||||
|     Common::Expected<T, ResultCode> expected; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct | ||||
|  * `T` with and creates a success `ResultVal` contained the constructed value. | ||||
|  */ | ||||
| template <typename T, typename... Args> | ||||
| ResultVal<T> MakeResult(Args&&... args) { | ||||
|     return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just | ||||
|  * copy or move constructing. | ||||
|  */ | ||||
| template <typename Arg> | ||||
| ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) { | ||||
|     return ResultVal<std::remove_reference_t<Arg>>::WithCode(RESULT_SUCCESS, | ||||
|                                                              std::forward<Arg>(arg)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps | ||||
|  * the contained value and assigns it to `target`, which can be either an l-value expression or a | ||||
|  |  | |||
|  | @ -92,7 +92,7 @@ CIAFile::~CIAFile() { | |||
| 
 | ||||
| ResultVal<std::size_t> CIAFile::Read(u64 offset, std::size_t length, u8* buffer) const { | ||||
|     UNIMPLEMENTED(); | ||||
|     return MakeResult<std::size_t>(length); | ||||
|     return length; | ||||
| } | ||||
| 
 | ||||
| ResultCode CIAFile::WriteTicket() { | ||||
|  | @ -203,7 +203,7 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length, | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult(length); | ||||
|     return length; | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::size_t> CIAFile::Write(u64 offset, std::size_t length, bool flush, | ||||
|  | @ -235,7 +235,7 @@ ResultVal<std::size_t> CIAFile::Write(u64 offset, std::size_t length, bool flush | |||
| 
 | ||||
|     // If we don't have a header yet, we can't pull offsets of other sections
 | ||||
|     if (install_state == CIAInstallState::InstallStarted) | ||||
|         return MakeResult<std::size_t>(length); | ||||
|         return length; | ||||
| 
 | ||||
|     // If we have been given data before (or including) .app content, pull it into
 | ||||
|     // our buffer, but only pull *up to* the content offset, no further.
 | ||||
|  | @ -267,14 +267,14 @@ ResultVal<std::size_t> CIAFile::Write(u64 offset, std::size_t length, bool flush | |||
| 
 | ||||
|     // Content data sizes can only be retrieved from TMD data
 | ||||
|     if (install_state != CIAInstallState::TMDLoaded) | ||||
|         return MakeResult<std::size_t>(length); | ||||
|         return length; | ||||
| 
 | ||||
|     // From this point forward, data will no longer be buffered in data
 | ||||
|     auto result = WriteContentData(offset, length, buffer); | ||||
|     if (result.Failed()) | ||||
|         return result; | ||||
| 
 | ||||
|     return MakeResult<std::size_t>(length); | ||||
|     return length; | ||||
| } | ||||
| 
 | ||||
| u64 CIAFile::GetSize() const { | ||||
|  | @ -1316,7 +1316,7 @@ ResultVal<std::unique_ptr<AMFileWrapper>> GetFileFromSession( | |||
|             // File::OpenSubFile
 | ||||
|             std::size_t offset = file->GetSessionFileOffset(server); | ||||
|             std::size_t size = file->GetSessionFileSize(server); | ||||
|             return MakeResult(std::make_unique<AMFileWrapper>(file, offset, size)); | ||||
|             return std::make_unique<AMFileWrapper>(file, offset, size); | ||||
|         } | ||||
| 
 | ||||
|         LOG_ERROR(Service_AM, "Failed to cast handle to FSFile!"); | ||||
|  |  | |||
|  | @ -291,7 +291,7 @@ ResultVal<MessageParameter> AppletManager::GlanceParameter(AppletId app_id) { | |||
|         next_parameter = {}; | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<MessageParameter>(std::move(parameter)); | ||||
|     return parameter; | ||||
| } | ||||
| 
 | ||||
| ResultVal<MessageParameter> AppletManager::ReceiveParameter(AppletId app_id) { | ||||
|  | @ -333,7 +333,7 @@ ResultVal<AppletManager::GetLockHandleResult> AppletManager::GetLockHandle( | |||
|                   corrected_attributes.raw); | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<AppletManager::GetLockHandleResult>({corrected_attributes, 0, lock}); | ||||
|     return GetLockHandleResult{corrected_attributes, 0, lock}; | ||||
| } | ||||
| 
 | ||||
| ResultVal<AppletManager::InitializeResult> AppletManager::Initialize(AppletId app_id, | ||||
|  | @ -372,8 +372,7 @@ ResultVal<AppletManager::InitializeResult> AppletManager::Initialize(AppletId ap | |||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<InitializeResult>( | ||||
|         {slot_data->notification_event, slot_data->parameter_event}); | ||||
|     return InitializeResult{slot_data->notification_event, slot_data->parameter_event}; | ||||
| } | ||||
| 
 | ||||
| ResultCode AppletManager::Enable(AppletAttributes attributes) { | ||||
|  | @ -420,7 +419,7 @@ ResultVal<Notification> AppletManager::InquireNotification(AppletId app_id) { | |||
|         if (slot_data->registered) { | ||||
|             auto notification = slot_data->notification; | ||||
|             slot_data->notification = Notification::None; | ||||
|             return MakeResult<Notification>(notification); | ||||
|             return notification; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -943,12 +942,12 @@ ResultVal<AppletManager::AppletManInfo> AppletManager::GetAppletManInfo( | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult<AppletManInfo>({ | ||||
|     return AppletManInfo{ | ||||
|         .active_applet_pos = active_applet_pos, | ||||
|         .requested_applet_id = requested_applet_id, | ||||
|         .home_menu_applet_id = AppletId::HomeMenu, | ||||
|         .active_applet_id = active_applet_id, | ||||
|     }); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| ResultVal<AppletManager::AppletInfo> AppletManager::GetAppletInfo(AppletId app_id) { | ||||
|  | @ -968,8 +967,13 @@ ResultVal<AppletManager::AppletInfo> AppletManager::GetAppletInfo(AppletId app_i | |||
|     auto media_type = ((slot_data->title_id >> 32) & 0xFFFFFFFF) == 0x00040000 | ||||
|                           ? Service::FS::MediaType::SDMC | ||||
|                           : Service::FS::MediaType::NAND; | ||||
|     return MakeResult<AppletInfo>({slot_data->title_id, media_type, slot_data->registered, | ||||
|                                    slot_data->loaded, slot_data->attributes.raw}); | ||||
|     return AppletInfo{ | ||||
|         .title_id = slot_data->title_id, | ||||
|         .media_type = media_type, | ||||
|         .registered = slot_data->registered, | ||||
|         .loaded = slot_data->loaded, | ||||
|         .attributes = slot_data->attributes.raw, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, | ||||
|  |  | |||
|  | @ -1239,7 +1239,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ | |||
|                 const u32 message_size = static_cast<u32>(message->GetSize()); | ||||
|                 std::vector<u8> buffer(message_size); | ||||
| 
 | ||||
|                 message->Read(0, message_size, buffer.data()).Unwrap(); | ||||
|                 void(message->Read(0, message_size, buffer.data()).Unwrap()); | ||||
|                 message->Close(); | ||||
| 
 | ||||
|                 std::memcpy(&message_headers[outbox_info_header.message_num++], buffer.data(), | ||||
|  | @ -1329,7 +1329,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ | |||
|                 const u32 message_size = static_cast<u32>(message->GetSize()); | ||||
|                 std::vector<u8> buffer(message_size); | ||||
| 
 | ||||
|                 message->Read(0, message_size, buffer.data()).Unwrap(); | ||||
|                 void(message->Read(0, message_size, buffer.data()).Unwrap()); | ||||
|                 message->Close(); | ||||
| 
 | ||||
|                 // Message id is at offset 0x20, and is 8 bytes
 | ||||
|  |  | |||
|  | @ -432,7 +432,7 @@ ResultVal<void*> Module::GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 f | |||
|     else | ||||
|         pointer = &cfg_config_file_buffer[itr->offset_or_data]; | ||||
| 
 | ||||
|     return MakeResult<void*>(pointer); | ||||
|     return pointer; | ||||
| } | ||||
| 
 | ||||
| ResultCode Module::GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) { | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ ResultVal<ArchiveHandle> ArchiveManager::OpenArchive(ArchiveIdCode id_code, | |||
|         ++next_handle; | ||||
|     } | ||||
|     handle_map.emplace(next_handle, std::move(res)); | ||||
|     return MakeResult(next_handle++); | ||||
|     return next_handle++; | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveManager::CloseArchive(ArchiveHandle handle) { | ||||
|  | @ -103,7 +103,7 @@ ArchiveManager::OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys: | |||
|     } | ||||
| 
 | ||||
|     auto file = std::make_shared<File>(system.Kernel(), std::move(backend).Unwrap(), path); | ||||
|     return std::make_pair(MakeResult(std::move(file)), open_timeout_ns); | ||||
|     return std::make_pair(std::move(file), open_timeout_ns); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveManager::DeleteFileFromArchive(ArchiveHandle archive_handle, | ||||
|  | @ -197,8 +197,7 @@ ResultVal<std::shared_ptr<Directory>> ArchiveManager::OpenDirectoryFromArchive( | |||
|         return backend.Code(); | ||||
|     } | ||||
| 
 | ||||
|     auto directory = std::make_shared<Directory>(std::move(backend).Unwrap(), path); | ||||
|     return MakeResult(std::move(directory)); | ||||
|     return std::make_shared<Directory>(std::move(backend).Unwrap(), path); | ||||
| } | ||||
| 
 | ||||
| ResultVal<u64> ArchiveManager::GetFreeBytesInArchive(ArchiveHandle archive_handle) { | ||||
|  | @ -206,7 +205,7 @@ ResultVal<u64> ArchiveManager::GetFreeBytesInArchive(ArchiveHandle archive_handl | |||
|     if (archive == nullptr) { | ||||
|         return FileSys::ERR_INVALID_ARCHIVE_HANDLE; | ||||
|     } | ||||
|     return MakeResult(archive->GetFreeBytes()); | ||||
|     return archive->GetFreeBytes(); | ||||
| } | ||||
| 
 | ||||
| ResultCode ArchiveManager::FormatArchive(ArchiveIdCode id_code, | ||||
|  | @ -314,7 +313,7 @@ ResultVal<ArchiveResource> ArchiveManager::GetArchiveResource(MediaType media_ty | |||
|     resource.cluster_size_in_bytes = 16384; | ||||
|     resource.partition_capacity_in_clusters = 0x80000; // 8GiB capacity
 | ||||
|     resource.free_space_in_clusters = 0x80000;         // 8GiB free
 | ||||
|     return MakeResult(resource); | ||||
|     return resource; | ||||
| } | ||||
| 
 | ||||
| void ArchiveManager::RegisterArchiveTypes() { | ||||
|  |  | |||
|  | @ -845,11 +845,11 @@ ResultVal<u16> FS_USER::GetSpecialContentIndexFromGameCard(u64 title_id, Special | |||
| 
 | ||||
|     switch (type) { | ||||
|     case SpecialContentType::Update: | ||||
|         return MakeResult(static_cast<u16>(NCSDContentIndex::Update)); | ||||
|         return static_cast<u16>(NCSDContentIndex::Update); | ||||
|     case SpecialContentType::Manual: | ||||
|         return MakeResult(static_cast<u16>(NCSDContentIndex::Manual)); | ||||
|         return static_cast<u16>(NCSDContentIndex::Manual); | ||||
|     case SpecialContentType::DLPChild: | ||||
|         return MakeResult(static_cast<u16>(NCSDContentIndex::DLP)); | ||||
|         return static_cast<u16>(NCSDContentIndex::DLP); | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
|  | @ -874,9 +874,9 @@ ResultVal<u16> FS_USER::GetSpecialContentIndexFromTMD(MediaType media_type, u64 | |||
| 
 | ||||
|     switch (type) { | ||||
|     case SpecialContentType::Manual: | ||||
|         return MakeResult(static_cast<u16>(FileSys::TMDContentIndex::Manual)); | ||||
|         return static_cast<u16>(FileSys::TMDContentIndex::Manual); | ||||
|     case SpecialContentType::DLPChild: | ||||
|         return MakeResult(static_cast<u16>(FileSys::TMDContentIndex::DLP)); | ||||
|         return static_cast<u16>(FileSys::TMDContentIndex::DLP); | ||||
|     default: | ||||
|         ASSERT(false); | ||||
|     } | ||||
|  |  | |||
|  | @ -301,7 +301,7 @@ ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size, VAddr data_segment_ | |||
|         } | ||||
|         SetEntry(system.Memory(), i, segment); | ||||
|     } | ||||
|     return MakeResult<u32>(prev_data_segment + module_address); | ||||
|     return prev_data_segment + module_address; | ||||
| } | ||||
| 
 | ||||
| ResultCode CROHelper::RebaseExportNamedSymbolTable() { | ||||
|  | @ -776,10 +776,10 @@ ResultCode CROHelper::ApplyImportNamedSymbol(VAddr crs_address) { | |||
|                             return result; | ||||
|                         } | ||||
| 
 | ||||
|                         return MakeResult<bool>(false); | ||||
|                         return false; | ||||
|                     } | ||||
| 
 | ||||
|                     return MakeResult<bool>(true); | ||||
|                     return true; | ||||
|                 }); | ||||
|             if (result.IsError()) { | ||||
|                 return result; | ||||
|  | @ -897,9 +897,9 @@ ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) { | |||
|                             return result; | ||||
|                         } | ||||
|                     } | ||||
|                     return MakeResult<bool>(false); | ||||
|                     return false; | ||||
|                 } | ||||
|                 return MakeResult<bool>(true); | ||||
|                 return true; | ||||
|             }); | ||||
|         if (result.IsError()) { | ||||
|             return result; | ||||
|  | @ -1090,10 +1090,10 @@ ResultCode CROHelper::ApplyExitRelocations(VAddr crs_address) { | |||
|                             return result; | ||||
|                         } | ||||
| 
 | ||||
|                         return MakeResult<bool>(false); | ||||
|                         return false; | ||||
|                     } | ||||
| 
 | ||||
|                     return MakeResult<bool>(true); | ||||
|                     return true; | ||||
|                 }); | ||||
|             if (result.IsError()) { | ||||
|                 LOG_ERROR(Service_LDR, "Error applying exit relocation {:08X}", result.raw); | ||||
|  | @ -1317,7 +1317,7 @@ ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) { | |||
|                                     if (result.IsError()) | ||||
|                                         return result; | ||||
| 
 | ||||
|                                     return MakeResult<bool>(true); | ||||
|                                     return true; | ||||
|                                 }); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error applying export {:08X}", result.raw); | ||||
|  | @ -1362,7 +1362,7 @@ ResultCode CROHelper::Unlink(VAddr crs_address) { | |||
|                                     if (result.IsError()) | ||||
|                                         return result; | ||||
| 
 | ||||
|                                     return MakeResult<bool>(true); | ||||
|                                     return true; | ||||
|                                 }); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error resetting export {:08X}", result.raw); | ||||
|  |  | |||
|  | @ -673,7 +673,7 @@ ResultVal<std::shared_ptr<Kernel::Event>> NWM_UDS::Initialize( | |||
|         channel_data.clear(); | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult(connection_status_event); | ||||
|     return connection_status_event; | ||||
| } | ||||
| 
 | ||||
| void NWM_UDS::InitializeWithVersion(Kernel::HLERequestContext& ctx) { | ||||
|  |  | |||
|  | @ -131,7 +131,7 @@ void PLG_LDR::OnProcessExit(Kernel::Process& process, Kernel::KernelSystem& kern | |||
| 
 | ||||
| ResultVal<Kernel::Handle> PLG_LDR::GetMemoryChangedHandle(Kernel::KernelSystem& kernel) { | ||||
|     if (plgldr_context.memory_changed_handle) | ||||
|         return MakeResult(plgldr_context.memory_changed_handle); | ||||
|         return plgldr_context.memory_changed_handle; | ||||
| 
 | ||||
|     std::shared_ptr<Kernel::Event> evt = kernel.CreateEvent( | ||||
|         Kernel::ResetType::OneShot, | ||||
|  | @ -139,7 +139,7 @@ ResultVal<Kernel::Handle> PLG_LDR::GetMemoryChangedHandle(Kernel::KernelSystem& | |||
|     CASCADE_RESULT(plgldr_context.memory_changed_handle, | ||||
|                    kernel.GetCurrentProcess()->handle_table.Create(std::move(evt))); | ||||
| 
 | ||||
|     return MakeResult(plgldr_context.memory_changed_handle); | ||||
|     return plgldr_context.memory_changed_handle; | ||||
| } | ||||
| 
 | ||||
| void PLG_LDR::OnMemoryChanged(Kernel::Process& process, Kernel::KernelSystem& kernel) { | ||||
|  |  | |||
|  | @ -44,7 +44,7 @@ ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( | |||
| 
 | ||||
|     registered_services_inverse.emplace(client_port->GetObjectId(), name); | ||||
|     registered_services.emplace(std::move(name), std::move(client_port)); | ||||
|     return MakeResult(std::move(server_port)); | ||||
|     return server_port; | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::shared_ptr<Kernel::ClientPort>> ServiceManager::GetServicePort( | ||||
|  | @ -56,7 +56,7 @@ ResultVal<std::shared_ptr<Kernel::ClientPort>> ServiceManager::GetServicePort( | |||
|         return ERR_SERVICE_NOT_REGISTERED; | ||||
|     } | ||||
| 
 | ||||
|     return MakeResult(it->second); | ||||
|     return it->second; | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::shared_ptr<Kernel::ClientSession>> ServiceManager::ConnectToService( | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue