diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/common/typed_address.h | 320 |
2 files changed, 321 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 61ab68864..90805babe 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -132,6 +132,7 @@ add_library(common STATIC time_zone.h tiny_mt.h tree.h + typed_address.h uint128.h unique_function.h uuid.cpp diff --git a/src/common/typed_address.h b/src/common/typed_address.h new file mode 100644 index 000000000..cf7bbeae1 --- /dev/null +++ b/src/common/typed_address.h @@ -0,0 +1,320 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <compare> +#include <type_traits> +#include <fmt/format.h> + +#include "common/common_types.h" + +namespace Common { + +template <bool Virtual, typename T> +class TypedAddress { +public: + // Constructors. + constexpr inline TypedAddress() : m_address(0) {} + constexpr inline TypedAddress(uint64_t a) : m_address(a) {} + + template <typename U> + constexpr inline explicit TypedAddress(const U* ptr) + : m_address(reinterpret_cast<uint64_t>(ptr)) {} + + // Copy constructor. + constexpr inline TypedAddress(const TypedAddress& rhs) = default; + + // Assignment operator. + constexpr inline TypedAddress& operator=(const TypedAddress& rhs) = default; + + // Arithmetic operators. + template <typename I> + constexpr inline TypedAddress operator+(I rhs) const { + static_assert(std::is_integral_v<I>); + return m_address + rhs; + } + + constexpr inline TypedAddress operator+(TypedAddress rhs) const { + return m_address + rhs.m_address; + } + + constexpr inline TypedAddress operator++() { + return ++m_address; + } + + constexpr inline TypedAddress operator++(int) { + return m_address++; + } + + template <typename I> + constexpr inline TypedAddress operator-(I rhs) const { + static_assert(std::is_integral_v<I>); + return m_address - rhs; + } + + constexpr inline ptrdiff_t operator-(TypedAddress rhs) const { + return m_address - rhs.m_address; + } + + constexpr inline TypedAddress operator--() { + return --m_address; + } + + constexpr inline TypedAddress operator--(int) { + return m_address--; + } + + template <typename I> + constexpr inline TypedAddress operator+=(I rhs) { + static_assert(std::is_integral_v<I>); + m_address += rhs; + return *this; + } + + template <typename I> + constexpr inline TypedAddress operator-=(I rhs) { + static_assert(std::is_integral_v<I>); + m_address -= rhs; + return *this; + } + + // Logical operators. + constexpr inline uint64_t operator&(uint64_t mask) const { + return m_address & mask; + } + + constexpr inline uint64_t operator|(uint64_t mask) const { + return m_address | mask; + } + + template <typename I> + constexpr inline TypedAddress operator|=(I rhs) { + static_assert(std::is_integral_v<I>); + m_address |= rhs; + return *this; + } + + constexpr inline uint64_t operator<<(int shift) const { + return m_address << shift; + } + + constexpr inline uint64_t operator>>(int shift) const { + return m_address >> shift; + } + + template <typename U> + constexpr inline size_t operator/(U size) const { + return m_address / size; + } + + constexpr explicit operator bool() const { + return m_address != 0; + } + + // constexpr inline uint64_t operator%(U align) const { return m_address % align; } + + // Comparison operators. + constexpr bool operator==(const TypedAddress&) const = default; + constexpr bool operator!=(const TypedAddress&) const = default; + constexpr auto operator<=>(const TypedAddress&) const = default; + + // For convenience, also define comparison operators versus uint64_t. + constexpr inline bool operator==(uint64_t rhs) const { + return m_address == rhs; + } + + constexpr inline bool operator!=(uint64_t rhs) const { + return m_address != rhs; + } + + // Allow getting the address explicitly, for use in accessors. + constexpr inline uint64_t GetValue() const { + return m_address; + } + +private: + uint64_t m_address{}; +}; + +struct PhysicalAddressTag {}; +struct VirtualAddressTag {}; +struct ProcessAddressTag {}; + +using PhysicalAddress = TypedAddress<false, PhysicalAddressTag>; +using VirtualAddress = TypedAddress<true, VirtualAddressTag>; +using ProcessAddress = TypedAddress<true, ProcessAddressTag>; + +// Define accessors. +template <typename T> +concept IsTypedAddress = std::same_as<T, PhysicalAddress> || std::same_as<T, VirtualAddress> || + std::same_as<T, ProcessAddress>; + +template <typename T> +constexpr inline T Null = [] { + if constexpr (std::is_same<T, uint64_t>::value) { + return 0; + } else { + static_assert(std::is_same<T, PhysicalAddress>::value || + std::is_same<T, VirtualAddress>::value || + std::is_same<T, ProcessAddress>::value); + return T(0); + } +}(); + +// Basic type validations. +static_assert(sizeof(PhysicalAddress) == sizeof(uint64_t)); +static_assert(sizeof(VirtualAddress) == sizeof(uint64_t)); +static_assert(sizeof(ProcessAddress) == sizeof(uint64_t)); + +static_assert(std::is_trivially_copyable_v<PhysicalAddress>); +static_assert(std::is_trivially_copyable_v<VirtualAddress>); +static_assert(std::is_trivially_copyable_v<ProcessAddress>); + +static_assert(std::is_trivially_copy_constructible_v<PhysicalAddress>); +static_assert(std::is_trivially_copy_constructible_v<VirtualAddress>); +static_assert(std::is_trivially_copy_constructible_v<ProcessAddress>); + +static_assert(std::is_trivially_move_constructible_v<PhysicalAddress>); +static_assert(std::is_trivially_move_constructible_v<VirtualAddress>); +static_assert(std::is_trivially_move_constructible_v<ProcessAddress>); + +static_assert(std::is_trivially_copy_assignable_v<PhysicalAddress>); +static_assert(std::is_trivially_copy_assignable_v<VirtualAddress>); +static_assert(std::is_trivially_copy_assignable_v<ProcessAddress>); + +static_assert(std::is_trivially_move_assignable_v<PhysicalAddress>); +static_assert(std::is_trivially_move_assignable_v<VirtualAddress>); +static_assert(std::is_trivially_move_assignable_v<ProcessAddress>); + +static_assert(std::is_trivially_destructible_v<PhysicalAddress>); +static_assert(std::is_trivially_destructible_v<VirtualAddress>); +static_assert(std::is_trivially_destructible_v<ProcessAddress>); + +static_assert(Null<uint64_t> == 0); +static_assert(Null<PhysicalAddress> == Null<uint64_t>); +static_assert(Null<VirtualAddress> == Null<uint64_t>); +static_assert(Null<ProcessAddress> == Null<uint64_t>); + +// Constructor/assignment validations. +static_assert([] { + const PhysicalAddress a(5); + PhysicalAddress b(a); + return b; +}() == PhysicalAddress(5)); +static_assert([] { + const PhysicalAddress a(5); + PhysicalAddress b(10); + b = a; + return b; +}() == PhysicalAddress(5)); + +// Arithmetic validations. +static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15)); +static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5)); +static_assert([] { + PhysicalAddress v(10); + v += 5; + return v; +}() == PhysicalAddress(15)); +static_assert([] { + PhysicalAddress v(10); + v -= 5; + return v; +}() == PhysicalAddress(5)); +static_assert(PhysicalAddress(10)++ == PhysicalAddress(10)); +static_assert(++PhysicalAddress(10) == PhysicalAddress(11)); +static_assert(PhysicalAddress(10)-- == PhysicalAddress(10)); +static_assert(--PhysicalAddress(10) == PhysicalAddress(9)); + +// Logical validations. +static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111); +static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101); +static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110); +static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010); +static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101); +static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010); +static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000); +static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101); +static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111); +static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111); + +// Comparisons. +static_assert(PhysicalAddress(0) == PhysicalAddress(0)); +static_assert(PhysicalAddress(0) != PhysicalAddress(1)); +static_assert(PhysicalAddress(0) < PhysicalAddress(1)); +static_assert(PhysicalAddress(0) <= PhysicalAddress(1)); +static_assert(PhysicalAddress(1) > PhysicalAddress(0)); +static_assert(PhysicalAddress(1) >= PhysicalAddress(0)); + +static_assert(!(PhysicalAddress(0) == PhysicalAddress(1))); +static_assert(!(PhysicalAddress(0) != PhysicalAddress(0))); +static_assert(!(PhysicalAddress(1) < PhysicalAddress(0))); +static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0))); +static_assert(!(PhysicalAddress(0) > PhysicalAddress(1))); +static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1))); + +} // namespace Common + +template <bool Virtual, typename T> +constexpr inline uint64_t GetInteger(Common::TypedAddress<Virtual, T> address) { + return address.GetValue(); +} + +template <> +struct fmt::formatter<Common::PhysicalAddress> { + constexpr auto parse(fmt::format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Common::PhysicalAddress& addr, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{:#x}", static_cast<u64>(addr.GetValue())); + } +}; + +template <> +struct fmt::formatter<Common::ProcessAddress> { + constexpr auto parse(fmt::format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Common::ProcessAddress& addr, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{:#x}", static_cast<u64>(addr.GetValue())); + } +}; + +template <> +struct fmt::formatter<Common::VirtualAddress> { + constexpr auto parse(fmt::format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Common::VirtualAddress& addr, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{:#x}", static_cast<u64>(addr.GetValue())); + } +}; + +namespace std { + +template <> +struct hash<Common::PhysicalAddress> { + size_t operator()(const Common::PhysicalAddress& k) const noexcept { + return k.GetValue(); + } +}; + +template <> +struct hash<Common::ProcessAddress> { + size_t operator()(const Common::ProcessAddress& k) const noexcept { + return k.GetValue(); + } +}; + +template <> +struct hash<Common::VirtualAddress> { + size_t operator()(const Common::VirtualAddress& k) const noexcept { + return k.GetValue(); + } +}; + +} // namespace std |