@@ -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
@@ -41,6 +43,7 @@ namespace std {
4143# include < __config>
4244# include < __system_error/throw_system_error.h>
4345# include < __utility/forward.h>
46+ # include < __utility/move.h>
4447# include < cerrno>
4548# include < cstdio>
4649# include < format>
@@ -52,6 +55,9 @@ namespace std {
5255# pragma GCC system_header
5356# endif
5457
58+ _LIBCPP_PUSH_MACROS
59+ # include < __undef_macros>
60+
5561_LIBCPP_BEGIN_NAMESPACE_STD
5662
5763# ifdef _LIBCPP_WIN32API
@@ -213,6 +219,115 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
213219# endif
214220}
215221
222+ _LIBCPP_HIDE_FROM_ABI inline void __flockfile (FILE* __stream) {
223+ # if defined(_LIBCPP_WIN32API)
224+ ::_lock_file (__stream);
225+ # elif defined(__PICOLIBC__) || defined(_AIX)
226+ ::__flockfile (__stream);
227+ # elif __has_include(<unistd.h>)
228+ ::flockfile (__stream);
229+ # else
230+ # error "Provide a way to do unlocked stream I/O operations"
231+ # endif
232+ }
233+ _LIBCPP_HIDE_FROM_ABI inline void __funlockfile (FILE* __stream) {
234+ # if defined(_LIBCPP_WIN32API)
235+ ::_unlock_file (__stream);
236+ # elif defined(__PICOLIBC__) || defined(_AIX)
237+ ::__funlockfile (__stream);
238+ # elif __has_include(<unistd.h>)
239+ ::funlockfile (__stream);
240+ # else
241+ # error "Provide a way to do unlocked stream I/O operations"
242+ # endif
243+ }
244+
245+ _LIBCPP_HIDE_FROM_ABI inline int __fflush_unlocked (FILE* __stream) {
246+ # if defined(_LIBCPP_WIN32API)
247+ return ::_fflush_nolock (__stream);
248+ # elif defined(__PICOLIBC__) || defined(_AIX)
249+ return ::__fflush_unlocked (__stream);
250+ # elif __has_include(<unistd.h>)
251+ return ::fflush_unlocked (__stream);
252+ # else
253+ # error "Provide a way to do unlocked stream I/O operations"
254+ # endif
255+ }
256+
257+ _LIBCPP_HIDE_FROM_ABI inline size_t __fwrite_unlocked (const void * __buffer, size_t __size, size_t __n, FILE* __stream) {
258+ # if defined(_LIBCPP_WIN32API)
259+ return ::_fwrite_nolock (__buffer, __size, __n, __stream);
260+ # elif defined(__PICOLIBC__) || defined(_AIX)
261+ return ::__fwrite_unlocked (__buffer, __size, __n, __stream);
262+ # elif __has_include(<unistd.h>)
263+ return ::fwrite_unlocked (__buffer, __size, __n, __stream);
264+ # else
265+ # error "Provide a way to do unlocked stream I/O operations"
266+ # endif
267+ }
268+
269+ // This "buffer" is not a typical buffer but an adaptor for FILE*
270+ //
271+ // This adaptor locks the file stream, allowing it to use unlocked I/O.
272+ // This is used by the *_buffered functions in <print>. The print functions have
273+ // no wchar_t support so char is hard-coded. Since the underlaying I/O functions
274+ // encode narrow or wide in their name this avoids some `if constexpr` branches.
275+ //
276+ // The underlying functions for unlocked I/O are not in the C Standard, and
277+ // their names differ between POSIX and Windows, therefore the functions are
278+ // wrapped in this class.
279+ class __file_stream_buffer : public __format ::__output_buffer<char > {
280+ public:
281+ using value_type = char ;
282+
283+ __file_stream_buffer (const __file_stream_buffer&) = delete ;
284+ __file_stream_buffer operator =(const __file_stream_buffer&) = delete ;
285+
286+ _LIBCPP_HIDE_FROM_ABI explicit __file_stream_buffer (FILE* __stream)
287+ : __output_buffer<char>{__small_buffer_, __buffer_size, __prepare_write, nullptr }, __stream_(__stream) {
288+ __print::__flockfile (__stream_);
289+ }
290+
291+ _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer () { __print::__funlockfile (__stream_); }
292+
293+ // In order to ensure all data is written this function needs to be called.
294+ //
295+ // The class wraps C based APIs that never throw. However the Standard
296+ // requires exceptions to be throw when a write operation fails. Therefore
297+ // this function should be called before the class is destroyed.
298+ _LIBCPP_HIDE_FROM_ABI void __write_internal_buffer () && { __write_buffer (); }
299+
300+ private:
301+ FILE* __stream_;
302+
303+ // This class uses a fixed size buffer and appends the elements in
304+ // __buffer_size chunks. An alternative would be to use an allocating buffer
305+ // and append the output in a single write operation. Benchmarking showed no
306+ // performance difference.
307+ static constexpr size_t __buffer_size = 256 ;
308+ char __small_buffer_[__buffer_size];
309+
310+ _LIBCPP_HIDE_FROM_ABI void __write_buffer () {
311+ size_t __n = this ->__size ();
312+ size_t __size = __print::__fwrite_unlocked (__small_buffer_, 1 , __n, __stream_);
313+ if (__size < __n) {
314+ if (std::feof (__stream_))
315+ std::__throw_system_error (EIO, " EOF while writing the formatted output" );
316+ std::__throw_system_error (std::ferror (__stream_), " failed to write formatted output" );
317+ }
318+ }
319+
320+ _LIBCPP_HIDE_FROM_ABI void __prepare_write () {
321+ __write_buffer ();
322+ this ->__buffer_flushed ();
323+ }
324+
325+ _LIBCPP_HIDE_FROM_ABI static void
326+ __prepare_write (__output_buffer<char >& __buffer, [[maybe_unused]] size_t __size_hint) {
327+ static_cast <__file_stream_buffer&>(__buffer).__prepare_write ();
328+ }
329+ };
330+
216331template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
217332_LIBCPP_HIDE_FROM_ABI inline void
218333__vprint_nonunicode (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
@@ -229,6 +344,26 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool
229344 }
230345}
231346
347+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
348+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered (
349+ __print::__file_stream_buffer& __buffer, string_view __fmt, format_args __args, bool __write_nl) {
350+ std::__format::__vformat_to (basic_format_parse_context{__fmt, __args.__size ()},
351+ std::__format_context_create (__buffer.__make_output_iterator (), __args));
352+ if (__write_nl)
353+ __buffer.push_back (' \n ' );
354+ }
355+
356+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
357+ _LIBCPP_HIDE_FROM_ABI inline void
358+ __vprint_nonunicode_buffered (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
359+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
360+ __print::__file_stream_buffer __buffer (__stream);
361+
362+ __print::__vprint_nonunicode_buffered (__buffer, __fmt, __args, __write_nl);
363+
364+ std::move (__buffer).__write_internal_buffer ();
365+ }
366+
232367# if _LIBCPP_HAS_UNICODE
233368
234369// Note these helper functions are mainly used to aid testing.
@@ -246,10 +381,27 @@ __vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bo
246381 __print::__vprint_nonunicode (__stream, __fmt, __args, __write_nl);
247382}
248383
384+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
385+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_posix (
386+ FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
387+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
388+ __print::__file_stream_buffer __buffer (__stream);
389+
390+ // TODO PRINT Should flush errors throw too?
391+ if (__is_terminal)
392+ __print::__fflush_unlocked (__stream);
393+
394+ __print::__vprint_nonunicode_buffered (__buffer, __fmt, __args, __write_nl);
395+
396+ std::move (__buffer).__write_internal_buffer ();
397+ }
249398# if _LIBCPP_HAS_WIDE_CHARACTERS
399+
250400template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
251401_LIBCPP_HIDE_FROM_ABI inline void
252402__vprint_unicode_windows (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
403+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
404+
253405 if (!__is_terminal)
254406 return __print::__vprint_nonunicode (__stream, __fmt, __args, __write_nl);
255407
@@ -284,6 +436,49 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
284436 " __write_to_windows_console is not available." );
285437# endif
286438}
439+
440+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
441+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_windows (
442+ FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
443+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
444+
445+ if (!__is_terminal)
446+ return __print::__vprint_nonunicode_buffered (__stream, __fmt, __args, __write_nl);
447+
448+ [[maybe_unused]] __print::__file_stream_buffer __b (__stream);
449+
450+ // TODO PRINT Should flush errors throw too?
451+ __print::__fflush_unlocked (__stream);
452+
453+ string __str = std::vformat (__fmt, __args);
454+ // UTF-16 uses the same number or less code units than UTF-8.
455+ // However the size of the code unit is 16 bits instead of 8 bits.
456+ //
457+ // The buffer uses the worst-case estimate and should never resize.
458+ // However when the string is large this could lead to OOM. Using a
459+ // smaller size might work, but since the buffer uses a grow factor
460+ // the final size might be larger when the estimate is wrong.
461+ //
462+ // TODO PRINT profile and improve the speed of this code.
463+ __format::__retarget_buffer<wchar_t > __buffer{__str.size ()};
464+ __unicode::__transcode (__str.begin (), __str.end (), __buffer.__make_output_iterator ());
465+ if (__write_nl)
466+ __buffer.push_back (L' \n ' );
467+
468+ [[maybe_unused]] wstring_view __view = __buffer.__view ();
469+
470+ // The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
471+ // the behavior in the test. This is not part of the public API.
472+ # ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
473+ _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION (__stream, __view);
474+ # elif defined(_LIBCPP_WIN32API)
475+ std::__write_to_windows_console (__stream, __view);
476+ # else
477+ std::__throw_runtime_error (" No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
478+ " __write_to_windows_console is not available." );
479+ # endif
480+ }
481+
287482# endif // _LIBCPP_HAS_WIDE_CHARACTERS
288483
289484template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -324,20 +519,47 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
324519# endif
325520}
326521
522+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
523+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered (
524+ [[maybe_unused]] FILE* __stream,
525+ [[maybe_unused]] string_view __fmt,
526+ [[maybe_unused]] format_args __args,
527+ [[maybe_unused]] bool __write_nl) {
528+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
529+
530+ # ifndef _LIBCPP_WIN32API
531+ __print::__vprint_unicode_buffered_posix (__stream, __fmt, __args, __write_nl, __print::__is_terminal (__stream));
532+ # elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
533+ __print::__vprint_unicode_buffered_windows (__stream, __fmt, __args, __write_nl, __print::__is_terminal (__stream));
534+ # else
535+ # error "Windows builds with wchar_t disabled are not supported."
536+ # endif
537+ }
538+
327539# endif // _LIBCPP_HAS_UNICODE
328540
329541} // namespace __print
330542
331543template <class ... _Args>
332544_LIBCPP_HIDE_FROM_ABI void print (FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
333545# 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 );
546+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
338547# else // _LIBCPP_HAS_UNICODE
339- __print::__vprint_nonunicode (__stream, __fmt. get (), std::make_format_args (__args...), false ) ;
548+ constexpr bool __use_unicode = false ;
340549# endif // _LIBCPP_HAS_UNICODE
550+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t <_Args>> && ...);
551+
552+ if constexpr (__use_unicode) {
553+ if constexpr (__locksafe)
554+ __print::__vprint_unicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), false );
555+ else
556+ __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
557+ } else {
558+ if constexpr (__locksafe)
559+ __print::__vprint_nonunicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), false );
560+ else
561+ __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
562+ }
341563}
342564
343565template <class ... _Args>
@@ -348,16 +570,26 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
348570template <class ... _Args>
349571_LIBCPP_HIDE_FROM_ABI void println (FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
350572# if _LIBCPP_HAS_UNICODE
573+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
574+ # else // _LIBCPP_HAS_UNICODE
575+ constexpr bool __use_unicode = false ;
576+ # endif // _LIBCPP_HAS_UNICODE
577+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t <_Args>> && ...);
578+
351579 // Note the wording in the Standard is inefficient. The output of
352580 // std::format is a std::string which is then copied. This solution
353581 // 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
582+ if constexpr (__use_unicode) {
583+ if constexpr (__locksafe)
584+ __print::__vprint_unicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), true );
585+ else
586+ __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
587+ } else {
588+ if constexpr (__locksafe)
589+ __print::__vprint_nonunicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), true );
590+ else
591+ __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
592+ }
361593}
362594
363595template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -381,6 +613,11 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __f
381613 __print::__vprint_unicode (__stream, __fmt, __args, false );
382614}
383615
616+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
617+ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode_buffered (FILE* __stream, string_view __fmt, format_args __args) {
618+ __print::__vprint_unicode_buffered (__stream, __fmt, __args, false );
619+ }
620+
384621template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
385622_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode (string_view __fmt, format_args __args) {
386623 std::vprint_unicode (stdout, __fmt, __args);
@@ -402,6 +639,8 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(string_view __fmt, format_ar
402639
403640_LIBCPP_END_NAMESPACE_STD
404641
642+ _LIBCPP_POP_MACROS
643+
405644#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
406645
407646#endif // _LIBCPP_PRINT
0 commit comments