#include "prx/libc/include/Api.hpp" #include #include #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #else #include #include #endif static void Check(bool condition, int line) { if (condition) return; std::fprintf(stderr, "find-last check failed at line %d\n", line); std::abort(); } #define Require(condition) Check((condition), __LINE__) class GuardedPage { unsigned char* allocation; public: std::size_t size; unsigned char* data; GuardedPage() { #ifdef _WIN32 SYSTEM_INFO info{}; GetSystemInfo(&info); size = info.dwPageSize; allocation = static_cast(VirtualAlloc(nullptr, size * 3, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS)); Require(allocation != nullptr); DWORD previous; Require(VirtualProtect(allocation + size, size, PAGE_READWRITE, &previous) != 0); #else const long pageSize = sysconf(_SC_PAGESIZE); Require(pageSize > 0); size = static_cast(pageSize); allocation = static_cast(mmap(nullptr, size * 3, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); Require(allocation != MAP_FAILED); Require(mprotect(allocation + size, size, PROT_READ | PROT_WRITE) == 0); #endif data = allocation + size; std::memset(data, 0xff, size); #ifdef _WIN32 Require(VirtualProtect(data, size, PAGE_READONLY, &previous) != 0); #else Require(mprotect(data, size, PROT_READ) == 0); #endif } GuardedPage(const GuardedPage&) = delete; GuardedPage& operator=(const GuardedPage&) = delete; ~GuardedPage() { #ifdef _WIN32 VirtualFree(allocation, 0, MEM_RELEASE); #else munmap(allocation, size * 3); #endif } }; template static void Invoke(TFunction function, const void* first, const void* last, TValue value, const void* expected) { *__error_nid_postfix() = 97; Require(function(first, last, value) == expected); Require(*__error_nid_postfix() == 97); } template static void Empty(TFunction function) { GuardedPage page; for (const void* address : {static_cast(nullptr), static_cast(page.data), static_cast(page.data - 1), static_cast(page.data + page.size)}) { for (const TValue value : {TValue{}, TValue{1}, std::numeric_limits::max()}) { Invoke(function, address, address, value, address); } } } template static void Positions(TFunction function) { std::array storage; const TValue zero{}, maximum = std::numeric_limits::max(); for (std::size_t alignment = 0; alignment < 32; ++alignment) { auto* first = storage.data() + alignment; for (std::size_t count = 1; count <= 129; ++count) { auto* last = first + count * sizeof(TValue); storage.fill(0xff); Invoke(function, first, last, zero, last); Invoke(function, first, last, maximum, last - sizeof(TValue)); for (std::size_t index = 0; index < count; ++index) { auto* match = first + index * sizeof(TValue); std::memcpy(match, &zero, sizeof(zero)); const auto before = storage; Invoke(function, first, last, zero, match); Require(storage == before); } Invoke(function, first, last, maximum, last); } } } template static void Values(TFunction function) { std::array storage{}; std::uint64_t state = 0xac431bd5708ef269ULL; const auto checkValue = [&](TValue value) { for (std::size_t alignment = 0; alignment < 32; ++alignment) { auto* first = storage.data() + alignment; auto* last = first + 64 * sizeof(TValue); const TValue other = static_cast(~value); for (std::size_t index = 0; index < 64; ++index) std::memcpy(first + index * sizeof(TValue), &other, sizeof(other)); Invoke(function, first, last, value, last); for (const std::size_t index : {std::size_t{0}, std::size_t{15}, std::size_t{31}, std::size_t{63}}) { auto* match = first + index * sizeof(TValue); std::memcpy(match, &value, sizeof(value)); const auto before = storage; Invoke(function, first, last, value, match); Require(storage == before); } } }; if constexpr (sizeof(TValue) <= 2) { for (std::uint32_t value = 0; value <= std::numeric_limits::max(); ++value) checkValue(static_cast(value)); } else { for (unsigned bit = 0; bit < sizeof(TValue) * 8; ++bit) { const auto value = static_cast(TValue{1} << bit); checkValue(value); checkValue(static_cast(~value)); } for (unsigned index = 0; index < 1024; ++index) { state ^= state << 13; state ^= state >> 7; state ^= state << 17; checkValue(static_cast(state)); } } } template static void GuardPages(TFunction function) { GuardedPage page; for (std::size_t count = 1; count <= page.size / sizeof(TValue); ++count) { for (const auto* first : {page.data, page.data + page.size - count * sizeof(TValue)}) { const auto* last = first + count * sizeof(TValue); Invoke(function, first, last, TValue{}, last); Invoke(function, first, last, std::numeric_limits::max(), last - sizeof(TValue)); } } } template static void Run(TFunction function, const char* mode) { if (std::strcmp(mode, "empty") == 0) Empty(function); else if (std::strcmp(mode, "positions") == 0) Positions(function); else if (std::strcmp(mode, "values") == 0) Values(function); else if (std::strcmp(mode, "guard_pages") == 0) GuardPages(function); else Require(false); } int main(int argc, char** argv) { Require(argc == 3); if (std::strcmp(argv[1], "1") == 0) Run(__std_find_last_trivial_1_nid_postfix, argv[2]); else if (std::strcmp(argv[1], "2") == 0) Run(__std_find_last_trivial_2_nid_postfix, argv[2]); else if (std::strcmp(argv[1], "4") == 0) Run(__std_find_last_trivial_4_nid_postfix, argv[2]); else if (std::strcmp(argv[1], "8") == 0) Run(__std_find_last_trivial_8_nid_postfix, argv[2]); else Require(false); }