Files
AnyPS5/core/libs/tests/GuestNpCppWebApiString.cpp

192 lines
7.3 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
extern "C" {
void APS5_VABI _ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(void* self, void* libContext);
void APS5_VABI _ZN3sce2Np9CppWebApi6Common6StringD1Ev(void* self);
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6String6appendEPKc(void* self, const char* text);
const char* APS5_VABI _ZNK3sce2Np9CppWebApi6Common6String5c_strEv(const void* self);
void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEEC1EPNS2_10LibContextE(void* self, void* libContext);
void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEED1Ev(void* self);
int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEE8pushBackERKS4_(void* self, const void* element);
}
namespace {
void Require(bool value) { if (!value) std::abort(); }
constexpr std::size_t kStringSize = 0x20;
constexpr std::size_t kGuardSize = 0x10;
constexpr std::size_t kReferenceCountOffset = 0x00;
constexpr std::size_t kBufferOffset = 0x08;
constexpr std::size_t kBufferSizeOffset = 0x10;
constexpr std::size_t kLibContextOffset = 0x18;
constexpr unsigned char kFill = 0xA5;
constexpr std::size_t kVectorSize = 0x18;
constexpr std::size_t kObjectOffset = 0x00;
constexpr std::size_t kBodyMagicOffset = 0x00;
constexpr std::size_t kBodyReferenceCountOffset = 0x30;
constexpr std::size_t kBodyElementKindOffset = 0x34;
constexpr std::size_t kBodyElementsOffset = 0x38;
constexpr std::size_t kBodySizeOffset = 0x40;
struct Storage {
alignas(8) unsigned char bytes[kStringSize + kGuardSize];
};
struct VectorStorage {
alignas(8) unsigned char bytes[kVectorSize + kGuardSize];
};
template <typename TValue>
TValue Load(const Storage& storage, std::size_t offset) {
TValue value;
std::memcpy(&value, storage.bytes + offset, sizeof(value));
return value;
}
template <typename TValue>
void Store(Storage& storage, std::size_t offset, TValue value) {
std::memcpy(storage.bytes + offset, &value, sizeof(value));
}
template <typename TValue>
TValue LoadBody(const void* body, std::size_t offset) {
TValue value;
std::memcpy(&value, static_cast<const unsigned char*>(body) + offset, sizeof(value));
return value;
}
bool GuardIntact(const Storage& storage) {
for (std::size_t i = kStringSize; i < sizeof(storage.bytes); i++) {
if (storage.bytes[i] != kFill) return false;
}
return true;
}
bool VectorGuardIntact(const VectorStorage& storage) {
for (std::size_t i = kVectorSize; i < sizeof(storage.bytes); i++) {
if (storage.bytes[i] != kFill) return false;
}
return true;
}
void TestConstructedStringIsEmpty() {
std::uint64_t libContext = 1;
Storage storage;
std::memset(storage.bytes, kFill, sizeof(storage.bytes));
_ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, &libContext);
Require(Load<std::int32_t>(storage, kReferenceCountOffset) == 0);
Require(Load<char*>(storage, kBufferOffset) == nullptr);
Require(Load<std::uint64_t>(storage, kBufferSizeOffset) == 0);
Require(Load<void*>(storage, kLibContextOffset) == &libContext);
Require(GuardIntact(storage));
const char* text = _ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes);
Require(text != nullptr && text[0] == '\0');
_ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes);
Require(Load<char*>(storage, kBufferOffset) == nullptr);
Require(Load<std::uint64_t>(storage, kBufferSizeOffset) == 0);
Require(GuardIntact(storage));
}
void TestNullLibContextIsStored() {
Storage storage;
std::memset(storage.bytes, kFill, sizeof(storage.bytes));
_ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, nullptr);
Require(Load<void*>(storage, kLibContextOffset) == nullptr);
Require(Load<char*>(storage, kBufferOffset) == nullptr);
Require(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes)[0] == '\0');
_ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes);
}
void TestAppendBuildsAndClearsBuffer() {
std::uint64_t libContext = 1;
Storage storage;
std::memset(storage.bytes, kFill, sizeof(storage.bytes));
_ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, &libContext);
Require(_ZN3sce2Np9CppWebApi6Common6String6appendEPKc(storage.bytes, "rank") == 0);
Require(std::strcmp(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes), "rank") == 0);
Require(_ZN3sce2Np9CppWebApi6Common6String6appendEPKc(storage.bytes, "ing") == 0);
Require(std::strcmp(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes), "ranking") == 0);
Require(Load<char*>(storage, kBufferOffset) != nullptr);
Require(GuardIntact(storage));
_ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes);
Require(Load<char*>(storage, kBufferOffset) == nullptr);
Require(Load<std::uint64_t>(storage, kBufferSizeOffset) == 0);
Require(GuardIntact(storage));
}
void TestAppendNullKeepsEmpty() {
std::uint64_t libContext = 1;
Storage storage;
std::memset(storage.bytes, kFill, sizeof(storage.bytes));
_ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(storage.bytes, &libContext);
Require(_ZN3sce2Np9CppWebApi6Common6String6appendEPKc(storage.bytes, nullptr) == 0);
Require(Load<char*>(storage, kBufferOffset) == nullptr);
Require(_ZNK3sce2Np9CppWebApi6Common6String5c_strEv(storage.bytes)[0] == '\0');
_ZN3sce2Np9CppWebApi6Common6StringD1Ev(storage.bytes);
}
void TestVectorPushBackCopiesStringContent() {
std::uint64_t libContext = 1;
VectorStorage vector;
Storage element;
std::memset(vector.bytes, kFill, sizeof(vector.bytes));
std::memset(element.bytes, kFill, sizeof(element.bytes));
_ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEEC1EPNS2_10LibContextE(vector.bytes, &libContext);
Require(VectorGuardIntact(vector));
_ZN3sce2Np9CppWebApi6Common6StringC1EPNS2_10LibContextE(element.bytes, &libContext);
Require(_ZN3sce2Np9CppWebApi6Common6String6appendEPKc(element.bytes, "privilege") == 0);
char* elementBuffer = Load<char*>(element, kBufferOffset);
Require(elementBuffer != nullptr);
Require(_ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEE8pushBackERKS4_(vector.bytes, element.bytes) == 0);
void* body;
std::memcpy(&body, vector.bytes + kObjectOffset, sizeof(body));
Require(body != nullptr);
Require(LoadBody<std::int32_t>(body, kBodyReferenceCountOffset) == 1);
Require(LoadBody<std::int32_t>(body, kBodyElementKindOffset) == 2);
Require(LoadBody<std::uint64_t>(body, kBodySizeOffset) == 1);
void* elements = LoadBody<void*>(body, kBodyElementsOffset);
char* storedBuffer;
std::memcpy(&storedBuffer, static_cast<unsigned char*>(elements) + kBufferOffset, sizeof(storedBuffer));
Require(storedBuffer != nullptr);
Require(storedBuffer != elementBuffer);
Require(std::strcmp(storedBuffer, "privilege") == 0);
_ZN3sce2Np9CppWebApi6Common6StringD1Ev(element.bytes);
Require(std::strcmp(storedBuffer, "privilege") == 0);
_ZN3sce2Np9CppWebApi6Common6VectorINS2_6StringEED1Ev(vector.bytes);
Require(VectorGuardIntact(vector));
}
}
int main() {
TestConstructedStringIsEmpty();
TestNullLibContextIsStored();
TestAppendBuildsAndClearsBuffer();
TestAppendNullKeepsEmpty();
TestVectorPushBackCopiesStringContent();
}