Skip to content

feat(libc): format time_put<char>::do_put as the console's libc does - #11044

Merged
1 commit merged into
boykopovar:mainfrom
verusferro:feat/libc-time-put-do-put
Oct 26, 2026
Merged

1 commit merged into
boykopovar:mainfrom
verusferro:feat/libc-time-put-do-put

Conversation

@verusferro

@verusferro verusferro commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

time_put<char>::do_put threw (TimePutMethodUnsupported), so a title that formats a std::tm through a stream (std::put_time, time_put::put) stopped there. It now does what the SDK libc.prx that PPSA06416 ships in sce_module/ (title SDK 7.00) does, read from its disassembly (REVERSE_ENGINEERING, source 2). The module exports it as +i81FtUCarA (0x9aa00):

  • It builds the format !% followed by the modifier and the conversion, or the conversion alone when the modifier is 0, and runs _Strftime on it, the same function strftime calls. It starts with a 16-byte buffer and doubles it until the result fits; the leading ! tells an empty result from one that did not fit, and is dropped.
  • It writes each remaining character through the output iterator the way the inline sputc does. When the stream buffer's put pointer (*(buffer + 0x40)) is set and (int)(*(buffer + 0x60) - next) > 0, it stores the character and advances the pointer. Otherwise it calls overflow, vtable slot 4, and marks the iterator failed when that returns eof. A null buffer marks the iterator failed. A failed iterator still offers the rest of the characters, and the stream and fill arguments are not read.

The facet gets a vtable with its single do_put slot instead of eight throwing slots. The formatting goes through this library's strftime, so its gaps carry over. GuestLocale.hpp describes the three guest types this touches: the stream buffer's put pointers, its overflow slot and the 16-byte iterator, which arrives in two registers and is returned in RAX:RDX.

The debt entry for these facets now says time_put formats through strftime. To keep it under 1000 characters, the std::clog, std::wclog and facet id sizes that shared it move to an entry of their own, unchanged.

Tested

Ubuntu 24.04 (WSL2) x86-64, GCC 13.3, Release, full build with BUILD_TESTING=ON:

  • The title's libc.prx, converted by the relinker and loaded in a host process, compared against this library's facet with the same fake stream buffer. That covers every conversion byte 0-255 with no modifier and with E, O, #, x, Y, 0x80 and %, each with room in the buffer, 2 bytes of room with overflow refusing every character or every other one, a null put pointer and a null buffer: 8,176 calls. Stored bytes, the order of overflow calls, the failed flag and the returned iterator match in all of them except the 12 %Z calls. There the module prints the empty zone name of a process without TZ and this library prints the host's, as the existing tzname debt entry records. A format that ends after E or O is left out: the console crashes there and strftime throws.
  • guest_locale gains CheckTimePut: a conversion that fits, one that runs into overflow part way (and needs a 32-byte buffer), E and O, a 0 conversion, an unknown conversion, overflow refusing (all 8 characters are still offered), a null put pointer and a null buffer. Emitting the ! as well fails it.
  • ctest -R '^(guest_locale|guest_strftime|patched_libraries|conventions_unknown_nids)$': 4/4 pass.
  • tools/progress.py: libc +1 implemented (TimePutDoPut), TimePutMethodUnsupported removed.
  • python3 tools/check_conventions.py --base origin/main: passed. python3 tools/pr_overlap.py: no conflicting lines or same exports; none of the 39 pull requests it could not compare touches time_put, the stream buffer types or this debt entry.

Windows: not run by me. guest_locale passed in @ycbuzi's run of 186fbf3b8f and in @wachi35676's run of this head 3338ffefff, as the parent of #11054.

Not tested: macOS, a title that calls put_time.

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

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Progress report (0119103 → 3338ffe)

➖ System libraries: 49.65% (+0.0%, +1 implemented, -1 removed functions)

✅ 1 implemented
Library Name
libc TimePutDoPut
🗑️ 1 removed
Library Name
libc TimePutMethodUnsupported

Changed libraries (implemented exports)

Library Before After Change
libc 1345/1356 (99.19%) 1346/1356 (99.26%) +0.07 pp

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 186fbf3. The guest streambuf offsets and vtable slot are statically asserted, the formatter follows the observed leading-sentinel and overflow behavior, and failed iterators still receive every output character. The focused test covers formatting, overflow rejection, null put-next, and null iterator buffer. The author also reports 8,176 comparisons against the title SDK implementation with only the documented timezone difference. Hosted conventions, intake, eligibility, relinker, sanitizer, and register-context checks pass; product jobs are skipped by path policy. I reviewed the implementation and test but did not run the full local build here.

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

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 5021 docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 6713 docs/dev/debt/libc.md
PR 7081 core/libs/prx/libc/src/LocaleSupport.cpp
docs/dev/debt/libc.md
PR 7386 docs/dev/debt/libc.md
PR 7447 docs/dev/debt/libc.md
PR 7544 docs/dev/debt/libc.md
PR 7648 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 9371 docs/dev/debt/libc.md
PR 9447 docs/dev/debt/libc.md
PR 9526 core/libs/prx/libc/src/LocaleSupport.cpp
core/libs/tests/GuestLocale.cpp
docs/dev/debt/libc.md
PR 9772 docs/dev/debt/libc.md
PR 9863 docs/dev/debt/libc.md
PR 9929 docs/dev/debt/libc.md
PR 9953 docs/dev/debt/libc.md
PR 9964 docs/dev/debt/libc.md
PR 10002 docs/dev/debt/libc.md
PR 10007 docs/dev/debt/libc.md
PR 10158 core/libs/prx/libc/src/LocaleSupport.cpp
core/libs/tests/GuestLocale.cpp
docs/dev/debt/libc.md
PR 10200 core/libs/prx/libc/src/LocaleSupport.cpp
core/libs/tests/GuestLocale.cpp
docs/dev/debt/libc.md
PR 10234 docs/dev/debt/libc.md
PR 10315 docs/dev/debt/libc.md
PR 10317 docs/dev/debt/libc.md
PR 10321 docs/dev/debt/libc.md
PR 10325 docs/dev/debt/libc.md
PR 10328 docs/dev/debt/libc.md
PR 10345 docs/dev/debt/libc.md
PR 10349 docs/dev/debt/libc.md
PR 10405 docs/dev/debt/libc.md
PR 10467 docs/dev/debt/libc.md
PR 10506 docs/dev/debt/libc.md
PR 10510 docs/dev/debt/libc.md
PR 10512 docs/dev/debt/libc.md
PR 10529 docs/dev/debt/libc.md
PR 10565 docs/dev/debt/libc.md
PR 10749 docs/dev/debt/libc.md
PR 11332 docs/dev/debt/libc.md
PR 11341 docs/dev/debt/libc.md

@ycbuzi

ycbuzi commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

A Windows run of this head: Windows 11 Home 10.0.26200, WinLibs GCC 15.2.0 r7 (MinGW-w64 UCRT, posix-seh), CMake 4.3.1, Ninja 1.13.2, Release. 186fbf3b8f merged onto main d2b3ee3bcc with no conflicts.

  • guest_locale_tests builds with no warnings or errors.
  • guest_locale passes both runs of ctest --repeat until-fail:2.
  • No negative control: the new checks use the Streambuf and StreambufVtable types this PR adds to GuestLocale.hpp, so the test doesn't build against main's header.

Not run: Linux, macOS, the comparison against the title's libc.prx.

AI-assisted: yes (Claude Code)

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I manually checked the exact heads of #9456 (159a6f44078e56a677b354da7e0d88688583a7f7) and #9468 (ef2f603255e817198618bd38a1fddd803b115ce5) against this PR (186fbf3b8f92f8c649bd869180af12887b4a8922). git merge-tree reports a content conflict only in core/libs/tests/GuestLocale.cpp; all three branches close CheckFacetGetcat and add a separate check at that insertion point. The implementation additions appear independent, and a combined result should retain one closing brace plus all three checks. Per CONTRIBUTING.md, please coordinate merge order with the other authors and declare the agreed dependency on the checklist (Depends on: #N) before this PR is considered ready. This is a merge-order issue, not a finding that the time_put implementation overlaps semantically with time_get or num_get.

The time_put facet's do_put formats % with the modifier and conversion through strftime and writes the result through the output iterator's stream buffer, following the SDK libc.prx: it stores into the put area while room remains, calls overflow otherwise, and marks the iterator failed when overflow returns eof or the buffer is null.
@verusferro
verusferro force-pushed the feat/libc-time-put-do-put branch from 186fbf3 to 3338ffe Compare October 25, 2026 16:22
@verusferro

Copy link
Copy Markdown
Contributor Author

Thanks. Instead of a merge order, I moved this PR's test off the lines #9456 and #9468 change: TestStreambuf, TestOverflow and CheckTimePut now sit before CheckFacetGetcat, and the call comes after the _Unlocksyslock lines at the end of main. git merge-tree --write-tree of the new head 3338ffefff with #9456 (159a6f4407) and with #9468 (ef2f603255) is clean each way, so neither has to merge first and the checklist keeps Depends on: none. A three-way merge still conflicts, but only because #9456 and #9468 change the same lines of GuestLocale.cpp and libc.md as each other.

The code is unchanged: the commit only moves 45 test lines, and it is now rebased onto d2b3ee3bcc, the same commit #11054 builds on. guest_locale, patched_libraries and conventions_unknown_nids pass, and so does check_conventions.py.

@wachi35676

Copy link
Copy Markdown
Contributor

Windows run, which the description lists as not tested. WinLibs GCC 15.2.0, Release, Windows 11 10.0.26300. I tested this branch as the parent of #11054, with #11054's head 1d31901c4bf7 merged onto main 90f918fa7. CheckTimePut is unchanged in that tree.

  • guest_locale passes 3/3, CheckTimePut included.
  • With GuestLocale.hpp and LocaleSupport.cpp from main, it aborts with time_put<char>::do_put not implemented.

AI-assisted: yes (Claude Code); results above were run on this machine.

@ycbuzi

ycbuzi commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

A Windows rerun of the new head 3338ffefff, merged onto main 99b8446cbc with no conflicts: Windows 11 Home 10.0.26200, WinLibs GCC 15.2.0 r7 (MinGW-w64 UCRT), Release, plain ctest --repeat until-fail:2.

  • guest_locale passes, 2 of 2. The build has no warnings.
  • No negative control: with main's GuestLocale.hpp and LocaleSupport.cpp, tests/GuestLocale.cpp doesn't build ('Streambuf' in namespace 'GuestLocale' does not name a type at :298).

AI-assisted: yes (Claude Code)

@oneandonlydean oneandonlydean closed this pull request by merging all changes into boykopovar:main in d6ac53b Oct 26, 2026
@verusferro
verusferro deleted the feat/libc-time-put-do-put branch October 28, 2026 13:07
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.

5 participants