Skip to content

feat(libSceIcu): implement u_isspace, u_isblank, u_isWhitespace, u_isJavaSpaceChar and u_isUWhiteSpace - #12630

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
Lindamea1:feat/icu-space-classifiers
Oct 29, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
Lindamea1:feat/icu-space-classifiers

Conversation

@Lindamea1

Copy link
Copy Markdown
Contributor

What

On main, libSceIcu exports these five ICU character property functions as Unknown stubs that throw. Their NIDs are compute_nid (tools/nid_names.py) of the plain ICU names, so each _nid_postfix definition replaces its stub (Export.cpp L319-L322, L344-L352, L369-L372, L409-L412) under the same export NID:

Export NID Returns 1 for
u_isspace tcSjvPP2-4s Zs, Zl, Zp, U+0009..000D, U+001C..001F and U+0085 (29 code points)
u_isblank twrAIl4N+EM U+0009 and U+0020 when (uint32_t)c <= 0x9F, otherwise Zs (18)
u_isWhitespace O3zT1gO1ZNM Zs, Zl and Zp except U+00A0, U+2007 and U+202F, plus U+0009..000D and U+001C..001F (25)
u_isJavaSpaceChar mY5RbYVLzr4 Zs, Zl and Zp (19)
u_isUWhiteSpace U089pStD8Bw the White_Space property (25)

Zs is U+0020, 00A0, 1680, 2000..200A, 202F, 205F and 3000; Zl is U+2028 and Zp is U+2029. White_Space is U+0009..000D, 0020, 0085, 00A0, 1680, 2000..200A, 2028, 2029, 202F, 205F and 3000. Each function takes a UChar32 (std::int32_t) and returns a UBool (std::int8_t) of 1 or 0. Every other int32_t returns 0, including negative values, surrogates and values above U+10FFFF. The functions keep no state, have no error paths and use no host ICU; Linux and Windows run the same code.

Reference, ICU 78.1 at 049e0d6a (firmware 13.42 reports ICU 78.1, #10961):

  • uchar.cpp L196-L244: the five definitions with IS_THAT_CONTROL_SPACE and IS_THAT_ASCII_CONTROL_SPACE. u_isUWhiteSpace tests bit UPROPS_WHITE_SPACE (uprops.h L185-L186) of u_getUnicodeProperties(c, 1).
  • uprops.h L381-L393: TAB, CR, NBSP, FIGURESP and NNBSP. umachine.h L269: typedef int8_t UBool.
  • Out of range: utrie2.h L844-L855 reads the trie's error slot when (uint32_t)c > 0x10FFFF. propsTrie (uchar_props_data.h L1524-L1537) and propsVectorsTrie (L4238-L4251) have initial and error value 0: category Cn and no White_Space bit.
  • The sets are those of the Unicode 17 UnicodeData.txt (Zs, Zl and Zp rows 33, 161, 5193, 7392-7402, 7432, 7433, 7439, 7487 and 11279) and ppucd.txt (WSpace rows 1364-1368, 1388, 1504, 1532, 7363, 9911-9921, 9957, 9958, 9966, 10026 and 14348) that ICU 78.1 ships.
  • ICU 65.1 (uchar.cpp L196-L245 at release-65-1, fd123bf0) and 68.2 (uchar.cpp L196-L245 at release-68-2, 84e1f26e) differ from 78.1 only by (UBool) casts and by writing 0x85 as NL, and UBool is int8_t there too. The releases between them and up to 77.1 differ from 78.1 at most in the same way: the casts remain until 75.1 and NL until 68.2.

Space.cpp keeps Zs and White_Space as fixed range tables, and Zl and Zp as the code points U+2028 and U+2029, instead of ICU's trie data. To check this against every ICU release from the 65 to 68 range that #11115 gives for the 3.20 module up to the 78.1 that 13.42 reports (17 releases, listed below), I emulated ICU's code paths for the five functions over the generated tries in each release's uchar_props_data.h. The emulation covers every code point, -0x10000..-1 and 0x110000..0x11FFFF. Every release gives exactly the sets above (29, 18, 25, 19 and 25 code points) and 0 for every out-of-range value: both tries' error slots hold 0, which means category Cn and propsVectors row 0, whose White_Space bit is clear. The emulation follows the 78.1 code, and each release matches it: apart from the casts and NL, the five definitions are the same, and so are GET_PROPS, u_getUnicodeProperties, the UTrie2 lookup macros and constants in utrie2.h, and UPROPS_WHITE_SPACE (bit 0). UnicodeData.txt and ppucd.txt list the same Zs, Zl, Zp and WSpace code points in every release. The ICU version of the 3.20 module itself is not measured.

ICU releases checked
ICU Commit UCD
65.1 fd123bf0 12.1.0
66.1 5f681ecb 13.0.0
67.1 125e29d5 13.0.0
68.1 5d81f6f9 13.0.0
68.2 84e1f26e 13.0.0
69.1 0e7b4428 13.0.0
70.1 a56dde82 14.0.0
71.1 c205e7ee 14.0.0
72.1 ff3514f2 15.0.0
73.1 5861e1fd 15.0.0
73.2 680f5217 15.0.0
74.1 9edac7b7 15.1.0
74.2 2d029329 15.1.0
75.1 7750081b 15.1.0
76.1 8eca245c 16.0.0
77.1 457157a9 16.0.0
78.1 049e0d6a 17.0.0

Firmware availability: BunnyFl's full 13.42 export list has u_isUWhiteSpace among the 104 exports it shares with the 3.20 table. u_isspace, u_isblank, u_isWhitespace and u_isJavaSpaceChar are not among its 299 exports, by name or NID, so these four are 3.20-inventory exports kept for older firmware, like those of #10961 and #11013.

Changes:

  • core/libs/prx/libSceIcu/Space.cpp and Space.hpp (included from libSceIcu.hpp) add the five exports; Export.cpp drops their five stubs.
  • core/libs/prx/libSceIcu/CMakeLists.txt builds Space.cpp and registers the new guest_icu_space test (tests/Space.cpp).
  • docs/dev/debt/libSceIcu.md: a new Functional entry (unverified on a console, ICU 78.1 with fixed sets, 13.42 availability) and the Unknown count from 107 to 102.
  • tools/progress.py --module libSceIcu goes from libraries 11/120 (9.17%) to libraries 16/120 (13.33%).

Overlap: no open PR mentions any of the five names or NIDs (gh pr list --search for each, and a git log -G search of the 5569 commits that the 1366 open PR heads fetched at about 20:30Z add to main, which does find #12320's u_isISOControl). A git merge-tree of this branch with each of the 14 open PR heads that change libSceIcu files merges cleanly with #10962 to #10967 and #12451. With #12320 only the Unknown count sentence in docs/dev/debt/libSceIcu.md conflicts, because #12320 moves it and sets it to 92; whichever lands second recounts (with both, Export.cpp keeps 82 Unknown stubs). #11088, #11115, #11118, #11122, #11132 and #11136 conflict in CMakeLists.txt and the debt file in the same hunks as with main; this branch's side of the debt hunk differs from main's only in the Unknown count. At 20:49Z those 14 heads were still current, and none of the 9 PRs opened after the fetch (#12562 to #12570) changes a libSceIcu file.

Tested

Linux (Gentoo, kernel 7.2.8, AMD Ryzen 7 9800X3D), GCC 16.2.1, Ninja, Release build with BUILD_TESTING=ON of this branch on main 319621b.

  • ctest --test-dir build -R '^(guest_icu_space|guest_icu_cstring|guest_icu_version|guest_icu_unescape|public_headers)$' --output-on-failure -j8 --timeout 300: 5/5 passed. A direct run of guest_icu_space_tests takes 0.019 s.
  • python3 tools/check_test_registration.py build: passed (1516 test executables, none built and not run).
  • CI test selection (tools/select_tests.py dump, select with the 7 changed files, ninja -f select.ninja selected, select_tests.py ctest): 490 of 2746 tests selected, 490/490 passed.
  • python3 tools/check_conventions.py --base 319621ba2, also with this title and description: passed.
  • The new guest_icu_space test (core/libs/prx/libSceIcu/tests/Space.cpp) calls each function for every value from -0x1000 to 0x110FFF, for INT32_MIN, -1, 0x110000 and INT32_MAX, and for each true code point plus 0x110000, 0x200000, 0x1000000, 0x80000000 and 0xFFFF0000 (modulo 2^32). It requires exactly 1 for the code points in a literal list written per function from the ICU 78.1 data, and exactly 0 for every other value. A script checked the five lists against UnicodeData.txt and ppucd.txt with the uchar.cpp formulas.
  • The test rejects wrong implementations. In one run I compiled it with the unmutated Space.cpp, which passes, and with 11 mutated copies, each of which aborted: u_isWhitespace keeping U+202F, u_isspace without U+0085 or returning 2, u_isblank without TAB or without its <= 0x9F branch, u_isUWhiteSpace equal to u_isspace, with U+180E in place of U+1680 or with a 21-bit mask, u_isJavaSpaceChar truncating to 16 bits, Zs without U+205F, and the separators without U+2029.
  • Probe of the NID-patched build/core/libs/libs/libSceIcu.prx: dlsym resolves the five NIDs, and their results for U+0009, 001C, 0020, 0085, 00A0, 2007, 2028, 0041, -1 and 0x110000 match the table above. No sceIcuUnknown_ symbol for these NIDs is left in the patched or unpatched library.
  • MinGW-w64 syntax check of the changed files only (x86_64-w64-mingw32-g++ 16.2.0, -std=gnu++20 -fsyntax-only -Wall -Wextra -D_WIN32_WINNT=0x0602) on Space.cpp, Export.cpp and tests/Space.cpp. This is not a Windows build or run.
  • Not compared with a console and not run with a title or homebrew.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

🤖 Generated with Claude Code

…JavaSpaceChar and u_isUWhiteSpace

Replace the five Unknown stubs whose NIDs hash these ICU names with the
uchar.cpp definitions of ICU 78.1 (049e0d6a), over the Zs, Zl, Zp and
White_Space code points that ICU 65.1 to 78.1 ship. Values outside
U+0000..U+10FFFF return 0, as ICU's trie error value gives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Progress report (3a2b919 → f70f9e3)

📈 System libraries: 45.94% (+0.03%, +5 implemented, -5 removed functions)

✅ 5 implemented
Library Name
libSceIcu u_isJavaSpaceChar_nid_postfix
libSceIcu u_isUWhiteSpace_nid_postfix
libSceIcu u_isWhitespace_nid_postfix
libSceIcu u_isblank_nid_postfix
libSceIcu u_isspace_nid_postfix
🗑️ 5 removed
Library Name
libSceIcu sceIcuUnknown_O3zT1gO1ZNM_nid_no_patch
libSceIcu sceIcuUnknown_U089pStD8Bw_nid_no_patch
libSceIcu sceIcuUnknown_mY5RbYVLzr4_nid_no_patch
libSceIcu sceIcuUnknown_tcSjvPP2_4s_nid_no_patch
libSceIcu sceIcuUnknown_twrAIl4N_EM_nid_no_patch

Changed libraries (implemented exports)

Library Before After Change
libSceIcu 11/120 (9.17%) 16/120 (13.33%) +4.17 pp

@adriadam10 adriadam10 self-assigned this Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 12320 docs/dev/debt/libSceIcu.md L16-17
PR 12629 docs/dev/debt/libSceIcu.md L17
PR 12692 docs/dev/debt/libSceIcu.md L17
PR 12693 core/libs/prx/libSceIcu/CMakeLists.txt L4
docs/dev/debt/libSceIcu.md L8
PR 12738 docs/dev/debt/libSceIcu.md L8

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 11088 core/libs/prx/libSceIcu/CMakeLists.txt
core/libs/prx/libSceIcu/libSceIcu.hpp
docs/dev/debt/libSceIcu.md
PR 11118 core/libs/prx/libSceIcu/CMakeLists.txt
core/libs/prx/libSceIcu/libSceIcu.hpp
docs/dev/debt/libSceIcu.md
PR 11122 core/libs/prx/libSceIcu/CMakeLists.txt
core/libs/prx/libSceIcu/libSceIcu.hpp
docs/dev/debt/libSceIcu.md
PR 11132 core/libs/prx/libSceIcu/CMakeLists.txt
core/libs/prx/libSceIcu/libSceIcu.hpp
docs/dev/debt/libSceIcu.md
PR 11136 core/libs/prx/libSceIcu/CMakeLists.txt
core/libs/prx/libSceIcu/libSceIcu.hpp
docs/dev/debt/libSceIcu.md

@JJenkx

JJenkx commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Windows run of this head (f70f9e34f3), merged onto main edf6804cf5 (clean merge, local only). Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X, WinLibs GCC 15.2.0 UCRT r7, Ninja, Release, BUILD_TESTING=ON, no Vulkan driver visible.

  • This PR: guest_icu_space passes 3 of 3 (ctest --repeat until-fail:3).
  • Negative control: not possible. With this PR's source files (core/libs/prx/libSceIcu/Export.cpp, core/libs/prx/libSceIcu/Space.cpp, core/libs/prx/libSceIcu/Space.hpp, core/libs/prx/libSceIcu/libSceIcu.hpp) put back to main's version, and the ones it adds removed, the build fails against the rest of the PR, so there is no source-only control.

AI-assisted: yes (Claude Code).

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@Lindamea1 reviewed at f70f9e3: PASS. The five ICU space predicates are written as Unicode character-set tables and match ICU's uchar.cpp definitions (current Zs set, U+180E excluded); five stubs are removed and the debt count goes 107 -> 102. The new guest_icu_space test is registered.

@adriadam10 exact-head CI is green (run 40790128160); we'll merge this in the next train unless you'd rather merge it yourself.

@oneandonlydean oneandonlydean self-assigned this Oct 29, 2026
@oneandonlydean
oneandonlydean merged commit f81c7a7 into boykopovar:main Oct 29, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants