Files
AnyPS5/core/libs/prx/libSceNKWebKit/CharactersToNumber.cpp
T
KernelPanicNT 78c9c9127a feat(libSceNKWebKit): parse decimal numbers as WTF double-conversion
Implement the six WTF::charactersToDouble and WTF::charactersToFloat
overloads for Latin-1 and UTF-16 input and the four static
WTF::double_conversion::StringToDoubleConverter StringToDouble and
StringToFloat overloads for char and uint16_t input, following pinned
public WebKit. One parser follows StringToIeee: the number grammar, the
772-digit cut with a sticky digit, the exponent saturation and the
processed length. charactersToDouble and charactersToFloat add
toDoubleType's leading ASCII spaces and ok/parsedLength results, map
UTF-16 code units above 0x7F to 0 and round the float overloads through
double. StringToDoubleConverter reads the code units as they are, skips
no spaces and rounds StringToFloat directly to float as Strtof does.
Null characters with a nonzero length and a null processed length throw
where WebKit faults, a decimal exponent that leaves the range of
WebKit's int throws where WebKit overflows, and a UTF-16 charactersTo*
length above UINT32_MAX throws where WebKit's conversion Vector crashes.
2026-10-31 03:59:10 +01:00

249 lines
10 KiB
C++

#include "prx/libSceNKWebKit/include/InventoryExports.hpp"
#include <array>
#include <charconv>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <stdexcept>
#include <string>
#include <system_error>
#include <vector>
namespace {
constexpr std::size_t MaxSignificantDigits = 772;
constexpr std::size_t ConversionBufferSize = 64;
constexpr std::int64_t MaxExponent = std::numeric_limits<int>::max() / 2;
constexpr std::int64_t MaxDecimalPower = 309;
constexpr std::int64_t MinDecimalPower = -324;
template<typename TFloat>
struct ParsedNumber {
TFloat value;
std::size_t length;
};
template<typename TCharacter>
bool IsDigit(TCharacter character) {
return character >= '0' && character <= '9';
}
std::int64_t CheckedExponent(std::int64_t value) {
if (value < std::numeric_limits<int>::min() || value > std::numeric_limits<int>::max()) throw std::overflow_error("WTF::double_conversion: the decimal exponent overflows int, which is undefined in WebKit");
return value;
}
template<typename TCharacter>
bool IsSpace(TCharacter character) {
return character <= ' ' && (character == ' ' || (character <= 0xD && character >= 0x9));
}
template<typename TFloat>
TFloat Convert(std::string digits, std::int64_t exponent) {
while (!digits.empty() && digits.back() == '0') {
digits.pop_back();
exponent = CheckedExponent(exponent + 1);
}
if (digits.empty()) return 0;
const auto count = static_cast<std::int64_t>(digits.size());
CheckedExponent(exponent + count);
if (exponent + count - 1 >= MaxDecimalPower) return std::numeric_limits<TFloat>::infinity();
if (exponent + count <= MinDecimalPower) return 0;
digits += 'e';
digits += std::to_string(exponent);
TFloat value = 0;
const auto result = std::from_chars(digits.data(), digits.data() + digits.size(), value);
if (result.ec == std::errc::result_out_of_range) return exponent + count > 0 ? std::numeric_limits<TFloat>::infinity() : 0;
if (result.ec != std::errc() || result.ptr != digits.data() + digits.size()) throw std::logic_error("WTF::parseDouble: decimal conversion rejected its digits");
return value;
}
template<typename TFloat, typename TCharacter>
class DecimalParser {
public:
DecimalParser(const TCharacter* input, std::size_t length) : input(input), current(input), end(input + length) {}
ParsedNumber<TFloat> Parse() {
if (current == end) return {0, 0};
if (*current == '+' || *current == '-') {
negative = *current == '-';
if (++current == end) return {0, 0};
}
bool leadingZero = false;
if (*current == '0') {
leadingZero = true;
while (*current == '0') {
if (++current == end) return signedZero();
}
}
while (IsDigit(*current)) {
if (digits.size() < MaxSignificantDigits) digits.push_back(static_cast<char>(*current));
else {
insignificantDigits = CheckedExponent(insignificantDigits + 1);
nonzeroDigitDropped = nonzeroDigitDropped || *current != '0';
}
if (++current == end) return finish();
}
if (*current == '.') {
if (++current == end) {
if (digits.empty() && !leadingZero) return {0, 0};
return finish();
}
if (digits.empty()) {
while (*current == '0') {
if (++current == end) return signedZero();
exponent = CheckedExponent(exponent - 1);
}
}
while (IsDigit(*current)) {
if (digits.size() < MaxSignificantDigits) {
digits.push_back(static_cast<char>(*current));
exponent = CheckedExponent(exponent - 1);
} else nonzeroDigitDropped = nonzeroDigitDropped || *current != '0';
if (++current == end) return finish();
}
}
if (!leadingZero && exponent == 0 && digits.empty()) return {0, 0};
if (*current == 'e' || *current == 'E') {
const auto* marker = current;
if (++current == end) return finishAt(marker);
bool negativeExponent = false;
if (*current == '+' || *current == '-') {
negativeExponent = *current == '-';
if (++current == end) return finishAt(marker);
}
if (!IsDigit(*current)) return finishAt(marker);
std::int64_t number = 0;
do {
const int digit = *current - '0';
if (number >= MaxExponent / 10 && !(number == MaxExponent / 10 && digit <= MaxExponent % 10)) number = MaxExponent;
else number = number * 10 + digit;
++current;
} while (current != end && IsDigit(*current));
exponent = CheckedExponent(exponent + (negativeExponent ? -number : number));
}
return finish();
}
private:
ParsedNumber<TFloat> signedZero() const {
return {negative ? -TFloat(0) : TFloat(0), static_cast<std::size_t>(current - input)};
}
ParsedNumber<TFloat> finishAt(const TCharacter* position) {
current = position;
return finish();
}
ParsedNumber<TFloat> finish() {
exponent = CheckedExponent(exponent + insignificantDigits);
if (nonzeroDigitDropped) {
digits.push_back('1');
exponent = CheckedExponent(exponent - 1);
}
const TFloat converted = Convert<TFloat>(digits, exponent);
return {negative ? -converted : converted, static_cast<std::size_t>(current - input)};
}
const TCharacter* input;
const TCharacter* current;
const TCharacter* end;
std::string digits;
std::int64_t exponent = 0;
std::int64_t insignificantDigits = 0;
bool nonzeroDigitDropped = false;
bool negative = false;
};
template<typename TFloat, typename TCharacter>
TFloat StringToIeee(const TCharacter* data, std::size_t length, std::size_t* processedCount) {
if (!processedCount) throw std::invalid_argument("WTF::double_conversion: a null processed length faults in WebKit");
*processedCount = 0;
if (!data && length) throw std::invalid_argument("WTF::double_conversion: null characters with a nonzero length fault in WebKit");
const auto parsed = DecimalParser<TFloat, TCharacter>(data, length).Parse();
*processedCount = parsed.length;
return parsed.value;
}
double ParseDouble(const unsigned char* data, std::size_t length, std::size_t* parsedLength) {
return StringToIeee<double>(data, length, parsedLength);
}
double ParseDouble(const char16_t* data, std::size_t length, std::size_t* parsedLength) {
if (length <= ConversionBufferSize) {
std::array<unsigned char, ConversionBufferSize> buffer{};
for (std::size_t index = 0; index < length; ++index) buffer[index] = data[index] < 0x80 ? static_cast<unsigned char>(data[index]) : 0;
return ParseDouble(buffer.data(), length, parsedLength);
}
if (length > std::numeric_limits<std::uint32_t>::max()) throw std::length_error("WTF::Vector: conversion buffer capacity above UINT_MAX");
std::vector<unsigned char> buffer(length);
for (std::size_t index = 0; index < length; ++index) buffer[index] = data[index] < 0x80 ? static_cast<unsigned char>(data[index]) : 0;
return ParseDouble(buffer.data(), length, parsedLength);
}
template<typename TCharacter>
double ToDouble(const TCharacter* data, std::size_t length, bool allowTrailingJunk, bool* ok, std::size_t* parsedLength) {
if (!data && length) throw std::invalid_argument("WTF number parsing: null characters with a nonzero length fault in WebKit");
std::size_t leadingSpaces = 0;
while (leadingSpaces < length && IsSpace(data[leadingSpaces])) ++leadingSpaces;
const double value = ParseDouble(data + leadingSpaces, length - leadingSpaces, parsedLength);
if (!*parsedLength) {
if (ok) *ok = false;
return 0.0;
}
*parsedLength += leadingSpaces;
if (ok) *ok = allowTrailingJunk || *parsedLength == length;
return value;
}
}
extern "C" {
float APS5_VABI _ZN3WTF17charactersToFloatEPKDsmPb(const char16_t* data, std::size_t length, bool* ok) {
std::size_t parsedLength = 0;
return static_cast<float>(ToDouble(data, length, false, ok, &parsedLength));
}
float APS5_VABI _ZN3WTF17charactersToFloatEPKDsmRm(const char16_t* data, std::size_t length, std::size_t* parsedLength) {
return static_cast<float>(ToDouble(data, length, true, nullptr, parsedLength));
}
float APS5_VABI _ZN3WTF17charactersToFloatEPKhmPb(const unsigned char* data, std::size_t length, bool* ok) {
std::size_t parsedLength = 0;
return static_cast<float>(ToDouble(data, length, false, ok, &parsedLength));
}
float APS5_VABI _ZN3WTF17charactersToFloatEPKhmRm(const unsigned char* data, std::size_t length, std::size_t* parsedLength) {
return static_cast<float>(ToDouble(data, length, true, nullptr, parsedLength));
}
double APS5_VABI _ZN3WTF18charactersToDoubleEPKDsmPb(const char16_t* data, std::size_t length, bool* ok) {
std::size_t parsedLength = 0;
return ToDouble(data, length, false, ok, &parsedLength);
}
double APS5_VABI _ZN3WTF18charactersToDoubleEPKhmPb(const unsigned char* data, std::size_t length, bool* ok) {
std::size_t parsedLength = 0;
return ToDouble(data, length, false, ok, &parsedLength);
}
float APS5_VABI _ZN3WTF17double_conversion23StringToDoubleConverter13StringToFloatEPKcmPm(const char* buffer, std::size_t length, std::size_t* processedCharactersCount) {
return StringToIeee<float>(buffer, length, processedCharactersCount);
}
float APS5_VABI _ZN3WTF17double_conversion23StringToDoubleConverter13StringToFloatEPKtmPm(const std::uint16_t* buffer, std::size_t length, std::size_t* processedCharactersCount) {
return StringToIeee<float>(buffer, length, processedCharactersCount);
}
double APS5_VABI _ZN3WTF17double_conversion23StringToDoubleConverter14StringToDoubleEPKcmPm(const char* buffer, std::size_t length, std::size_t* processedCharactersCount) {
return StringToIeee<double>(buffer, length, processedCharactersCount);
}
double APS5_VABI _ZN3WTF17double_conversion23StringToDoubleConverter14StringToDoubleEPKtmPm(const std::uint16_t* buffer, std::size_t length, std::size_t* processedCharactersCount) {
return StringToIeee<double>(buffer, length, processedCharactersCount);
}
}