mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-28 12:05:06 +00:00
171 lines
7.5 KiB
C++
171 lines
7.5 KiB
C++
#include "prx/libSceJsc/include/Api.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <codecvt>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <locale>
|
|
#include <limits>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
|
|
|
|
static void Check(bool value, int line) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "JavaScriptCore string check failed at line %d\n", line);
|
|
std::abort();
|
|
}
|
|
}
|
|
#define Require(value) Check((value), __LINE__)
|
|
|
|
static void Unicode() {
|
|
std::u32string scalars;
|
|
for (char32_t codepoint = 1; codepoint <= 0x10FFFF; ++codepoint) {
|
|
if (codepoint < 0xD800 || codepoint > 0xDFFF) scalars.push_back(codepoint);
|
|
}
|
|
std::codecvt_utf8<char32_t> encode;
|
|
std::codecvt_utf8_utf16<char16_t> decode;
|
|
std::mbstate_t state{};
|
|
const char32_t* readScalars = nullptr;
|
|
std::string utf8(scalars.size() * 4, '\0');
|
|
char* writtenBytes = nullptr;
|
|
Require(encode.out(state, scalars.data(), scalars.data() + scalars.size(), readScalars,
|
|
utf8.data(), utf8.data() + utf8.size(), writtenBytes) == std::codecvt_base::ok);
|
|
Require(readScalars == scalars.data() + scalars.size());
|
|
utf8.resize(writtenBytes - utf8.data());
|
|
state = {};
|
|
const char* readBytes = nullptr;
|
|
std::u16string utf16(utf8.size(), 0);
|
|
char16_t* writtenUnits = nullptr;
|
|
Require(decode.in(state, utf8.data(), utf8.data() + utf8.size(), readBytes,
|
|
utf16.data(), utf16.data() + utf16.size(), writtenUnits) == std::codecvt_base::ok);
|
|
Require(readBytes == utf8.data() + utf8.size());
|
|
utf16.resize(writtenUnits - utf16.data());
|
|
auto* string = JSStringCreateWithUTF8CString(utf8.c_str());
|
|
Require(JSStringGetLength(string) == utf16.size());
|
|
const auto* characters = JSStringGetCharactersPtr(string);
|
|
Require(std::equal(utf16.begin(), utf16.end(), characters));
|
|
const std::vector<std::uint16_t> units(utf16.begin(), utf16.end());
|
|
auto* other = JSStringCreateWithCharacters(units.data(), units.size());
|
|
Require(JSStringIsEqual(string, other));
|
|
Require(JSStringGetMaximumUTF8CStringSize(string) == utf16.size() * 3 + 1);
|
|
std::vector<char> buffer(utf8.size() + 2, '?');
|
|
Require(JSStringGetUTF8CString(string, buffer.data(), buffer.size() - 1) == utf8.size() + 1);
|
|
Require(std::memcmp(buffer.data(), utf8.c_str(), utf8.size() + 1) == 0);
|
|
Require(buffer.back() == '?');
|
|
JSStringRelease(other);
|
|
JSStringRelease(string);
|
|
}
|
|
|
|
static void Boundaries() {
|
|
const char text[] = "A\xC2\xA2\xE2\x82\xAC\xF0\x9F\x98\x80Z";
|
|
auto* string = JSStringCreateWithUTF8CString(text);
|
|
Require(JSStringGetLength(string) == 6);
|
|
const std::array<std::size_t, 6> complete{0, 1, 3, 6, 10, 11};
|
|
for (std::size_t capacity = 1; capacity <= sizeof(text) + 1; ++capacity) {
|
|
std::array<char, 32> buffer;
|
|
buffer.fill('?');
|
|
std::size_t expected = 0;
|
|
for (const auto length : complete) {
|
|
if (length < capacity) expected = length;
|
|
}
|
|
Require(JSStringGetUTF8CString(string, buffer.data(), capacity) == expected + 1);
|
|
Require(std::memcmp(buffer.data(), text, expected) == 0 && buffer[expected] == '\0');
|
|
for (auto index = expected + 1; index < buffer.size(); ++index) Require(buffer[index] == '?');
|
|
}
|
|
char unchanged = '!';
|
|
Require(JSStringGetUTF8CString(string, &unchanged, 0) == 0 && unchanged == '!');
|
|
Require(JSStringGetUTF8CString(string, nullptr, 10) == 0);
|
|
Require(JSStringGetUTF8CString(nullptr, &unchanged, 1) == 0 && unchanged == '!');
|
|
Require(JSStringGetLength(nullptr) == 0 && JSStringGetCharactersPtr(nullptr) == nullptr);
|
|
Require(JSStringIsEqual(nullptr, nullptr) && !JSStringIsEqual(nullptr, string));
|
|
Require(JSStringIsEqualToUTF8CString(string, text));
|
|
Require(!JSStringIsEqualToUTF8CString(string, "a"));
|
|
JSStringRelease(string);
|
|
}
|
|
|
|
static void Invalid() {
|
|
const auto* unavailable = reinterpret_cast<const std::uint16_t*>(std::uintptr_t{1});
|
|
for (const auto count : {static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()) + 1,
|
|
static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max()), std::numeric_limits<std::size_t>::max()}) {
|
|
bool threw = false;
|
|
try {
|
|
JSStringCreateWithCharacters(unavailable, count);
|
|
} catch (const std::runtime_error&) {
|
|
threw = true;
|
|
}
|
|
Require(threw);
|
|
}
|
|
auto* empty = JSStringCreateWithUTF8CString("");
|
|
Require(JSStringGetLength(empty) == 0);
|
|
auto* nullString = JSStringCreateWithCharacters(nullptr, 0);
|
|
Require(JSStringGetLength(nullString) == 0 && JSStringGetCharactersPtr(nullString) == nullptr);
|
|
Require(!JSStringIsEqual(empty, nullString));
|
|
Require(JSStringIsEqualToUTF8CString(nullString, "\xFF"));
|
|
JSStringRelease(nullString);
|
|
for (const auto* invalid : {"\x80", "\xC0\x80", "\xC1\xBF", "\xE0\x80\x80", "\xED\xA0\x80", "\xF0\x80\x80\x80", "\xF4\x90\x80\x80", "\xF5\x80\x80\x80", "\xFF", "x\xC2", "x\xE2\x82", "x\xF0\x9F\x98", "\xE2!"}) {
|
|
auto* string = JSStringCreateWithUTF8CString(invalid);
|
|
Require(JSStringGetLength(string) == 0 && JSStringGetCharactersPtr(string) == nullptr);
|
|
Require(!JSStringIsEqual(string, empty));
|
|
Require(JSStringIsEqualToUTF8CString(string, "\xFF"));
|
|
char buffer = '!';
|
|
Require(JSStringGetUTF8CString(string, &buffer, 1) == 1 && buffer == '\0');
|
|
JSStringRelease(string);
|
|
}
|
|
for (const auto& units : std::vector<std::vector<std::uint16_t>>{{0xD800}, {0xDC00}, {'x', 0xD800}, {'x', 0xDC00, 'y'}, {'x', 0xD800, 'y'}}) {
|
|
auto* string = JSStringCreateWithCharacters(units.data(), units.size());
|
|
Require(JSStringGetLength(string) == units.size());
|
|
Require(std::equal(units.begin(), units.end(), JSStringGetCharactersPtr(string)));
|
|
std::array<char, 16> buffer;
|
|
buffer.fill('?');
|
|
const std::size_t prefix = units[0] == 'x' ? 1 : 0;
|
|
Require(JSStringGetUTF8CString(string, buffer.data(), buffer.size()) == prefix + 1);
|
|
Require(buffer[prefix] == '\0' && buffer[prefix + 1] == '?');
|
|
JSStringRelease(string);
|
|
}
|
|
JSStringRelease(empty);
|
|
}
|
|
|
|
static void Ownership() {
|
|
std::array<std::uint16_t, 4> units{'A', 0, 0x03A9, 'B'};
|
|
auto* string = JSStringCreateWithCharacters(units.data(), units.size());
|
|
units.fill('?');
|
|
const auto* pointer = JSStringGetCharactersPtr(string);
|
|
Require(pointer[0] == 'A' && pointer[1] == 0 && pointer[2] == 0x03A9 && pointer[3] == 'B');
|
|
std::array<char, 10> buffer{};
|
|
Require(JSStringGetUTF8CString(string, buffer.data(), buffer.size()) == 6);
|
|
const std::array<char, 6> expected{'A', 0, static_cast<char>(0xCE), static_cast<char>(0xA9), 'B', 0};
|
|
Require(std::equal(expected.begin(), expected.end(), buffer.begin()));
|
|
auto* retained = JSStringRetain(string);
|
|
Require(retained == string);
|
|
JSStringRelease(string);
|
|
Require(JSStringGetCharactersPtr(retained) == pointer && JSStringGetLength(retained) == 4);
|
|
std::vector<std::thread> threads;
|
|
for (unsigned index = 0; index < 4; ++index) {
|
|
threads.emplace_back([retained] {
|
|
for (unsigned count = 0; count < 5000; ++count) {
|
|
Require(JSStringRetain(retained) == retained);
|
|
Require(JSStringGetLength(retained) == 4);
|
|
JSStringRelease(retained);
|
|
}
|
|
});
|
|
}
|
|
for (auto& thread : threads) thread.join();
|
|
JSStringRelease(retained);
|
|
}
|
|
|
|
int main() {
|
|
Unicode();
|
|
Boundaries();
|
|
Invalid();
|
|
Ownership();
|
|
}
|