From 635aac8ff301fd0430acd92526a1e171e3a80c90 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 28 Jun 2026 19:57:16 +0200 Subject: [PATCH] Fix consteval read_from_ptr and add sanity checks if possible --- rpcs3/CMakeLists.txt | 1 + rpcs3/Emu/RSX/VK/VKMemAlloc.cpp | 2 +- rpcs3/tests/rpcs3_test.vcxproj | 1 + rpcs3/tests/test_types_util.cpp | 487 ++++++++++++++++++++++++++++++++ rpcs3/util/types.hpp | 42 ++- 5 files changed, 530 insertions(+), 3 deletions(-) create mode 100644 rpcs3/tests/test_types_util.cpp diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 2fa497467c..fee8fa4327 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -193,6 +193,7 @@ if(BUILD_RPCS3_TESTS) tests/test_rsx_fp_asm.cpp tests/test_dmux_pamf.cpp tests/test_spu_analyser.cpp + tests/test_types_util.cpp ) target_link_libraries(rpcs3_test diff --git a/rpcs3/Emu/RSX/VK/VKMemAlloc.cpp b/rpcs3/Emu/RSX/VK/VKMemAlloc.cpp index 4689e910d2..09d6ae13fb 100644 --- a/rpcs3/Emu/RSX/VK/VKMemAlloc.cpp +++ b/rpcs3/Emu/RSX/VK/VKMemAlloc.cpp @@ -56,7 +56,7 @@ private: #endif #endif #include -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) #pragma warning(pop) #else #pragma GCC diagnostic pop diff --git a/rpcs3/tests/rpcs3_test.vcxproj b/rpcs3/tests/rpcs3_test.vcxproj index fb96921efc..274d9d028d 100644 --- a/rpcs3/tests/rpcs3_test.vcxproj +++ b/rpcs3/tests/rpcs3_test.vcxproj @@ -105,6 +105,7 @@ + diff --git a/rpcs3/tests/test_types_util.cpp b/rpcs3/tests/test_types_util.cpp new file mode 100644 index 0000000000..8f9251e0a5 --- /dev/null +++ b/rpcs3/tests/test_types_util.cpp @@ -0,0 +1,487 @@ +#include "stdafx.h" +#include + +extern atomic_t g_headless; + +#define CHECK_COMPILATION_ERRORS 0 +#define CHECK_DEATH 0 + +namespace utils +{ + TEST(Utils, read_write_to_ptr_array) + { + g_headless = true; // Disable exception popups + + // write_to_ptr without pos + std::array arr{}; + write_to_ptr(arr, u8(umax)); + EXPECT_EQ(arr.at(0), u8(umax)); + write_to_ptr(arr, u16(umax)); + EXPECT_EQ(*utils::bless(arr.data()), u16(umax)); + write_to_ptr(arr, u32(umax)); + EXPECT_EQ(*utils::bless(arr.data()), u32(umax)); + write_to_ptr(arr, u64(umax)); + EXPECT_EQ(*utils::bless(arr.data()), u64(umax)); + + // write_to_ptr and read_from_ptr with pos + std::array arr2{}; + + for (u8 i = 0; i < arr2.size(); i++) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(arr2.at(i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + arr2 = {}; + for (u16 i = 0; i < arr2.size(); i += sizeof(u16)) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(*utils::bless(arr2.data() + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + arr2 = {}; + for (u32 i = 0; i < arr2.size(); i += sizeof(u32)) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(*utils::bless(arr2.data() + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + arr2 = {}; + for (u64 i = 0; i < arr2.size(); i += sizeof(u64)) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(*utils::bless(arr2.data() + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + arr2 = {}; + for (usz i = 0; i < arr2.size(); i += sizeof(u128)) + { + write_to_ptr(arr2, i, u128(i)); + const u128 exp_u128 = i; + EXPECT_EQ(std::memcmp(arr2.data(), &exp_u128, sizeof(u128)), 0); + const u128 val_u128 = read_from_ptr(arr2, i); + EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0); + } + + // write_to_ptr without pos + std::array arr32{}; + write_to_ptr(arr32, u32(umax)); + EXPECT_EQ(*utils::bless(arr32.data()), u32(umax)); + write_to_ptr(arr32, u64(umax)); + EXPECT_EQ(*utils::bless(arr32.data()), u64(umax)); + + // write_to_ptr and read_from_ptr with pos + std::array arr32_2{}; + + std::memset(arr32_2.data(), 0, arr32_2.size() * sizeof(u32)); + for (u32 i = 0; i < arr32_2.size() * sizeof(u32); i += sizeof(u32)) + { + const usz index = i / sizeof(u32); + write_to_ptr(arr32_2, index, i); + EXPECT_EQ(*utils::bless(arr32_2.data() + index), i); + EXPECT_EQ(read_from_ptr(arr32_2, index), i); + } + + std::memset(arr32_2.data(), 0, arr32_2.size() * sizeof(u32)); + for (u64 i = 0; i < arr32_2.size() * sizeof(u32); i += sizeof(u64)) + { + const usz index = i / sizeof(u64); + write_to_ptr(arr32_2, index, i); + EXPECT_EQ(*utils::bless(arr32_2.data() + index), i); + EXPECT_EQ(read_from_ptr(arr32_2, index), i); + } + + std::memset(arr32_2.data(), 0, arr32_2.size() * sizeof(u32)); + for (usz i = 0; i < arr32_2.size() * sizeof(u32); i += sizeof(u128)) + { + const usz index = i / sizeof(u128); + write_to_ptr(arr32_2, index, u128(i)); + const u128 exp_u128 = i; + EXPECT_EQ(std::memcmp(arr32_2.data(), &exp_u128, sizeof(u128)), 0); + const u128 val_u128 = read_from_ptr(arr32_2, index); + EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0); + } + + // These tests can take a long time and produce warnings, so let's only run them in local builds +#if CHECK_DEATH + EXPECT_DEATH(write_to_ptr(arr, u128()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u8()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u16()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u32()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u64()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u128()), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2.size()), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2.size()), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2.size()), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2.size()), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2.size()), ".*"); + + EXPECT_DEATH(write_to_ptr(arr32, u128()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, arr32_2.size(), u32()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, arr32_2.size(), u64()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, arr32_2.size(), u128()), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, arr32_2.size()), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, arr32_2.size()), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, arr32_2.size()), ".*"); +#endif + } + + TEST(Utils, read_write_to_ptr_c_array) + { + g_headless = true; // Disable exception popups + + // write_to_ptr without pos + u8 arr[sizeof(u64)]{}; + write_to_ptr(arr, u8(umax)); + EXPECT_EQ(*arr, u8(umax)); + write_to_ptr(arr, u16(umax)); + EXPECT_EQ(*utils::bless(arr + 0), u16(umax)); + write_to_ptr(arr, u32(umax)); + EXPECT_EQ(*utils::bless(arr + 0), u32(umax)); + write_to_ptr(arr, u64(umax)); + EXPECT_EQ(*utils::bless(arr + 0), u64(umax)); + + // write_to_ptr and read_from_ptr with pos + u8 arr2[sizeof(u128)]{}; + + for (u8 i = 0; i < sizeof(arr2); i++) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(arr2[i], i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + std::memset(arr2, 0, sizeof(arr2)); + for (u16 i = 0; i < sizeof(arr2); i += sizeof(u16)) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(*utils::bless(arr2 + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + std::memset(arr2, 0, sizeof(arr2)); + for (u32 i = 0; i < sizeof(arr2); i += sizeof(u32)) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(*utils::bless(arr2 + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + std::memset(arr2, 0, sizeof(arr2)); + for (u64 i = 0; i < sizeof(arr2); i += sizeof(u64)) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(*utils::bless(arr2 + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + std::memset(arr2, 0, sizeof(arr2)); + for (usz i = 0; i < sizeof(arr2); i += sizeof(u128)) + { + write_to_ptr(arr2, i, u128(i)); + const u128 exp_u128 = i; + EXPECT_EQ(std::memcmp(arr2, &exp_u128, sizeof(u128)), 0); + const u128 val_u128 = read_from_ptr(arr2, i); + EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0); + } + + // write_to_ptr without pos + u32 arr32[sizeof(u64) / sizeof(u32)]{}; + write_to_ptr(arr32, u32(umax)); + EXPECT_EQ(*utils::bless(arr32 + 0), u32(umax)); + write_to_ptr(arr32, u64(umax)); + EXPECT_EQ(*utils::bless(arr32 + 0), u64(umax)); + + // write_to_ptr and read_from_ptr with pos + u32 arr32_2[sizeof(u128) / sizeof(u32)]{}; + + std::memset(arr32_2, 0, sizeof(arr32_2)); + for (u32 i = 0; i < sizeof(arr32_2); i += sizeof(u32)) + { + const usz index = i / sizeof(u32); + write_to_ptr(arr32_2, index, i); + EXPECT_EQ(*utils::bless(arr32_2 + index), i); + EXPECT_EQ(read_from_ptr(arr32_2, index), i); + } + + std::memset(arr32_2, 0, sizeof(arr32_2)); + for (u64 i = 0; i < sizeof(arr32_2); i += sizeof(u64)) + { + const usz index = i / sizeof(u64); + write_to_ptr(arr32_2, index, i); + EXPECT_EQ(*utils::bless(arr32_2 + index), i); + EXPECT_EQ(read_from_ptr(arr32_2, index), i); + } + + std::memset(arr32_2, 0, sizeof(arr32_2)); + for (usz i = 0; i < sizeof(arr32_2); i += sizeof(u128)) + { + const usz index = i / sizeof(u128); + write_to_ptr(arr32_2, index, u128(i)); + const u128 exp_u128 = i; + EXPECT_EQ(std::memcmp(arr32_2, &exp_u128, sizeof(u128)), 0); + const u128 val_u128 = read_from_ptr(arr32_2, index); + EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0); + } + + // These tests can take a long time and produce warnings, so let's only run them in local builds +#if CHECK_DEATH + EXPECT_DEATH(write_to_ptr(arr, u128()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u8()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u16()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u32()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u64()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u128()), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, std::size(arr2)), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, std::size(arr2)), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, std::size(arr2)), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, std::size(arr2)), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, std::size(arr2)), ".*"); + + EXPECT_DEATH(write_to_ptr(arr32, u128()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, std::size(arr32_2), u32()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, std::size(arr32_2), u64()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, std::size(arr32_2), u128()), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, std::size(arr32_2)), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, std::size(arr32_2)), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, std::size(arr32_2)), ".*"); +#endif + } + + TEST(Utils, read_write_to_ptr_raw_array) + { + g_headless = true; // Disable exception popups + + // write_to_ptr without pos + constexpr usz arr_size = sizeof(u64); + u8* arr = new u8[arr_size]; + std::memset(arr, 0, arr_size); + + write_to_ptr(arr, u8(umax)); + EXPECT_EQ(*arr, u8(umax)); + write_to_ptr(arr, u16(umax)); + EXPECT_EQ(*utils::bless(arr), u16(umax)); + write_to_ptr(arr, u32(umax)); + EXPECT_EQ(*utils::bless(arr), u32(umax)); + write_to_ptr(arr, u64(umax)); + EXPECT_EQ(*utils::bless(arr), u64(umax)); + + // write_to_ptr and read_from_ptr with pos + constexpr usz arr2_size = sizeof(u128); + u8* arr2 = new u8[arr2_size]; + + std::memset(arr2, 0, arr2_size); + for (u8 i = 0; i < arr2_size; i++) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(arr2[i], i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + std::memset(arr2, 0, arr2_size); + for (u16 i = 0; i < arr2_size; i += sizeof(u16)) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(*utils::bless(arr2 + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + std::memset(arr2, 0, arr2_size); + for (u32 i = 0; i < arr2_size; i += sizeof(u32)) + { + write_to_ptr(arr2, i, i); + EXPECT_EQ(*utils::bless(arr2 + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + std::memset(arr2, 0, arr2_size); + for (usz i = 0; i < arr2_size; i += sizeof(u64)) + { + write_to_ptr(arr2, i, u64(i)); + EXPECT_EQ(*utils::bless(arr2 + i), i); + EXPECT_EQ(read_from_ptr(arr2, i), i); + } + + std::memset(arr2, 0, arr2_size); + for (usz i = 0; i < arr2_size; i += sizeof(u128)) + { + write_to_ptr(arr2, i, u128(i)); + const u128 exp_u128 = i; + EXPECT_EQ(std::memcmp(arr2, &exp_u128, sizeof(u128)), 0); + const u128 val_u128 = read_from_ptr(arr2, i); + EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0); + } + + // write_to_ptr without pos + constexpr usz arr32_count = sizeof(u64) / sizeof(u32); + constexpr usz arr32_size = arr32_count * sizeof(u32); + u32* arr32 = new u32[arr32_count]; + std::memset(arr32, 0, arr32_size); + + write_to_ptr(arr32, u32(umax)); + EXPECT_EQ(*utils::bless(arr32), u32(umax)); + write_to_ptr(arr32, u64(umax)); + EXPECT_EQ(*utils::bless(arr32), u64(umax)); + + // write_to_ptr and read_from_ptr with pos + constexpr usz arr32_2_count = sizeof(u128) / sizeof(u32); + constexpr usz arr32_2_size = arr32_2_count * sizeof(u32); + u32* arr32_2 = new u32[arr32_2_count]; + + std::memset(arr32_2, 0, arr32_2_size); + for (u32 i = 0; i < arr32_2_size; i += sizeof(u32)) + { + const usz index = i / sizeof(u32); + write_to_ptr(arr32_2, index, i); + EXPECT_EQ(*utils::bless(arr32_2 + index), i); + EXPECT_EQ(read_from_ptr(arr32_2, index), i); + } + + std::memset(arr32_2, 0, arr32_2_size); + for (u64 i = 0; i < arr32_2_size; i += sizeof(u64)) + { + const usz index = i / sizeof(u64); + write_to_ptr(arr32_2, index, i); + EXPECT_EQ(*utils::bless(arr32_2 + index), i); + EXPECT_EQ(read_from_ptr(arr32_2, index), i); + } + + std::memset(arr32_2, 0, arr32_2_size); + for (usz i = 0; i < arr32_2_size; i += sizeof(u128)) + { + const usz index = i / sizeof(u128); + write_to_ptr(arr32_2, index, u128(i)); + const u128 exp_u128 = i; + EXPECT_EQ(std::memcmp(arr32_2, &exp_u128, sizeof(u128)), 0); + const u128 val_u128 = read_from_ptr(arr32_2, index); + EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0); + } + + // There are no sanity checks for raw pointers in these functions, so these will not fail. +#if 0 + // These tests can take a long time and produce warnings, so let's only run them in local builds +#if CHECK_DEATH + EXPECT_DEATH(write_to_ptr(arr, u128()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u8()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u16()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u32()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u64()), ".*"); + EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u128()), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2_size), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2_size), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2_size), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2_size), ".*"); + EXPECT_DEATH(read_from_ptr(arr2, arr2_size), ".*"); + + EXPECT_DEATH(write_to_ptr(arr32, u128()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, arr32_count, u32()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, arr32_count, u64()), ".*"); + EXPECT_DEATH(write_to_ptr(arr32_2, arr32_count, u128()), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, arr32_count), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, arr32_count), ".*"); + EXPECT_DEATH(read_from_ptr(arr32_2, arr32_count), ".*"); +#endif +#endif + + delete[] arr; + delete[] arr2; + delete[] arr32; + delete[] arr32_2; + } + + TEST(Utils, read_from_ptr_const_eval_array) + { + g_headless = true; // Disable exception popups + + constexpr std::array arr { 1, 2, 3, 4, 5, 6, 7, 8 }; + + static_assert(read_from_ptr(arr, 0) == u8(1)); + static_assert(read_from_ptr(arr, 1) == u8(2)); + static_assert(read_from_ptr(arr, 2) == u8(3)); + static_assert(read_from_ptr(arr, 3) == u8(4)); + static_assert(read_from_ptr(arr, 4) == u8(5)); + static_assert(read_from_ptr(arr, 5) == u8(6)); + static_assert(read_from_ptr(arr, 6) == u8(7)); + static_assert(read_from_ptr(arr, 7) == u8(8)); + + static_assert(read_from_ptr>(arr, 0) == u16(0x0201)); + static_assert(read_from_ptr>(arr, 2) == u16(0x0403)); + static_assert(read_from_ptr>(arr, 4) == u16(0x0605)); + static_assert(read_from_ptr>(arr, 6) == u16(0x0807)); + + static_assert(read_from_ptr>(arr, 0) == u32(0x04030201)); + static_assert(read_from_ptr>(arr, 4) == u32(0x08070605)); + + static_assert(read_from_ptr>(arr, 0) == u64(0x0807060504030201)); + + constexpr std::array arr32 { 0x00000000, 0xFFFFFFFF }; + + static_assert(read_from_ptr(arr32, 0) == u32(0x00000000)); + static_assert(read_from_ptr(arr32, 1) == u32(0xFFFFFFFF)); + + static_assert(read_from_ptr>(arr32, 0) == u64(0xFFFFFFFF00000000)); + + // This should not compile +#if CHECK_COMPILATION_ERRORS + constexpr auto v8 = read_from_ptr(arr, arr.size()); + constexpr auto v16 = read_from_ptr(arr, arr.size() + 1 - sizeof(u16)); + constexpr auto v32 = read_from_ptr(arr, arr.size() + 1 - sizeof(u32)); + constexpr auto v64 = read_from_ptr(arr, arr.size() + 1 - sizeof(u64)); + constexpr auto v128 = read_from_ptr(arr, 0); + + constexpr auto v32_32 = read_from_ptr(arr32, arr32.size()); + constexpr auto v64_32 = read_from_ptr(arr32, arr32.size() + 1 - sizeof(u64) / sizeof(u32)); + constexpr auto v128_32 = read_from_ptr(arr32, 0); +#endif + } + + TEST(Utils, read_from_ptr_const_eval_c_array) + { + g_headless = true; // Disable exception popups + + constexpr u8 arr[sizeof(u64)] { 1, 2, 3, 4, 5, 6, 7, 8 }; + + static_assert(read_from_ptr(arr, 0) == u8(1)); + static_assert(read_from_ptr(arr, 1) == u8(2)); + static_assert(read_from_ptr(arr, 2) == u8(3)); + static_assert(read_from_ptr(arr, 3) == u8(4)); + static_assert(read_from_ptr(arr, 4) == u8(5)); + static_assert(read_from_ptr(arr, 5) == u8(6)); + static_assert(read_from_ptr(arr, 6) == u8(7)); + static_assert(read_from_ptr(arr, 7) == u8(8)); + + static_assert(read_from_ptr>(arr, 0) == u16(0x0201)); + static_assert(read_from_ptr>(arr, 2) == u16(0x0403)); + static_assert(read_from_ptr>(arr, 4) == u16(0x0605)); + static_assert(read_from_ptr>(arr, 6) == u16(0x0807)); + + static_assert(read_from_ptr>(arr, 0) == u32(0x04030201)); + static_assert(read_from_ptr>(arr, 4) == u32(0x08070605)); + + static_assert(read_from_ptr>(arr, 0) == u64(0x0807060504030201)); + + constexpr u32 arr32[sizeof(u64) / sizeof(u32)] { 0x00000000, 0xFFFFFFFF }; + + static_assert(read_from_ptr(arr32, 0) == u32(0x00000000)); + static_assert(read_from_ptr(arr32, 1) == u32(0xFFFFFFFF)); + + static_assert(read_from_ptr>(arr32, 0) == u64(0xFFFFFFFF00000000)); + + // This should not compile +#if CHECK_COMPILATION_ERRORS + constexpr auto v8 = read_from_ptr(arr, std::size(arr)); + constexpr auto v16 = read_from_ptr(arr, std::size(arr) + 1 - sizeof(u16)); + constexpr auto v32 = read_from_ptr(arr, std::size(arr) + 1 - sizeof(u32)); + constexpr auto v64 = read_from_ptr(arr, std::size(arr) + 1 - sizeof(u64)); + constexpr auto v128 = read_from_ptr(arr, 0); + + constexpr auto v32_32 = read_from_ptr(arr32, std::size(arr32)); + constexpr auto v64_32 = read_from_ptr(arr32, std::size(arr32) + 1 - sizeof(u64) / sizeof(u32)); + constexpr auto v128_32 = read_from_ptr(arr32, 0); +#endif + } +} diff --git a/rpcs3/util/types.hpp b/rpcs3/util/types.hpp index a1a07bac69..d9be10232c 100644 --- a/rpcs3/util/types.hpp +++ b/rpcs3/util/types.hpp @@ -1189,11 +1189,27 @@ constexpr T read_from_ptr(U&& array, usz pos = 0) { // TODO: ensure array element types are trivial static_assert(sizeof(T) % sizeof(array[0]) == 0); - std::decay_t buf[sizeof(T) / sizeof(array[0])]; + constexpr usz elements_per_value = sizeof(T) / sizeof(array[0]); + + std::decay_t buf[elements_per_value]; + if (!std::is_constant_evaluated()) + { + if constexpr (requires { std::size(array); }) + { + ensure((pos + elements_per_value) <= std::size(array)); + } + std::memcpy(+buf, &array[pos], sizeof(buf)); + } else - for (usz i = 0; i < pos; buf[i] = array[pos + i], i++); + { + // We could add an ensure or static_assert for OOB, but lucky for us, the [] operator will not compile with OOB. + for (usz i = 0; i < elements_per_value; i++) + { + buf[i] = array[pos + i]; + } + } return std::bit_cast(buf); } @@ -1201,20 +1217,42 @@ template constexpr void write_to_ptr(U&& array, usz pos, const T& value) { static_assert(sizeof(T) % sizeof(array[0]) == 0); + constexpr usz elements_per_value = sizeof(T) / sizeof(array[0]); + + if constexpr (requires { std::size(array); }) + { + ensure((pos + elements_per_value) <= std::size(array)); + } + if (!std::is_constant_evaluated()) + { std::memcpy(static_cast(&array[pos]), &value, sizeof(value)); + } else + { ensure(!"Unimplemented"); + } } template constexpr void write_to_ptr(U&& array, const T& value) { static_assert(sizeof(T) % sizeof(array[0]) == 0); + constexpr usz elements_per_value = sizeof(T) / sizeof(array[0]); + + if constexpr (requires { std::size(array); }) + { + ensure(elements_per_value <= std::size(array)); + } + if (!std::is_constant_evaluated()) + { std::memcpy(static_cast(&array[0]), &value, sizeof(value)); + } else + { ensure(!"Unimplemented"); + } } constexpr struct aref_tag_t{} aref_tag{};