Skip to content

feat(libSceLibcInternal): implement the wchar_t num_put::do_put integer and bool members - #15747

Closed
B-Howell wants to merge 1 commit into
boykopovar:mainfrom
B-Howell:feat/r3-numput-wchar-do-int
Closed

B-Howell wants to merge 1 commit into
boykopovar:mainfrom
B-Howell:feat/r3-numput-wchar-do-int

Conversation

@B-Howell

@B-Howell B-Howell commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

What

Implements five ostreambuf_iterator<wchar_t> num_put members in libSceLibcInternal that used to throw through NotImplemented_nid_no_patch (sceLibcInternalPending3_144 and 147..150 in Err.cpp). They are what std::wostream << integer and << bool run:

Symbol NID New entry point libc helper
_ZNKSt7num_putIwSt19ostreambuf_iteratorIwSt11char_traitsIwEEE6do_putES3_RSt8ios_basewb 1C2-2WB9NN4 sceLibcInternalWideNumPutDoPutBool_nid_no_patch LibcWideNumPutBool_nid_no_patch
..._RSt8ios_basewl VpwhOe58wsM sceLibcInternalWideNumPutDoPutLong_nid_no_patch LibcWideNumPutLong_nid_no_patch
..._RSt8ios_basewm jHo78LGEtmU sceLibcInternalWideNumPutDoPutUnsignedLong_nid_no_patch LibcWideNumPutUnsignedLong_nid_no_patch
..._RSt8ios_basewx 9SSHrlIamto sceLibcInternalWideNumPutDoPutLongLong_nid_no_patch LibcWideNumPutLongLong_nid_no_patch
..._RSt8ios_basewy uX0nKsUo8gc sceLibcInternalWideNumPutDoPutUnsignedLongLong_nid_no_patch LibcWideNumPutUnsignedLongLong_nid_no_patch

The behaviour is the one [facet.num.put.virtuals] fixes, and the narrow num_put<char> facet in libc's LocaleSupport.cpp already implements it. This PR does not add a second formatter. The wide members reuse that code:

  • Stage 1 (printf conversion): the same PutFormattedInteger builds the %[+][#]{l,ll}{d,u,o,x,X} conversion from basefield, showpos, showbase and uppercase and runs snprintf. It is now a template over the iterator and fill type. The sign and 0x prefix rule moved into a shared IntegerPrefix.
  • Stage 2 (widening and punctuation): a new PutInteger overload for WideOutputIterator widens the digits through the stream's ctype<wchar_t>. It takes grouping() and thousands_sep() from the stream's numpunct<wchar_t>, through GuestLocale::WideNumpunctVtable, whose slot offsets are already static_asserted in GuestLocale.hpp. If the locale has no numpunct<wchar_t>, it falls back to the C-locale facet from libc's existing numpunct<wchar_t>::_Getcat. InsertSeparators is now a template over the string type, so the same grouping loop inserts a 16-bit separator.
  • Stage 3 (padding): PutField is now a template over the iterator and string type, so left, internal and the default right adjustment, and the reset of width() to 0, are the same code as for char.
  • do_put(bool): without boolalpha it returns do_put(out, str, fill, (long)val) as a virtual call through the facet's own putLong slot, as the standard specifies. A derived facet's do_put(long) override is therefore reached, as in the narrow NumPutBool. With boolalpha it writes truename() or falsename() of numpunct<wchar_t> (16-bit strings, inline or heap-stored) through the same PutField.

Layouts added to prx/libc/include/GuestLocale.hpp, each with a static_assert:

  • WideStreambuf: putNext is at 0x40 and putEnd at 0x60, as in the narrow Streambuf. overflow(int_type) is vtable slot 0x20. The writer stores into the put area while putEnd - putNext (in 16-bit units) is positive. Otherwise it calls overflow with the zero-extended code unit (char_traits<wchar_t>::to_int_type) and fails the iterator on -1 (WEOF). A null buffer also fails the iterator. This is the narrow PutCharacter rule.
  • WideOutputIterator is {bool failed; WideStreambuf* buffer} (0x10 bytes), the same layout as the narrow OutputIterator.
  • WideNumPutVtable: putBool is at 0x20, putLong at 0x28 and putPointer at 0x58. The order and parameter types come from the Itanium-mangled do_put names and match the narrow NumPutVtable.
  • WideCtypeVtable: widen(char) is at 0x60 and widen(const char*, const char*, wchar_t*) at 0x68. ctype<wchar_t> declares 12 virtuals (do_is x2, do_scan_is, do_scan_not, do_tolower x2, do_toupper x2, do_widen x2, do_narrow x2). After the 2 header slots and 4 facet slots that gives the 18-slot _ZTVSt5ctypeIwE that libc already exports. do_widen is the ninth and tenth virtual whatever the order of the first eight. The narrow CtypeVtable gets widenRange at 0x48 by the same rule. If the stream's locale has no ctype<wchar_t> facet, the digits are zero-extended, as the narrow facet copies them unchanged when there is no ctype<char>.

libc reaches libSceLibcInternal through five extern "C" _nid_no_patch helpers declared in GuestLocale.hpp, as #14373 exposes libc's numpunct members through a libc header. The _nid_no_patch suffix keeps them out of the NID tables, so this PR adds no libc export. The anonymous-namespace WideNumPutBool is an APS5_VABI function, so tools/progress.py counts it as a libc done name.

Changes:

  • New file core/libs/prx/libSceLibcInternal/WideNumPut.cpp with the five exports. The five stubs are removed from Err.cpp, and the entry points are declared in prx/libSceLibcInternal/include/Api.hpp.
  • core/libs/prx/libc/src/LocaleSupport.cpp: the shared stages above, the wide writer, StreamWideNumpunct, WideWiden, TakeWideString, and the five helpers. The narrow num_put<char> output does not change; guest_locale still passes.
  • New guest test guest_libc_internal_wide_num_put (core/libs/tests/GuestLibcInternalWideNumPut.cpp). It covers:
    • every integer overload on the classic locale: showpos with internal fill, uppercase hex with showbase, octal with and without showbase, left and right adjustment, a non-ASCII fill (U+3000), LLONG_MIN, and ULONG_MAX in octal;
    • boolalpha true and false; numeric bool through the base do_put(long); a derived facet whose do_put(long) override is reached from the base do_put(bool) while the base do_put(long) ignores it;
    • a stream locale with its own numpunct<wchar_t>: grouping "\3\2" with U+2009 as the separator, hex grouping after the 0x prefix, an ungrouped short number, and a non-ASCII truename. The heap-stored 15-character falsename is read and freed;
    • a stream locale with its own ctype<wchar_t> whose do_widen range maps digits to fullwidth forms, which shows that the slot at 0x68 is used;
    • a two-unit put area that overflows into overflow with zero-extended 16-bit units, a refused unit that fails the iterator, and a null buffer.
  • docs/dev/debt/libSceLibcInternal.md: the five "prototype and behavior are unverified" lines are removed. One entry is added. It says these members share the gaps of libc's narrow facet, describes the ctype<wchar_t> slot and the zero-extension when that facet is missing, and says the console's libSceLibcInternal.sprx was not read.

tools/progress.py collect_libraries(), run on a git archive of origin/main b11c7bf84f and on this branch:

  • origin/main: 9921 done of 37146 (libSceLibcInternal 92 done and 1045 todo; libc 1628 done and 19 todo).
  • This branch: 9927 done of 37147 (libSceLibcInternal 97 done and 1040 todo; libc 1629 done and 19 todo).
  • Done goes up by 6: the five sceLibcInternalWideNumPutDoPut*_nid_no_patch entry points and the libc APS5_VABI helper WideNumPutBool. The five todo names removed are exactly sceLibcInternalPending3_144, 147, 148, 149 and 150. No new todo appears.

Tested

Only the focused test targets and their libraries were built (guest_libc_internal_wide_num_put_tests and guest_locale_tests), on head e364f9c9db on top of origin/main b11c7bf84f. No title was run.

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

…er and bool members

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the open PR limit Closed over the open PR limit: 1 without merged PRs, 20 with at least 1, none from 20 label Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

Thank you for your contribution! Authors with no pull requests merged into main can have 1 open pull request at a time, authors with at least 1 merged can have 20, and from 20 merged there is no limit. This PR has been automatically closed and labeled "open PR limit" because that limit was reached. This is not a judgment of the quality of your changes: reopen it once one of your open pull requests is merged or closed.

@github-actions github-actions Bot closed this Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

Progress report (b11c7bf → e364f9c)

📈 System libraries: 26.72% (+0.01%, +6 implemented, -5 removed functions)

✅ 6 implemented
Library Name
libSceLibcInternal sceLibcInternalWideNumPutDoPutBool_nid_no_patch
libSceLibcInternal sceLibcInternalWideNumPutDoPutLongLong_nid_no_patch
libSceLibcInternal sceLibcInternalWideNumPutDoPutLong_nid_no_patch
libSceLibcInternal sceLibcInternalWideNumPutDoPutUnsignedLongLong_nid_no_patch
libSceLibcInternal sceLibcInternalWideNumPutDoPutUnsignedLong_nid_no_patch
libc WideNumPutBool
🗑️ 5 removed
Library Name
libSceLibcInternal sceLibcInternalPending3_144_nid_no_patch
libSceLibcInternal sceLibcInternalPending3_147_nid_no_patch
libSceLibcInternal sceLibcInternalPending3_148_nid_no_patch
libSceLibcInternal sceLibcInternalPending3_149_nid_no_patch
libSceLibcInternal sceLibcInternalPending3_150_nid_no_patch

Changed libraries (implemented exports)

Library Before After Change
libc 1628/1647 (98.85%) 1629/1648 (98.85%) +0.0007%
libSceLibcInternal 92/1137 (8.09%) 97/1137 (8.53%) +0.44%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

open PR limit Closed over the open PR limit: 1 without merged PRs, 20 with at least 1, none from 20

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant