#include "prx/libSceJson2/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include using namespace SceJson2; struct Parameter { void* allocator; void* context; std::size_t bufferSize; }; using AliasAllocateCallback = void* (APS5_VABI*)(std::size_t, void*); using AliasFreeCallback = void (APS5_VABI*)(void*, void*); using AliasNotifyCallback = void (APS5_VABI*)(int, std::size_t, void*); struct Callbacks { AliasAllocateCallback allocate; AliasFreeCallback free; AliasNotifyCallback notify; }; static_assert(sizeof(Value) == 32 && sizeof(Parameter) == 24 && sizeof(Pair) == 48); static void Require(bool valid, const char* message) { if (!valid) { std::fprintf(stderr, "%s\n", message); std::abort(); } } struct AliasTrace { std::map live; int budget = -1; bool once = false; std::size_t requests = 0; unsigned notifications = 0; }; static void* APS5_VABI AllocateAlias(std::size_t size, void* context) { auto& trace = *static_cast(context); ++trace.requests; if (trace.budget == 0) { if (trace.once) trace.budget = -1; return nullptr; } if (trace.budget > 0) --trace.budget; void* memory = std::malloc(size); Require(memory != nullptr, "Alias tracking allocator requires host memory"); std::memset(memory, 0xa5, size); trace.live.emplace(memory, size); return memory; } static void APS5_VABI FreeAlias(void* memory, void* context) { if (!memory) return; Require(static_cast(context)->live.erase(memory) == 1, "Alias destruction must free each allocation through its owning context exactly once"); std::free(memory); } static void APS5_VABI NotifyAlias(int error, std::size_t, void* context) { Require(static_cast(error) == 0x80848102, "Alias allocation failures must retain the Json2 error"); ++static_cast(context)->notifications; } static const String* StringOf(const Value& value) { return reinterpret_cast(value.payload); } static void CheckText(const Value& value, const char* expected) { Require(value.type == 5 && value.payload && std::strcmp(_ZNK3sce4Json6String5c_strEv(StringOf(value)), expected) == 0, "Value constructor variants must preserve string contents"); } int main() { AliasTrace trace; Callbacks callbacks{AllocateAlias, FreeAlias, NotifyAlias}; alignas(8) std::array settings; settings.fill(std::byte{0xa5}); auto* parameter = reinterpret_cast(settings.data()); _ZN3sce4Json17InitParameterRttiC2EPNS0_14AllocParamRttiEPvm(reinterpret_cast(parameter), &callbacks, &trace, 8192); Require(parameter->allocator && parameter->context == &trace && parameter->bufferSize == 8192, "RTTI constructor variant must retain its callbacks, context and buffer setting"); for (std::size_t i = sizeof(Parameter); i < settings.size(); ++i) Require(settings[i] == std::byte{0xa5}, "RTTI construction must preserve bytes beyond its parameter block"); std::array initializer; initializer.fill(0xa5); _ZN3sce4Json11InitializerC2Ev(initializer.data()); Require(initializer[0] == 0, "Initializer constructor variant must clear its active byte"); for (std::size_t i = 1; i < initializer.size(); ++i) Require(initializer[i] == 0xa5, "Initializer construction must preserve every adjacent byte"); Require(_ZN3sce4Json11Initializer10initializeEPKNS0_13InitParameterE(initializer.data(), parameter) == 0, "Initialize through the constructor variant and RTTI allocator"); const auto baseline = trace.live.size(); for (std::int32_t type = 0; type < 8; ++type) { Value value; std::memset(&value, 0xa5, sizeof(value)); _ZN3sce4Json5ValueC2ENS0_9ValueTypeE(&value, static_cast(type)); Require(value.type == type && !value.parent && !value.auxiliary && value.reserved == 0xa5a5a5a5, "Typed value constructor variant must retain its type and inactive metadata"); _ZN3sce4Json5ValueD2Ev(&value); Require(trace.live.size() == baseline, "Typed constructor/destructor variants must reclaim their handles and storage"); } Value value{}; _ZN3sce4Json5ValueC2Ev(&value); Require(value.type == 0, "Default value constructor variant must construct null"); _ZN3sce4Json5ValueD2Ev(&value); _ZN3sce4Json5ValueC2Eb(&value, true); Require(value.type == 1 && (value.payload & 255) == 1, "Boolean constructor variant must preserve its payload"); _ZN3sce4Json5ValueD2Ev(&value); _ZN3sce4Json5ValueC2El(&value, -77); Require(value.type == 2 && value.payload == std::bit_cast(std::int64_t{-77}), "Signed constructor variant must preserve all payload bits"); _ZN3sce4Json5ValueD2Ev(&value); _ZN3sce4Json5ValueC2Em(&value, UINT64_MAX); Require(value.type == 3 && value.payload == UINT64_MAX, "Unsigned constructor variant must preserve all payload bits"); _ZN3sce4Json5ValueD2Ev(&value); _ZN3sce4Json5ValueC2Ed(&value, 1.25); Require(value.type == 4 && value.payload == std::bit_cast(1.25), "Real constructor variant must preserve its payload"); _ZN3sce4Json5ValueD2Ev(&value); String empty{}, text{}, copiedText{}; _ZN3sce4Json6StringC2Ev(&empty); Require(empty.text && !*_ZNK3sce4Json6String5c_strEv(&empty), "Default string constructor variant must own empty storage"); _ZN3sce4Json6StringC2EPKc(&text, "abc"); _ZN3sce4Json6StringC2ERKS1_(&copiedText, &text); Require(text.text != copiedText.text && std::strcmp(_ZNK3sce4Json6String5c_strEv(&copiedText), "abc") == 0, "String copy constructor variant must own independent storage"); _ZN3sce4Json5ValueC2EPKc(&value, "abc"); CheckText(value, "abc"); Value copiedValue{}; _ZN3sce4Json5ValueC2ERKS1_(&copiedValue, &value); CheckText(copiedValue, "abc"); Require(copiedValue.payload != value.payload, "Value copy constructor variant must own an independent handle"); _ZN3sce4Json5ValueD2Ev(&copiedValue); _ZN3sce4Json5ValueD2Ev(&value); _ZN3sce4Json5ValueC2ERKNS0_6StringE(&value, &text); CheckText(value, "abc"); Require(StringOf(value)->text != text.text, "String-to-value constructor variant must copy storage"); _ZN3sce4Json5ValueD2Ev(&value); Array array{}, copiedArray{}; _ZN3sce4Json5ArrayC2Ev(&array); _ZN3sce4Json5ValueC2El(&value, 42); _ZN3sce4Json5Array9push_backERKNS0_5ValueE(&array, &value); _ZN3sce4Json5ArrayC2ERKS1_(&copiedArray, &array); Require(array.items != copiedArray.items && _ZNK3sce4Json5Array4sizeEv(&copiedArray) == 1 && _ZNK3sce4Json5Array5frontEv(&copiedArray)->payload == 42, "Array copy constructor variant must copy its entries into independent storage"); Value wrapped{}; _ZN3sce4Json5ValueC2ERKNS0_5ArrayE(&wrapped, &array); const auto* child = _ZNK3sce4Json5Array5frontEv(reinterpret_cast(wrapped.payload)); Require(wrapped.type == 6 && child->parent == &wrapped && child->payload == 42, "Array-to-value constructor variant must bind copied children to their owner"); _ZN3sce4Json5ValueD2Ev(&wrapped); Object object{}, copiedObject{}; _ZN3sce4Json6ObjectC2Ev(&object); _ZN3sce4Json5Value3setEl(_ZN3sce4Json6ObjectixERKNS0_6StringE(&object, &text), 42); _ZN3sce4Json6ObjectC2ERKS1_(&copiedObject, &object); Require(object.items != copiedObject.items && _ZNK3sce4Json6Object4sizeEv(&copiedObject) == 1, "Object copy constructor variant must own independent tree storage"); _ZN3sce4Json5ValueC2ERKNS0_6ObjectE(&wrapped, &object); child = _ZNK3sce4Json5ValueixEPKc(&wrapped, "abc"); Require(wrapped.type == 7 && child->parent == &wrapped && child->payload == 42, "Object-to-value constructor variant must bind copied members to their owner"); _ZN3sce4Json5ValueD2Ev(&wrapped); Pair pair{}; _ZN3sce4Json6Object4PairC2Ev(&pair); Require(pair.key.text && pair.value.type == 0, "Default pair constructor variant must construct its key and null value"); _ZN3sce4Json6Object4PairD2Ev(&pair); _ZN3sce4Json6Object4PairC2ERKNS0_6StringERKNS0_5ValueE(&pair, &text, &value); Require(pair.key.text != text.text && pair.value.type == 2 && pair.value.payload == 42, "Pair constructor variant must copy its key and value"); _ZN3sce4Json6Object4PairD2Ev(&pair); ArrayIterator iterator{}, copy{}; _ZN3sce4Json5Array8iteratorC2Ev(&iterator); _ZN3sce4Json5Array8iteratorC2ERKS2_(©, &iterator); Require(iterator.state && copy.state && iterator.state != copy.state && !*reinterpret_cast(copy.state), "Default array iterator variants must own independent unbound states"); _ZN3sce4Json5Array8iteratorD2Ev(©); _ZN3sce4Json5Array8iteratorD2Ev(&iterator); _ZNK3sce4Json5Array5beginEv(&iterator, &array); _ZN3sce4Json5Array8iteratorC2ERKS2_(©, &iterator); Require(copy.state != iterator.state && _ZNK3sce4Json5Array8iteratordeEv(©) == _ZNK3sce4Json5Array5frontEv(&array), "Array iterator copy variant must preserve the actual element identity"); _ZN3sce4Json5Array8iteratorD2Ev(©); _ZN3sce4Json5Array8iteratorD2Ev(&iterator); ObjectIterator objectIterator{}, objectCopy{}; _ZN3sce4Json6Object8iteratorC2Ev(&objectIterator); _ZN3sce4Json6Object8iteratorC2ERKS2_(&objectCopy, &objectIterator); Require(objectIterator.state && objectCopy.state && objectIterator.state != objectCopy.state && !*reinterpret_cast(objectCopy.state), "Default object objectIterator variants must own independent unbound states"); _ZN3sce4Json6Object8iteratorD2Ev(&objectCopy); _ZN3sce4Json6Object8iteratorD2Ev(&objectIterator); _ZNK3sce4Json6Object5beginEv(&objectIterator, &object); _ZN3sce4Json6Object8iteratorC2ERKS2_(&objectCopy, &objectIterator); Require(objectCopy.state != objectIterator.state && _ZNK3sce4Json6Object8iteratordeEv(&objectCopy) == _ZNK3sce4Json6Object8iteratordeEv(&objectIterator), "Object objectIterator objectCopy variant must preserve the actual member identity"); _ZN3sce4Json6Object8iteratorD2Ev(&objectCopy); _ZN3sce4Json6Object8iteratorD2Ev(&objectIterator); _ZN3sce4Json6ObjectD2Ev(&copiedObject); _ZN3sce4Json6ObjectD2Ev(&object); _ZN3sce4Json5ArrayD2Ev(&copiedArray); _ZN3sce4Json5ArrayD2Ev(&array); _ZN3sce4Json5ValueD2Ev(&value); _ZN3sce4Json6StringD2Ev(&copiedText); _ZN3sce4Json6StringD2Ev(&text); _ZN3sce4Json6StringD2Ev(&empty); Require(trace.live.size() == baseline, "All constructor/destructor variants must reclaim their owned resources"); String absent{}; const auto requests = trace.requests; _ZN3sce4Json5ValueC2ERKNS0_6StringE(&value, &absent); Require(value.type == 0 && trace.requests == requests, "Missing string storage must leave a null value without allocating"); _ZN3sce4Json5ValueD2Ev(&value); _ZN3sce4Json5ValueC2EPKc(&value, nullptr); CheckText(value, ""); _ZN3sce4Json5ValueD2Ev(&value); struct Failure { bool once; int budget; int type; unsigned notifications; std::size_t requests; const char* text; }; const Failure failures[]{ {false, 0, 0, 1, 1, ""}, {false, 1, 0, 2, 3, ""}, {false, 2, 0, 1, 3, ""}, {false, 3, 0, 1, 4, ""}, {false, 4, 5, 1, 5, ""}, {false, 5, 5, 0, 5, "abc"}, {true, 0, 0, 1, 1, ""}, {true, 1, 5, 1, 4, ""}, {true, 2, 0, 1, 3, ""}, {true, 3, 0, 1, 4, ""}, {true, 4, 5, 1, 5, ""}, {true, 5, 5, 0, 5, "abc"}, }; for (const auto& failure : failures) { const auto notifications = trace.notifications; const auto before = trace.requests; trace.budget = failure.budget; trace.once = failure.once; _ZN3sce4Json5ValueC2EPKc(&value, "abc"); trace.budget = -1; trace.once = false; Require(value.type == failure.type && trace.notifications - notifications == failure.notifications && trace.requests - before == failure.requests, "Text constructor variant must preserve temporary string allocation and failure admission"); if (value.type == 5) CheckText(value, failure.text); _ZN3sce4Json5ValueD2Ev(&value); Require(trace.live.size() == baseline, "Failed constructor variants must reclaim their admitted prefix"); } _ZN3sce4Json11InitializerD2Ev(initializer.data()); Require(initializer[0] == 0 && trace.live.empty(), "Initializer destructor variant must terminate its resources"); for (std::size_t i = 1; i < initializer.size(); ++i) Require(initializer[i] == 0xa5, "Initializer destruction must preserve every adjacent byte"); return 0; }