AnyPS5/core/libs/tests/Mspace.cpp

217 lines
13 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <thread>
extern "C" {
int* APS5_VABI __error_nid_postfix();
void* APS5_VABI sceLibcMspaceCreate_nid_postfix(const char*, void*, std::size_t, unsigned);
int APS5_VABI sceLibcMspaceDestroy_nid_postfix(void*);
void* APS5_VABI sceLibcMspaceMalloc_nid_postfix(void*, std::size_t);
void* APS5_VABI sceLibcMspaceCalloc_nid_postfix(void*, std::size_t, std::size_t);
void* APS5_VABI sceLibcMspaceRealloc_nid_postfix(void*, void*, std::size_t);
void APS5_VABI sceLibcMspaceFree_nid_postfix(void*, void*);
int APS5_VABI sceLibcMspacePosixMemalign_nid_postfix(void*, void**, std::size_t, std::size_t);
std::size_t APS5_VABI sceLibcMspaceMallocUsableSize_nid_postfix(const void*);
void* APS5_VABI sceLibcMspaceMemalign_nid_postfix(void*, std::size_t, std::size_t);
void* APS5_VABI sceLibcMspaceReallocalign_nid_postfix(void*, void*, std::size_t, std::size_t);
int APS5_VABI sceLibcMspaceMallocStats_nid_postfix(void*, void*);
int APS5_VABI sceLibcMspaceMallocStatsFast_nid_postfix(void*, void*);
}
struct MallocManagedSize {
std::uint16_t size;
std::uint16_t version;
std::uint32_t reserved;
std::size_t maxSystemSize;
std::size_t currentSystemSize;
std::size_t maxInuseSize;
std::size_t currentInuseSize;
};
static void Require(bool value) {
if (!value) { std::fputs("Mspace check failed\n", stderr); std::abort(); }
}
static void CheckAlignment() {
alignas(32) static std::array<unsigned char, 65536 + 16> storage{};
void* arena = sceLibcMspaceCreate_nid_postfix("aligned", storage.data() + 16, 65536, 0);
Require(arena != nullptr);
const auto aligned = [](const void* pointer) { return pointer && (reinterpret_cast<std::uintptr_t>(pointer) & 31u) == 0; };
void* blocks[64] = {};
for (std::size_t index = 0; index < 64; ++index) {
blocks[index] = sceLibcMspaceMalloc_nid_postfix(arena, index + 1);
Require(aligned(blocks[index]));
std::memset(blocks[index], 0xa5, index + 1);
}
void* zeroed = sceLibcMspaceCalloc_nid_postfix(arena, 3, 13);
Require(aligned(zeroed));
blocks[0] = sceLibcMspaceRealloc_nid_postfix(arena, blocks[0], 600);
Require(aligned(blocks[0]));
void* small = sceLibcMspaceMemalign_nid_postfix(arena, 16, 40);
Require(aligned(small));
void* moved = sceLibcMspaceReallocalign_nid_postfix(arena, nullptr, 40, 8);
Require(aligned(moved));
void* posix = nullptr;
Require(sceLibcMspacePosixMemalign_nid_postfix(arena, &posix, 8, 40) == 0 && aligned(posix));
for (void* block : blocks) sceLibcMspaceFree_nid_postfix(arena, block);
for (void* block : {zeroed, small, moved, posix}) sceLibcMspaceFree_nid_postfix(arena, block);
Require(sceLibcMspaceDestroy_nid_postfix(arena) == 0);
}
static void CheckAdjacentBlocks() {
alignas(32) static std::array<unsigned char, 65536> storage{};
void* arena = sceLibcMspaceCreate_nid_postfix("adjacent", storage.data(), storage.size(), 0);
Require(arena != nullptr);
const auto end = [](void* block) { return static_cast<unsigned char*>(block) + sceLibcMspaceMallocUsableSize_nid_postfix(block); };
std::array<void*, 96> blocks{};
for (std::size_t index = 0; index < blocks.size(); ++index) {
blocks[index] = sceLibcMspaceMalloc_nid_postfix(arena, index % 48 + 1);
Require(blocks[index] != nullptr);
if (index > 0) Require(blocks[index] == end(blocks[index - 1]));
}
void* last = blocks.back();
Require(sceLibcMspaceRealloc_nid_postfix(arena, last, 80) == last);
void* after = sceLibcMspaceMalloc_nid_postfix(arena, 16);
Require(after == end(last));
MallocManagedSize stats{sizeof(MallocManagedSize), 1, 0, 0, 0, 0, 0};
Require(sceLibcMspaceMallocStats_nid_postfix(arena, &stats) == 0);
Require(stats.currentInuseSize == static_cast<std::size_t>(end(after) - static_cast<unsigned char*>(blocks[0])));
Require(sceLibcMspaceDestroy_nid_postfix(arena) == 0);
}
static void CheckMovedBlockKeepsUsableBytes() {
alignas(32) static std::array<unsigned char, 65536> storage{};
void* arena = sceLibcMspaceCreate_nid_postfix("moved", storage.data(), storage.size(), 0);
Require(arena != nullptr);
const auto fill = [](void* block) {
const auto usable = sceLibcMspaceMallocUsableSize_nid_postfix(block);
Require(usable > 1);
for (std::size_t index = 0; index < usable; ++index) static_cast<unsigned char*>(block)[index] = static_cast<unsigned char>(index + 1);
return usable;
};
const auto kept = [](const void* block, std::size_t usable) {
for (std::size_t index = 0; index < usable; ++index) if (static_cast<const unsigned char*>(block)[index] != static_cast<unsigned char>(index + 1)) return false;
return true;
};
void* block = sceLibcMspaceMalloc_nid_postfix(arena, 1);
const auto usable = fill(block);
Require(sceLibcMspaceMalloc_nid_postfix(arena, 1) != nullptr);
void* moved = sceLibcMspaceRealloc_nid_postfix(arena, block, 4096);
Require(moved != nullptr && moved != block && kept(moved, usable));
void* other = sceLibcMspaceMalloc_nid_postfix(arena, 1);
const auto otherUsable = fill(other);
Require(sceLibcMspaceMalloc_nid_postfix(arena, 1) != nullptr);
void* realigned = sceLibcMspaceReallocalign_nid_postfix(arena, other, 4096, 256);
Require(realigned != nullptr && realigned != other && kept(realigned, otherUsable));
Require(sceLibcMspaceDestroy_nid_postfix(arena) == 0);
}
int main() {
CheckAlignment();
CheckAdjacentBlocks();
CheckMovedBlockKeepsUsableBytes();
alignas(16) std::array<unsigned char, 65536> storage{};
void* arena = sceLibcMspaceCreate_nid_postfix("test", storage.data(), storage.size(), 0);
Require(arena != nullptr);
Require(sceLibcMspaceCreate_nid_postfix("overlap", storage.data(), storage.size(), 0) == nullptr);
auto* first = static_cast<unsigned char*>(sceLibcMspaceCalloc_nid_postfix(arena, 32, 4));
Require(first > storage.data() && first + 128 <= storage.data() + storage.size());
for (int i = 0; i < 128; ++i) { Require(first[i] == 0); first[i] = static_cast<unsigned char>(i); }
void* blocker = sceLibcMspaceMalloc_nid_postfix(arena, 128);
MallocManagedSize beforeOverflow{sizeof(MallocManagedSize), 1, 0, 0, 0, 0, 0};
Require(sceLibcMspaceMallocStats_nid_postfix(arena, &beforeOverflow) == 0);
for (std::size_t delta = 0; delta < 16; ++delta) {
const auto size = std::numeric_limits<std::size_t>::max() - delta;
*__error_nid_postfix() = 0;
Require(sceLibcMspaceRealloc_nid_postfix(arena, first, size) == nullptr);
Require(*__error_nid_postfix() == 12);
*__error_nid_postfix() = 0;
Require(sceLibcMspaceReallocalign_nid_postfix(arena, first, size, 16) == nullptr);
Require(*__error_nid_postfix() == 12);
Require(sceLibcMspaceMallocUsableSize_nid_postfix(first) == 128);
for (int i = 0; i < 128; ++i) Require(first[i] == i);
MallocManagedSize afterOverflow{sizeof(MallocManagedSize), 1, 0, 0, 0, 0, 0};
Require(sceLibcMspaceMallocStats_nid_postfix(arena, &afterOverflow) == 0);
Require(afterOverflow.currentInuseSize == beforeOverflow.currentInuseSize);
Require(afterOverflow.maxInuseSize == beforeOverflow.maxInuseSize);
}
auto* grown = static_cast<unsigned char*>(sceLibcMspaceRealloc_nid_postfix(arena, first, 4096));
Require(grown && grown != first);
for (int i = 0; i < 128; ++i) Require(grown[i] == i);
Require(sceLibcMspaceMallocUsableSize_nid_postfix(grown) >= 4096);
Require(sceLibcMspaceRealloc_nid_postfix(arena, grown, storage.size()) == nullptr);
Require(grown[127] == 127);
void* aligned = nullptr;
Require(sceLibcMspacePosixMemalign_nid_postfix(arena, &aligned, 4096, 1024) == 0);
Require((reinterpret_cast<std::uintptr_t>(aligned) & 4095) == 0);
void* unchanged = aligned;
Require(sceLibcMspacePosixMemalign_nid_postfix(arena, &unchanged, 3, 8) == 22 && unchanged == aligned);
Require(sceLibcMspaceCalloc_nid_postfix(arena, std::numeric_limits<std::size_t>::max(), 2) == nullptr);
sceLibcMspaceFree_nid_postfix(arena, blocker);
sceLibcMspaceFree_nid_postfix(arena, aligned);
Require(sceLibcMspaceRealloc_nid_postfix(arena, grown, 0) == nullptr);
void* large = sceLibcMspaceMalloc_nid_postfix(arena, storage.size() - 256);
Require(large != nullptr); // freeing coalesced all the fragmented blocks
sceLibcMspaceFree_nid_postfix(arena, large);
void* head = sceLibcMspaceMalloc_nid_postfix(arena, 64);
Require(head != nullptr && sceLibcMspaceRealloc_nid_postfix(arena, head, 1024) == head);
Require(sceLibcMspaceMallocUsableSize_nid_postfix(head) == 1024);
sceLibcMspaceFree_nid_postfix(arena, head);
std::array<void*, 2000> small{};
for (auto& pointer : small) {
pointer = sceLibcMspaceMalloc_nid_postfix(arena, 16);
Require(pointer != nullptr);
}
for (std::size_t i = 0; i < small.size(); i += 2) sceLibcMspaceFree_nid_postfix(arena, small[i]);
Require(sceLibcMspaceMalloc_nid_postfix(arena, storage.size() - 256) == nullptr);
for (std::size_t i = 1; i < small.size(); i += 2) sceLibcMspaceFree_nid_postfix(arena, small[i]);
large = sceLibcMspaceMalloc_nid_postfix(arena, storage.size() - 256);
Require(large != nullptr);
sceLibcMspaceFree_nid_postfix(arena, large);
std::array<std::thread, 4> workers;
for (auto& worker : workers) worker = std::thread([&] {
for (int i = 0; i < 1000; ++i) {
void* pointer = sceLibcMspaceMalloc_nid_postfix(arena, 97);
Require(pointer != nullptr);
std::memset(pointer, 42, 97);
Require(sceLibcMspaceMallocUsableSize_nid_postfix(pointer) >= 97);
sceLibcMspaceFree_nid_postfix(arena, pointer);
}
});
for (auto& worker : workers) worker.join();
MallocManagedSize stats{sizeof(MallocManagedSize), 1, 0, 0, 0, 0, 0};
Require(sceLibcMspaceMallocStats_nid_postfix(arena, &stats) == 0);
Require(stats.currentSystemSize == storage.size() && stats.maxSystemSize == storage.size());
Require(stats.currentInuseSize == 0 && stats.maxInuseSize >= storage.size() - 256);
MallocManagedSize shortStats{8, 1, 0, 0, 0, 0, 0};
Require(sceLibcMspaceMallocStatsFast_nid_postfix(arena, &shortStats) == 22);
void* memaligned = sceLibcMspaceMemalign_nid_postfix(arena, 256, 100);
Require(memaligned && (reinterpret_cast<std::uintptr_t>(memaligned) & 255) == 0);
Require(sceLibcMspaceMemalign_nid_postfix(arena, 24, 100) == nullptr);
unsigned char* realigned = static_cast<unsigned char*>(sceLibcMspaceReallocalign_nid_postfix(arena, nullptr, 64, 64));
Require(realigned && (reinterpret_cast<std::uintptr_t>(realigned) & 63) == 0);
for (int i = 0; i < 64; ++i) realigned[i] = static_cast<unsigned char>(i + 1);
unsigned char* regrown = static_cast<unsigned char*>(sceLibcMspaceReallocalign_nid_postfix(arena, realigned, 256, 64));
Require(regrown && (reinterpret_cast<std::uintptr_t>(regrown) & 63) == 0);
for (int i = 0; i < 64; ++i) Require(regrown[i] == static_cast<unsigned char>(i + 1));
Require(sceLibcMspaceReallocalign_nid_postfix(arena, regrown, 16, 0) == nullptr);
Require(sceLibcMspaceReallocalign_nid_postfix(arena, regrown, 16, 3) == nullptr);
Require(sceLibcMspaceReallocalign_nid_postfix(arena, regrown, 0, 16) == nullptr);
Require(sceLibcMspaceMallocStatsFast_nid_postfix(arena, &stats) == 0 && stats.currentInuseSize >= 100);
sceLibcMspaceFree_nid_postfix(arena, memaligned);
void* region = sceLibcMspaceMalloc_nid_postfix(arena, 8192);
Require(region != nullptr);
Require(sceLibcMspaceCreate_nid_postfix("outside", static_cast<unsigned char*>(region) + 8192, 4096, 0) == nullptr);
void* nested = sceLibcMspaceCreate_nid_postfix("nested", region, 8192, 1);
Require(nested == region);
void* inner = sceLibcMspaceMalloc_nid_postfix(nested, 64);
Require(inner > region && inner < static_cast<unsigned char*>(region) + 8192);
Require(sceLibcMspaceMallocUsableSize_nid_postfix(inner) == 64 && sceLibcMspaceMallocUsableSize_nid_postfix(region) == 8192);
Require(sceLibcMspaceCreate_nid_postfix("flags", storage.data(), storage.size(), 2) == nullptr);
Require(sceLibcMspaceDestroy_nid_postfix(nested) == 0);
sceLibcMspaceFree_nid_postfix(arena, region);
Require(sceLibcMspaceDestroy_nid_postfix(arena) == 0);
Require(sceLibcMspaceMalloc_nid_postfix(arena, 8) == nullptr);
Require(sceLibcMspaceCreate_nid_postfix("reuse", storage.data(), storage.size(), 0) == arena);
Require(sceLibcMspaceDestroy_nid_postfix(arena) == 0);
}