@@ -27,9 +27,11 @@ namespace std {
2727
2828 void vprint_unicode(string_view fmt, format_args args);
2929 void vprint_unicode(FILE* stream, string_view fmt, format_args args);
30+ void vprint_unicode_buffered(FILE* stream, string_view fmt, format_args args);
3031
3132 void vprint_nonunicode(string_view fmt, format_args args);
3233 void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);
34+ void vprint_nonunicode_buffered(FILE* stream, string_view fmt, format_args args);
3335}
3436*/
3537
@@ -213,6 +215,107 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
213215# endif
214216}
215217
218+ _LIBCPP_HIDE_FROM_ABI inline void __flockfile (FILE* __stream) {
219+ # if defined(_LIBCPP_WIN32API)
220+ ::_lock_file (__stream);
221+ # elif __has_include(<unistd.h>)
222+ ::flockfile (__stream);
223+ # else
224+ # error "Provide a way to do unlocked stream I/O operations"
225+ # endif
226+ }
227+ _LIBCPP_HIDE_FROM_ABI inline void __funlockfile (FILE* __stream) {
228+ # if defined(_LIBCPP_WIN32API)
229+ ::_unlock_file (__stream);
230+ # elif __has_include(<unistd.h>)
231+ ::funlockfile (__stream);
232+ # else
233+ # error "Provide a way to do unlocked stream I/O operations"
234+ # endif
235+ }
236+
237+ _LIBCPP_HIDE_FROM_ABI inline int __fflush_unlocked (FILE* __stream) {
238+ # if defined(_LIBCPP_WIN32API)
239+ return ::_fflush_nolock (__stream);
240+ # elif __has_include(<unistd.h>)
241+ return ::fflush_unlocked (__stream);
242+ # else
243+ # error "Provide a way to do unlocked stream I/O operations"
244+ # endif
245+ }
246+
247+ _LIBCPP_HIDE_FROM_ABI inline size_t __fwrite_unlocked (const void * __buffer, size_t __size, size_t __n, FILE* __stream) {
248+ # if defined(_LIBCPP_WIN32API)
249+ return ::_fwrite_nolock (__buffer, __size, __n, __stream);
250+ # elif __has_include(<unistd.h>)
251+ return ::fwrite_unlocked (__buffer, __size, __n, __stream);
252+ # else
253+ # error "Provide a way to do unlocked stream I/O operations"
254+ # endif
255+ }
256+
257+ // This "buffer" is not a typical buffer but an adaptor for FILE*
258+ //
259+ // This adaptor locks the file stream, allowing it to use unlocked I/O.
260+ // This is used by the *_buffered functions in <print>. The print functions have
261+ // no wchar_t support so char is hard-coded. Since the underlaying I/O functions
262+ // encode narrow or wide in their name this avoids some `if constexpr` branches.
263+ //
264+ // The underlying functions for unlocked I/O are not in the C Standard, and
265+ // their names differ between POSIX and Windows, therefore the functions are
266+ // wrapped in this class.
267+ class __file_stream_buffer : public __format ::__output_buffer<char > {
268+ public:
269+ using value_type = char ;
270+
271+ __file_stream_buffer (const __file_stream_buffer&) = delete ;
272+ __file_stream_buffer operator =(const __file_stream_buffer&) = delete ;
273+
274+ _LIBCPP_HIDE_FROM_ABI explicit __file_stream_buffer (FILE* __stream)
275+ : __output_buffer<char>{__small_buffer_, __buffer_size, __prepare_write, nullptr }, __stream_(__stream) {
276+ __print::__flockfile (__stream_);
277+ }
278+
279+ _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer () { __print::__funlockfile (__stream_); }
280+
281+ // In order to ensure all data is written this function needs to be called.
282+ //
283+ // The class wraps C based APIs that never throw. However the Standard
284+ // requires exceptions to be throw when a write operation fails. Therefore
285+ // this function should be called before the class is destroyed.
286+ _LIBCPP_HIDE_FROM_ABI void __write_internal_buffer () && { __write_buffer (); }
287+
288+ private:
289+ FILE* __stream_;
290+
291+ // This class uses a fixed size buffer and appends the elements in
292+ // __buffer_size chunks. An alternative would be to use an allocating buffer
293+ // and append the output in a single write operation. Benchmarking showed no
294+ // performance difference.
295+ static constexpr size_t __buffer_size = 256 ;
296+ char __small_buffer_[__buffer_size];
297+
298+ _LIBCPP_HIDE_FROM_ABI void __write_buffer () {
299+ size_t __n = this ->__size ();
300+ size_t __size = __print::__fwrite_unlocked (__small_buffer_, 1 , __n, __stream_);
301+ if (__size < __n) {
302+ if (std::feof (__stream_))
303+ std::__throw_system_error (EIO, " EOF while writing the formatted output" );
304+ std::__throw_system_error (std::ferror (__stream_), " failed to write formatted output" );
305+ }
306+ }
307+
308+ _LIBCPP_HIDE_FROM_ABI void __prepare_write () {
309+ __write_buffer ();
310+ this ->__buffer_flushed ();
311+ }
312+
313+ _LIBCPP_HIDE_FROM_ABI static void
314+ __prepare_write (__output_buffer<char >& __buffer, [[maybe_unused]] size_t __size_hint) {
315+ static_cast <__file_stream_buffer&>(__buffer).__prepare_write ();
316+ }
317+ };
318+
216319template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
217320_LIBCPP_HIDE_FROM_ABI inline void
218321__vprint_nonunicode (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
@@ -229,6 +332,26 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool
229332 }
230333}
231334
335+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
336+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered (
337+ __print::__file_stream_buffer& __buffer, string_view __fmt, format_args __args, bool __write_nl) {
338+ std::__format::__vformat_to (basic_format_parse_context{__fmt, __args.__size ()},
339+ std::__format_context_create (__buffer.__make_output_iterator (), __args));
340+ if (__write_nl)
341+ __buffer.push_back (' \n ' );
342+ }
343+
344+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
345+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered (
346+ FILE* __stream , string_view __fmt, format_args __args, bool __write_nl) {
347+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
348+ __print::__file_stream_buffer __buffer (__stream);
349+
350+ __print::__vprint_nonunicode_buffered (__buffer, __fmt, __args, __write_nl);
351+
352+ std::move (__buffer).__write_internal_buffer ();
353+ }
354+
232355# if _LIBCPP_HAS_UNICODE
233356
234357// Note these helper functions are mainly used to aid testing.
@@ -246,10 +369,27 @@ __vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bo
246369 __print::__vprint_nonunicode (__stream, __fmt, __args, __write_nl);
247370}
248371
372+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
373+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_posix (
374+ FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
375+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
376+ __print::__file_stream_buffer __buffer (__stream);
377+
378+ // TODO PRINT Should flush errors throw too?
379+ if (__is_terminal)
380+ __print::__fflush_unlocked (__stream);
381+
382+ __print::__vprint_nonunicode_buffered (__buffer, __fmt, __args, __write_nl);
383+
384+ std::move (__buffer).__write_internal_buffer ();
385+ }
249386# if _LIBCPP_HAS_WIDE_CHARACTERS
387+
250388template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
251389_LIBCPP_HIDE_FROM_ABI inline void
252390__vprint_unicode_windows (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
391+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
392+
253393 if (!__is_terminal)
254394 return __print::__vprint_nonunicode (__stream, __fmt, __args, __write_nl);
255395
@@ -284,6 +424,49 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
284424 " __write_to_windows_console is not available." );
285425# endif
286426}
427+
428+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
429+ _LIBCPP_HIDE_FROM_ABI inline void
430+ __vprint_unicode_buffered_windows (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
431+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
432+
433+ if (!__is_terminal)
434+ return __print::__vprint_nonunicode_buffered (__stream, __fmt, __args, __write_nl);
435+
436+ [[maybe_unused]] __print::__file_stream_buffer __unused (__stream);
437+
438+ // TODO PRINT Should flush errors throw too?
439+ __print::__fflush_unlocked (__stream);
440+
441+ string __str = std::vformat (__fmt, __args);
442+ // UTF-16 uses the same number or less code units than UTF-8.
443+ // However the size of the code unit is 16 bits instead of 8 bits.
444+ //
445+ // The buffer uses the worst-case estimate and should never resize.
446+ // However when the string is large this could lead to OOM. Using a
447+ // smaller size might work, but since the buffer uses a grow factor
448+ // the final size might be larger when the estimate is wrong.
449+ //
450+ // TODO PRINT profile and improve the speed of this code.
451+ __format::__retarget_buffer<wchar_t > __buffer{__str.size ()};
452+ __unicode::__transcode (__str.begin (), __str.end (), __buffer.__make_output_iterator ());
453+ if (__write_nl)
454+ __buffer.push_back (L' \n ' );
455+
456+ [[maybe_unused]] wstring_view __view = __buffer.__view ();
457+
458+ // The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
459+ // the behavior in the test. This is not part of the public API.
460+ # ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
461+ _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION (__stream, __view);
462+ # elif defined(_LIBCPP_WIN32API)
463+ std::__write_to_windows_console (__stream, __view);
464+ # else
465+ std::__throw_runtime_error (" No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
466+ " __write_to_windows_console is not available." );
467+ # endif
468+ }
469+
287470# endif // _LIBCPP_HAS_WIDE_CHARACTERS
288471
289472template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -324,20 +507,47 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
324507# endif
325508}
326509
510+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
511+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered (
512+ [[maybe_unused]] FILE* __stream,
513+ [[maybe_unused]] string_view __fmt,
514+ [[maybe_unused]] format_args __args,
515+ [[maybe_unused]] bool __write_nl) {
516+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
517+
518+ # ifndef _LIBCPP_WIN32API
519+ __print::__vprint_unicode_buffered_posix (__stream, __fmt, __args, __write_nl, __print::__is_terminal (__stream));
520+ # elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
521+ __print::__vprint_unicode_buffered_windows (__stream, __fmt, __args, __write_nl, __print::__is_terminal (__stream));
522+ # else
523+ # error "Windows builds with wchar_t disabled are not supported."
524+ # endif
525+ }
526+
327527# endif // _LIBCPP_HAS_UNICODE
328528
329529} // namespace __print
330530
331531template <class ... _Args>
332532_LIBCPP_HIDE_FROM_ABI void print (FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
333533# if _LIBCPP_HAS_UNICODE
334- if constexpr (__print::__use_unicode_execution_charset)
335- __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
336- else
337- __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
534+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
338535# else // _LIBCPP_HAS_UNICODE
339- __print::__vprint_nonunicode (__stream, __fmt. get (), std::make_format_args (__args...), false ) ;
536+ constexpr bool __use_unicode = false ;
340537# endif // _LIBCPP_HAS_UNICODE
538+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t <_Args>> && ...);
539+
540+ if constexpr (__use_unicode) {
541+ if constexpr (__locksafe)
542+ __print::__vprint_unicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), false );
543+ else
544+ __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
545+ } else {
546+ if constexpr (__locksafe)
547+ __print::__vprint_nonunicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), false );
548+ else
549+ __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
550+ }
341551}
342552
343553template <class ... _Args>
@@ -348,16 +558,26 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
348558template <class ... _Args>
349559_LIBCPP_HIDE_FROM_ABI void println (FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
350560# if _LIBCPP_HAS_UNICODE
561+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
562+ # else // _LIBCPP_HAS_UNICODE
563+ constexpr bool __use_unicode = false ;
564+ # endif // _LIBCPP_HAS_UNICODE
565+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t <_Args>> && ...);
566+
351567 // Note the wording in the Standard is inefficient. The output of
352568 // std::format is a std::string which is then copied. This solution
353569 // just appends a newline at the end of the output.
354- if constexpr (__print::__use_unicode_execution_charset)
355- __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
356- else
357- __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
358- # else // _LIBCPP_HAS_UNICODE
359- __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
360- # endif // _LIBCPP_HAS_UNICODE
570+ if constexpr (__use_unicode) {
571+ if constexpr (__locksafe)
572+ __print::__vprint_unicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), true );
573+ else
574+ __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
575+ } else {
576+ if constexpr (__locksafe)
577+ __print::__vprint_nonunicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), true );
578+ else
579+ __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
580+ }
361581}
362582
363583template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -381,6 +601,11 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __f
381601 __print::__vprint_unicode (__stream, __fmt, __args, false );
382602}
383603
604+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
605+ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode_buffered (FILE* __stream, string_view __fmt, format_args __args) {
606+ __print::__vprint_unicode_buffered (__stream, __fmt, __args, false );
607+ }
608+
384609template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
385610_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode (string_view __fmt, format_args __args) {
386611 std::vprint_unicode (stdout, __fmt, __args);
0 commit comments