Fix consteval read_from_ptr and add sanity checks if possible

This commit is contained in:
Megamouse 2026-06-28 19:57:16 +02:00 committed by Elad
parent d63273c473
commit 635aac8ff3
5 changed files with 530 additions and 3 deletions

View File

@ -193,6 +193,7 @@ if(BUILD_RPCS3_TESTS)
tests/test_rsx_fp_asm.cpp tests/test_rsx_fp_asm.cpp
tests/test_dmux_pamf.cpp tests/test_dmux_pamf.cpp
tests/test_spu_analyser.cpp tests/test_spu_analyser.cpp
tests/test_types_util.cpp
) )
target_link_libraries(rpcs3_test target_link_libraries(rpcs3_test

View File

@ -56,7 +56,7 @@ private:
#endif #endif
#endif #endif
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>
#ifdef _MSC_VER #if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop) #pragma warning(pop)
#else #else
#pragma GCC diagnostic pop #pragma GCC diagnostic pop

View File

@ -105,6 +105,7 @@
<ClCompile Include="test_sys_fs.cpp" /> <ClCompile Include="test_sys_fs.cpp" />
<ClCompile Include="test_tuple.cpp" /> <ClCompile Include="test_tuple.cpp" />
<ClCompile Include="test_pair.cpp" /> <ClCompile Include="test_pair.cpp" />
<ClCompile Include="test_types_util.cpp" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" Condition="'$(GTestInstalled)' == 'true'"> <ImportGroup Label="ExtensionTargets" Condition="'$(GTestInstalled)' == 'true'">

View File

@ -0,0 +1,487 @@
#include "stdafx.h"
#include <gtest/gtest.h>
extern atomic_t<bool> 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<u8, sizeof(u64)> arr{};
write_to_ptr(arr, u8(umax));
EXPECT_EQ(arr.at(0), u8(umax));
write_to_ptr(arr, u16(umax));
EXPECT_EQ(*utils::bless<u16>(arr.data()), u16(umax));
write_to_ptr(arr, u32(umax));
EXPECT_EQ(*utils::bless<u32>(arr.data()), u32(umax));
write_to_ptr(arr, u64(umax));
EXPECT_EQ(*utils::bless<u64>(arr.data()), u64(umax));
// write_to_ptr and read_from_ptr with pos
std::array<u8, sizeof(u128)> 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<u8>(arr2, i), i);
}
arr2 = {};
for (u16 i = 0; i < arr2.size(); i += sizeof(u16))
{
write_to_ptr(arr2, i, i);
EXPECT_EQ(*utils::bless<u16>(arr2.data() + i), i);
EXPECT_EQ(read_from_ptr<u16>(arr2, i), i);
}
arr2 = {};
for (u32 i = 0; i < arr2.size(); i += sizeof(u32))
{
write_to_ptr(arr2, i, i);
EXPECT_EQ(*utils::bless<u32>(arr2.data() + i), i);
EXPECT_EQ(read_from_ptr<u32>(arr2, i), i);
}
arr2 = {};
for (u64 i = 0; i < arr2.size(); i += sizeof(u64))
{
write_to_ptr(arr2, i, i);
EXPECT_EQ(*utils::bless<u64>(arr2.data() + i), i);
EXPECT_EQ(read_from_ptr<u64>(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<u128>(arr2, i);
EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0);
}
// write_to_ptr without pos
std::array<u32, sizeof(u64) / sizeof(u32)> arr32{};
write_to_ptr(arr32, u32(umax));
EXPECT_EQ(*utils::bless<u32>(arr32.data()), u32(umax));
write_to_ptr(arr32, u64(umax));
EXPECT_EQ(*utils::bless<u64>(arr32.data()), u64(umax));
// write_to_ptr and read_from_ptr with pos
std::array<u32, sizeof(u128) / sizeof(u32)> 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<u32>(arr32_2.data() + index), i);
EXPECT_EQ(read_from_ptr<u32>(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<u64>(arr32_2.data() + index), i);
EXPECT_EQ(read_from_ptr<u64>(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<u128>(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<u8>(arr2, arr2.size()), ".*");
EXPECT_DEATH(read_from_ptr<u16>(arr2, arr2.size()), ".*");
EXPECT_DEATH(read_from_ptr<u32>(arr2, arr2.size()), ".*");
EXPECT_DEATH(read_from_ptr<u64>(arr2, arr2.size()), ".*");
EXPECT_DEATH(read_from_ptr<u128>(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<u32>(arr32_2, arr32_2.size()), ".*");
EXPECT_DEATH(read_from_ptr<u64>(arr32_2, arr32_2.size()), ".*");
EXPECT_DEATH(read_from_ptr<u128>(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<u16>(arr + 0), u16(umax));
write_to_ptr(arr, u32(umax));
EXPECT_EQ(*utils::bless<u32>(arr + 0), u32(umax));
write_to_ptr(arr, u64(umax));
EXPECT_EQ(*utils::bless<u64>(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<u8>(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<u16>(arr2 + i), i);
EXPECT_EQ(read_from_ptr<u16>(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<u32>(arr2 + i), i);
EXPECT_EQ(read_from_ptr<u32>(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<u64>(arr2 + i), i);
EXPECT_EQ(read_from_ptr<u64>(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<u128>(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<u32>(arr32 + 0), u32(umax));
write_to_ptr(arr32, u64(umax));
EXPECT_EQ(*utils::bless<u64>(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<u32>(arr32_2 + index), i);
EXPECT_EQ(read_from_ptr<u32>(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<u64>(arr32_2 + index), i);
EXPECT_EQ(read_from_ptr<u64>(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<u128>(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<u8>(arr2, std::size(arr2)), ".*");
EXPECT_DEATH(read_from_ptr<u16>(arr2, std::size(arr2)), ".*");
EXPECT_DEATH(read_from_ptr<u32>(arr2, std::size(arr2)), ".*");
EXPECT_DEATH(read_from_ptr<u64>(arr2, std::size(arr2)), ".*");
EXPECT_DEATH(read_from_ptr<u128>(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<u32>(arr32_2, std::size(arr32_2)), ".*");
EXPECT_DEATH(read_from_ptr<u64>(arr32_2, std::size(arr32_2)), ".*");
EXPECT_DEATH(read_from_ptr<u128>(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<u16>(arr), u16(umax));
write_to_ptr(arr, u32(umax));
EXPECT_EQ(*utils::bless<u32>(arr), u32(umax));
write_to_ptr(arr, u64(umax));
EXPECT_EQ(*utils::bless<u64>(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<u8>(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<u16>(arr2 + i), i);
EXPECT_EQ(read_from_ptr<u16>(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<u32>(arr2 + i), i);
EXPECT_EQ(read_from_ptr<u32>(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<u64>(arr2 + i), i);
EXPECT_EQ(read_from_ptr<u64>(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<u128>(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<u32>(arr32), u32(umax));
write_to_ptr(arr32, u64(umax));
EXPECT_EQ(*utils::bless<u64>(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<u32>(arr32_2 + index), i);
EXPECT_EQ(read_from_ptr<u32>(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<u64>(arr32_2 + index), i);
EXPECT_EQ(read_from_ptr<u64>(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<u128>(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<u8>(arr2, arr2_size), ".*");
EXPECT_DEATH(read_from_ptr<u16>(arr2, arr2_size), ".*");
EXPECT_DEATH(read_from_ptr<u32>(arr2, arr2_size), ".*");
EXPECT_DEATH(read_from_ptr<u64>(arr2, arr2_size), ".*");
EXPECT_DEATH(read_from_ptr<u128>(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<u32>(arr32_2, arr32_count), ".*");
EXPECT_DEATH(read_from_ptr<u64>(arr32_2, arr32_count), ".*");
EXPECT_DEATH(read_from_ptr<u128>(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<u8, sizeof(u64)> arr { 1, 2, 3, 4, 5, 6, 7, 8 };
static_assert(read_from_ptr<u8>(arr, 0) == u8(1));
static_assert(read_from_ptr<u8>(arr, 1) == u8(2));
static_assert(read_from_ptr<u8>(arr, 2) == u8(3));
static_assert(read_from_ptr<u8>(arr, 3) == u8(4));
static_assert(read_from_ptr<u8>(arr, 4) == u8(5));
static_assert(read_from_ptr<u8>(arr, 5) == u8(6));
static_assert(read_from_ptr<u8>(arr, 6) == u8(7));
static_assert(read_from_ptr<u8>(arr, 7) == u8(8));
static_assert(read_from_ptr<le_t<u16>>(arr, 0) == u16(0x0201));
static_assert(read_from_ptr<le_t<u16>>(arr, 2) == u16(0x0403));
static_assert(read_from_ptr<le_t<u16>>(arr, 4) == u16(0x0605));
static_assert(read_from_ptr<le_t<u16>>(arr, 6) == u16(0x0807));
static_assert(read_from_ptr<le_t<u32>>(arr, 0) == u32(0x04030201));
static_assert(read_from_ptr<le_t<u32>>(arr, 4) == u32(0x08070605));
static_assert(read_from_ptr<le_t<u64>>(arr, 0) == u64(0x0807060504030201));
constexpr std::array<u32, sizeof(u64) / sizeof(u32)> arr32 { 0x00000000, 0xFFFFFFFF };
static_assert(read_from_ptr<u32>(arr32, 0) == u32(0x00000000));
static_assert(read_from_ptr<u32>(arr32, 1) == u32(0xFFFFFFFF));
static_assert(read_from_ptr<le_t<u64>>(arr32, 0) == u64(0xFFFFFFFF00000000));
// This should not compile
#if CHECK_COMPILATION_ERRORS
constexpr auto v8 = read_from_ptr<u8>(arr, arr.size());
constexpr auto v16 = read_from_ptr<u16>(arr, arr.size() + 1 - sizeof(u16));
constexpr auto v32 = read_from_ptr<u32>(arr, arr.size() + 1 - sizeof(u32));
constexpr auto v64 = read_from_ptr<u64>(arr, arr.size() + 1 - sizeof(u64));
constexpr auto v128 = read_from_ptr<u128>(arr, 0);
constexpr auto v32_32 = read_from_ptr<u32>(arr32, arr32.size());
constexpr auto v64_32 = read_from_ptr<u64>(arr32, arr32.size() + 1 - sizeof(u64) / sizeof(u32));
constexpr auto v128_32 = read_from_ptr<u128>(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<u8>(arr, 0) == u8(1));
static_assert(read_from_ptr<u8>(arr, 1) == u8(2));
static_assert(read_from_ptr<u8>(arr, 2) == u8(3));
static_assert(read_from_ptr<u8>(arr, 3) == u8(4));
static_assert(read_from_ptr<u8>(arr, 4) == u8(5));
static_assert(read_from_ptr<u8>(arr, 5) == u8(6));
static_assert(read_from_ptr<u8>(arr, 6) == u8(7));
static_assert(read_from_ptr<u8>(arr, 7) == u8(8));
static_assert(read_from_ptr<le_t<u16>>(arr, 0) == u16(0x0201));
static_assert(read_from_ptr<le_t<u16>>(arr, 2) == u16(0x0403));
static_assert(read_from_ptr<le_t<u16>>(arr, 4) == u16(0x0605));
static_assert(read_from_ptr<le_t<u16>>(arr, 6) == u16(0x0807));
static_assert(read_from_ptr<le_t<u32>>(arr, 0) == u32(0x04030201));
static_assert(read_from_ptr<le_t<u32>>(arr, 4) == u32(0x08070605));
static_assert(read_from_ptr<le_t<u64>>(arr, 0) == u64(0x0807060504030201));
constexpr u32 arr32[sizeof(u64) / sizeof(u32)] { 0x00000000, 0xFFFFFFFF };
static_assert(read_from_ptr<u32>(arr32, 0) == u32(0x00000000));
static_assert(read_from_ptr<u32>(arr32, 1) == u32(0xFFFFFFFF));
static_assert(read_from_ptr<le_t<u64>>(arr32, 0) == u64(0xFFFFFFFF00000000));
// This should not compile
#if CHECK_COMPILATION_ERRORS
constexpr auto v8 = read_from_ptr<u8>(arr, std::size(arr));
constexpr auto v16 = read_from_ptr<u16>(arr, std::size(arr) + 1 - sizeof(u16));
constexpr auto v32 = read_from_ptr<u32>(arr, std::size(arr) + 1 - sizeof(u32));
constexpr auto v64 = read_from_ptr<u64>(arr, std::size(arr) + 1 - sizeof(u64));
constexpr auto v128 = read_from_ptr<u128>(arr, 0);
constexpr auto v32_32 = read_from_ptr<u32>(arr32, std::size(arr32));
constexpr auto v64_32 = read_from_ptr<u64>(arr32, std::size(arr32) + 1 - sizeof(u64) / sizeof(u32));
constexpr auto v128_32 = read_from_ptr<u128>(arr32, 0);
#endif
}
}

View File

@ -1189,11 +1189,27 @@ constexpr T read_from_ptr(U&& array, usz pos = 0)
{ {
// TODO: ensure array element types are trivial // TODO: ensure array element types are trivial
static_assert(sizeof(T) % sizeof(array[0]) == 0); static_assert(sizeof(T) % sizeof(array[0]) == 0);
std::decay_t<decltype(array[0])> buf[sizeof(T) / sizeof(array[0])]; constexpr usz elements_per_value = sizeof(T) / sizeof(array[0]);
std::decay_t<decltype(array[0])> buf[elements_per_value];
if (!std::is_constant_evaluated()) 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)); std::memcpy(+buf, &array[pos], sizeof(buf));
}
else 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<T>(buf); return std::bit_cast<T>(buf);
} }
@ -1201,20 +1217,42 @@ template <typename T, typename U>
constexpr void write_to_ptr(U&& array, usz pos, const T& value) constexpr void write_to_ptr(U&& array, usz pos, const T& value)
{ {
static_assert(sizeof(T) % sizeof(array[0]) == 0); 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()) if (!std::is_constant_evaluated())
{
std::memcpy(static_cast<void*>(&array[pos]), &value, sizeof(value)); std::memcpy(static_cast<void*>(&array[pos]), &value, sizeof(value));
}
else else
{
ensure(!"Unimplemented"); ensure(!"Unimplemented");
}
} }
template <typename T, typename U> template <typename T, typename U>
constexpr void write_to_ptr(U&& array, const T& value) constexpr void write_to_ptr(U&& array, const T& value)
{ {
static_assert(sizeof(T) % sizeof(array[0]) == 0); 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()) if (!std::is_constant_evaluated())
{
std::memcpy(static_cast<void*>(&array[0]), &value, sizeof(value)); std::memcpy(static_cast<void*>(&array[0]), &value, sizeof(value));
}
else else
{
ensure(!"Unimplemented"); ensure(!"Unimplemented");
}
} }
constexpr struct aref_tag_t{} aref_tag{}; constexpr struct aref_tag_t{} aref_tag{};