From 1bfb3cbf09069fe71ac784b2d6fa28659ffeb99d Mon Sep 17 00:00:00 2001 From: Hannah Lyons <7751607+babyhuey@users.noreply.github.com> Date: Thu, 8 Oct 2026 15:07:18 -0400 Subject: [PATCH] feat(libc): implement the C locale wide character class functions --- core/libs/prx/libc/src/RuneLocale.cpp | 64 +++++++++++++++++++++++++++ core/libs/tests/GuestRuneLocale.cpp | 54 ++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/core/libs/prx/libc/src/RuneLocale.cpp b/core/libs/prx/libc/src/RuneLocale.cpp index 3eb19dfdb..cba8bd792 100644 --- a/core/libs/prx/libc/src/RuneLocale.cpp +++ b/core/libs/prx/libc/src/RuneLocale.cpp @@ -1,5 +1,7 @@ +#include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include namespace GuestRune { @@ -78,3 +80,65 @@ const GuestRune::Locale* _CurrentRuneLocale_nid_postfix = &_DefaultRuneLocale_ni int __mb_sb_limit_nid_postfix = 256; } + +namespace GuestRune { + +constexpr std::uint32_t Weof = 0xffffffff; + +int Is(std::uint32_t c, std::uint64_t mask) { + return c < 256 && (_CurrentRuneLocale_nid_postfix->types[c] & mask) != 0; +} + +} + +extern "C" { + +int APS5_VABI iswalnum_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Alpha | GuestRune::Number); } +int APS5_VABI iswalpha_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Alpha); } +int APS5_VABI iswblank_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Blank); } +int APS5_VABI iswcntrl_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Control); } +int APS5_VABI iswdigit_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Digit); } +int APS5_VABI iswgraph_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Graph); } +int APS5_VABI iswlower_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Lower); } +int APS5_VABI iswprint_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Print); } +int APS5_VABI iswpunct_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Punct); } +int APS5_VABI iswspace_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Space); } +int APS5_VABI iswupper_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Upper); } +int APS5_VABI iswxdigit_nid_postfix(std::uint32_t c) { return GuestRune::Is(c, GuestRune::Hex); } +int APS5_VABI iswctype_nid_postfix(std::uint32_t c, std::uint64_t mask) { return GuestRune::Is(c, mask); } + +std::uint64_t APS5_VABI wctype_nid_postfix(const char* property) { + static constexpr struct { + const char* name; + std::uint64_t mask; + } properties[] = { + {"alnum", GuestRune::Alpha | GuestRune::Number}, {"alpha", GuestRune::Alpha}, {"blank", GuestRune::Blank}, + {"cntrl", GuestRune::Control}, {"digit", GuestRune::Digit}, {"graph", GuestRune::Graph}, + {"lower", GuestRune::Lower}, {"print", GuestRune::Print}, {"punct", GuestRune::Punct}, + {"space", GuestRune::Space}, {"upper", GuestRune::Upper}, {"xdigit", GuestRune::Hex}, + {"ideogram", 0x80000}, {"special", 0x100000}, {"phonogram", 0x200000}, + {"number", GuestRune::Number}, {"rune", 0xffffff00}, + }; + for (const auto& entry : properties) { + if (std::strcmp(property, entry.name) == 0) return entry.mask; + } + return 0; +} + +std::uint32_t APS5_VABI towlower_nid_postfix(std::uint32_t c) { + return c < 256 ? static_cast(_CurrentRuneLocale_nid_postfix->lower[c]) : c; +} + +std::uint32_t APS5_VABI towupper_nid_postfix(std::uint32_t c) { + return c < 256 ? static_cast(_CurrentRuneLocale_nid_postfix->upper[c]) : c; +} + +std::uint32_t APS5_VABI btowc_nid_postfix(int c) { + return c == -1 ? GuestRune::Weof : static_cast(c); +} + +int APS5_VABI wctob_nid_postfix(std::uint32_t c) { + return c < 256 ? static_cast(c) : -1; +} + +} diff --git a/core/libs/tests/GuestRuneLocale.cpp b/core/libs/tests/GuestRuneLocale.cpp index fb32e6f8b..86d5de99b 100644 --- a/core/libs/tests/GuestRuneLocale.cpp +++ b/core/libs/tests/GuestRuneLocale.cpp @@ -22,6 +22,24 @@ int APS5_VABI isxdigit_nid_postfix(int); int APS5_VABI isblank_nid_postfix(int); int APS5_VABI tolower_nid_postfix(int); int APS5_VABI toupper_nid_postfix(int); +int APS5_VABI iswalnum_nid_postfix(std::uint32_t); +int APS5_VABI iswalpha_nid_postfix(std::uint32_t); +int APS5_VABI iswblank_nid_postfix(std::uint32_t); +int APS5_VABI iswcntrl_nid_postfix(std::uint32_t); +int APS5_VABI iswdigit_nid_postfix(std::uint32_t); +int APS5_VABI iswgraph_nid_postfix(std::uint32_t); +int APS5_VABI iswlower_nid_postfix(std::uint32_t); +int APS5_VABI iswprint_nid_postfix(std::uint32_t); +int APS5_VABI iswpunct_nid_postfix(std::uint32_t); +int APS5_VABI iswspace_nid_postfix(std::uint32_t); +int APS5_VABI iswupper_nid_postfix(std::uint32_t); +int APS5_VABI iswxdigit_nid_postfix(std::uint32_t); +int APS5_VABI iswctype_nid_postfix(std::uint32_t, std::uint64_t); +std::uint64_t APS5_VABI wctype_nid_postfix(const char*); +std::uint32_t APS5_VABI towlower_nid_postfix(std::uint32_t); +std::uint32_t APS5_VABI towupper_nid_postfix(std::uint32_t); +std::uint32_t APS5_VABI btowc_nid_postfix(int); +int APS5_VABI wctob_nid_postfix(std::uint32_t); } void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -71,5 +89,41 @@ int main() { Require(Field(locale, 3136 + 4 * c) == toupper_nid_postfix(c)); } Require(Field(locale, 64 + 8 * 0x80) == 0 && Field(locale, 64 + 8 * 0xff) == 0); + + const struct { + const char* name; + std::uint64_t mask; + int (APS5_VABI* wide)(std::uint32_t); + int (APS5_VABI* narrow)(int); + } wide[] = { + {"alnum", 0x400100, iswalnum_nid_postfix, nullptr}, {"alpha", 0x100, iswalpha_nid_postfix, isalpha_nid_postfix}, + {"blank", 0x20000, iswblank_nid_postfix, isblank_nid_postfix}, {"cntrl", 0x200, iswcntrl_nid_postfix, iscntrl_nid_postfix}, + {"digit", 0x400, iswdigit_nid_postfix, isdigit_nid_postfix}, {"graph", 0x800, iswgraph_nid_postfix, isgraph_nid_postfix}, + {"lower", 0x1000, iswlower_nid_postfix, islower_nid_postfix}, {"print", 0x40000, iswprint_nid_postfix, isprint_nid_postfix}, + {"punct", 0x2000, iswpunct_nid_postfix, ispunct_nid_postfix}, {"space", 0x4000, iswspace_nid_postfix, isspace_nid_postfix}, + {"upper", 0x8000, iswupper_nid_postfix, isupper_nid_postfix}, {"xdigit", 0x10000, iswxdigit_nid_postfix, isxdigit_nid_postfix}, + }; + for (const auto& entry : wide) { + Require(wctype_nid_postfix(entry.name) == entry.mask); + for (std::uint32_t c = 0; c < 300; ++c) { + const int expected = c < 256 && (entry.narrow ? entry.narrow(static_cast(c)) : isalpha_nid_postfix(static_cast(c)) || isdigit_nid_postfix(static_cast(c))); + Require((entry.wide(c) != 0) == (expected != 0)); + Require((iswctype_nid_postfix(c, entry.mask) != 0) == (expected != 0)); + } + Require(entry.wide(0xffffffff) == 0 && iswctype_nid_postfix(0xffffffff, entry.mask) == 0); + } + Require(wctype_nid_postfix("ideogram") == 0x80000 && wctype_nid_postfix("special") == 0x100000); + Require(wctype_nid_postfix("phonogram") == 0x200000 && wctype_nid_postfix("number") == 0x400000); + Require(wctype_nid_postfix("rune") == 0xffffff00 && wctype_nid_postfix("") == 0 && wctype_nid_postfix("Alpha") == 0); + Require(iswctype_nid_postfix('A', 0) == 0 && iswctype_nid_postfix('7', wctype_nid_postfix("number")) != 0); + for (std::uint32_t c = 0; c < 300; ++c) { + Require(towlower_nid_postfix(c) == (c < 256 ? static_cast(tolower_nid_postfix(static_cast(c))) : c)); + Require(towupper_nid_postfix(c) == (c < 256 ? static_cast(toupper_nid_postfix(static_cast(c))) : c)); + } + Require(towlower_nid_postfix(0xffffffff) == 0xffffffff && towupper_nid_postfix(0xffffffff) == 0xffffffff); + Require(btowc_nid_postfix(-1) == 0xffffffff && btowc_nid_postfix(0) == 0); + Require(btowc_nid_postfix('A') == 'A' && btowc_nid_postfix(0xe9) == 0xe9 && btowc_nid_postfix(-23) == 0xe9); + Require(wctob_nid_postfix('A') == 'A' && wctob_nid_postfix(0xe9) == 0xe9 && wctob_nid_postfix(0) == 0); + Require(wctob_nid_postfix(0x100) == -1 && wctob_nid_postfix(0x20ac) == -1 && wctob_nid_postfix(0xffffffff) == -1); return 0; }