@@ -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,107 @@ _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 __has_include(<unistd.h>)
226+ ::flockfile (__stream);
227+ # else
228+ # error "Provide a way to do unlocked stream I/O operations"
229+ # endif
230+ }
231+ _LIBCPP_HIDE_FROM_ABI inline void __funlockfile (FILE* __stream) {
232+ # if defined(_LIBCPP_WIN32API)
233+ ::_unlock_file (__stream);
234+ # elif __has_include(<unistd.h>)
235+ ::funlockfile (__stream);
236+ # else
237+ # error "Provide a way to do unlocked stream I/O operations"
238+ # endif
239+ }
240+
241+ _LIBCPP_HIDE_FROM_ABI inline int __fflush_unlocked (FILE* __stream) {
242+ # if defined(_LIBCPP_WIN32API)
243+ return ::_fflush_nolock (__stream);
244+ # elif __has_include(<unistd.h>)
245+ return ::fflush_unlocked (__stream);
246+ # else
247+ # error "Provide a way to do unlocked stream I/O operations"
248+ # endif
249+ }
250+
251+ _LIBCPP_HIDE_FROM_ABI inline size_t __fwrite_unlocked (const void * __buffer, size_t __size, size_t __n, FILE* __stream) {
252+ # if defined(_LIBCPP_WIN32API)
253+ return ::_fwrite_nolock (__buffer, __size, __n, __stream);
254+ # elif __has_include(<unistd.h>)
255+ return ::fwrite_unlocked (__buffer, __size, __n, __stream);
256+ # else
257+ # error "Provide a way to do unlocked stream I/O operations"
258+ # endif
259+ }
260+
261+ // This "buffer" is not a typical buffer but an adaptor for FILE*
262+ //
263+ // This adaptor locks the file stream, allowing it to use unlocked I/O.
264+ // This is used by the *_buffered functions in <print>. The print functions have
265+ // no wchar_t support so char is hard-coded. Since the underlaying I/O functions
266+ // encode narrow or wide in their name this avoids some `if constexpr` branches.
267+ //
268+ // The underlying functions for unlocked I/O are not in the C Standard, and
269+ // their names differ between POSIX and Windows, therefore the functions are
270+ // wrapped in this class.
271+ class __file_stream_buffer : public __format ::__output_buffer<char > {
272+ public:
273+ using value_type = char ;
274+
275+ __file_stream_buffer (const __file_stream_buffer&) = delete ;
276+ __file_stream_buffer operator =(const __file_stream_buffer&) = delete ;
277+
278+ _LIBCPP_HIDE_FROM_ABI explicit __file_stream_buffer (FILE* __stream)
279+ : __output_buffer<char>{__small_buffer_, __buffer_size, __prepare_write, nullptr }, __stream_(__stream) {
280+ __print::__flockfile (__stream_);
281+ }
282+
283+ _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer () { __print::__funlockfile (__stream_); }
284+
285+ // In order to ensure all data is written this function needs to be called.
286+ //
287+ // The class wraps C based APIs that never throw. However the Standard
288+ // requires exceptions to be throw when a write operation fails. Therefore
289+ // this function should be called before the class is destroyed.
290+ _LIBCPP_HIDE_FROM_ABI void __write_internal_buffer () && { __write_buffer (); }
291+
292+ private:
293+ FILE* __stream_;
294+
295+ // This class uses a fixed size buffer and appends the elements in
296+ // __buffer_size chunks. An alternative would be to use an allocating buffer
297+ // and append the output in a single write operation. Benchmarking showed no
298+ // performance difference.
299+ static constexpr size_t __buffer_size = 256 ;
300+ char __small_buffer_[__buffer_size];
301+
302+ _LIBCPP_HIDE_FROM_ABI void __write_buffer () {
303+ size_t __n = this ->__size ();
304+ size_t __size = __print::__fwrite_unlocked (__small_buffer_, 1 , __n, __stream_);
305+ if (__size < __n) {
306+ if (std::feof (__stream_))
307+ std::__throw_system_error (EIO, " EOF while writing the formatted output" );
308+ std::__throw_system_error (std::ferror (__stream_), " failed to write formatted output" );
309+ }
310+ }
311+
312+ _LIBCPP_HIDE_FROM_ABI void __prepare_write () {
313+ __write_buffer ();
314+ this ->__buffer_flushed ();
315+ }
316+
317+ _LIBCPP_HIDE_FROM_ABI static void
318+ __prepare_write (__output_buffer<char >& __buffer, [[maybe_unused]] size_t __size_hint) {
319+ static_cast <__file_stream_buffer&>(__buffer).__prepare_write ();
320+ }
321+ };
322+
216323template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
217324_LIBCPP_HIDE_FROM_ABI inline void
218325__vprint_nonunicode (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
@@ -229,6 +336,26 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool
229336 }
230337}
231338
339+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
340+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered (
341+ __print::__file_stream_buffer& __buffer, string_view __fmt, format_args __args, bool __write_nl) {
342+ std::__format::__vformat_to (basic_format_parse_context{__fmt, __args.__size ()},
343+ std::__format_context_create (__buffer.__make_output_iterator (), __args));
344+ if (__write_nl)
345+ __buffer.push_back (' \n ' );
346+ }
347+
348+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
349+ _LIBCPP_HIDE_FROM_ABI inline void
350+ __vprint_nonunicode_buffered (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
351+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
352+ __print::__file_stream_buffer __buffer (__stream);
353+
354+ __print::__vprint_nonunicode_buffered (__buffer, __fmt, __args, __write_nl);
355+
356+ std::move (__buffer).__write_internal_buffer ();
357+ }
358+
232359# if _LIBCPP_HAS_UNICODE
233360
234361// Note these helper functions are mainly used to aid testing.
@@ -246,10 +373,27 @@ __vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bo
246373 __print::__vprint_nonunicode (__stream, __fmt, __args, __write_nl);
247374}
248375
376+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
377+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_posix (
378+ FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
379+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
380+ __print::__file_stream_buffer __buffer (__stream);
381+
382+ // TODO PRINT Should flush errors throw too?
383+ if (__is_terminal)
384+ __print::__fflush_unlocked (__stream);
385+
386+ __print::__vprint_nonunicode_buffered (__buffer, __fmt, __args, __write_nl);
387+
388+ std::move (__buffer).__write_internal_buffer ();
389+ }
249390# if _LIBCPP_HAS_WIDE_CHARACTERS
391+
250392template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
251393_LIBCPP_HIDE_FROM_ABI inline void
252394__vprint_unicode_windows (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
395+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
396+
253397 if (!__is_terminal)
254398 return __print::__vprint_nonunicode (__stream, __fmt, __args, __write_nl);
255399
@@ -284,6 +428,49 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
284428 " __write_to_windows_console is not available." );
285429# endif
286430}
431+
432+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
433+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_windows (
434+ FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
435+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
436+
437+ if (!__is_terminal)
438+ return __print::__vprint_nonunicode_buffered (__stream, __fmt, __args, __write_nl);
439+
440+ [[maybe_unused]] __print::__file_stream_buffer __unused (__stream);
441+
442+ // TODO PRINT Should flush errors throw too?
443+ __print::__fflush_unlocked (__stream);
444+
445+ string __str = std::vformat (__fmt, __args);
446+ // UTF-16 uses the same number or less code units than UTF-8.
447+ // However the size of the code unit is 16 bits instead of 8 bits.
448+ //
449+ // The buffer uses the worst-case estimate and should never resize.
450+ // However when the string is large this could lead to OOM. Using a
451+ // smaller size might work, but since the buffer uses a grow factor
452+ // the final size might be larger when the estimate is wrong.
453+ //
454+ // TODO PRINT profile and improve the speed of this code.
455+ __format::__retarget_buffer<wchar_t > __buffer{__str.size ()};
456+ __unicode::__transcode (__str.begin (), __str.end (), __buffer.__make_output_iterator ());
457+ if (__write_nl)
458+ __buffer.push_back (L' \n ' );
459+
460+ [[maybe_unused]] wstring_view __view = __buffer.__view ();
461+
462+ // The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
463+ // the behavior in the test. This is not part of the public API.
464+ # ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
465+ _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION (__stream, __view);
466+ # elif defined(_LIBCPP_WIN32API)
467+ std::__write_to_windows_console (__stream, __view);
468+ # else
469+ std::__throw_runtime_error (" No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
470+ " __write_to_windows_console is not available." );
471+ # endif
472+ }
473+
287474# endif // _LIBCPP_HAS_WIDE_CHARACTERS
288475
289476template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -324,20 +511,47 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
324511# endif
325512}
326513
514+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
515+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered (
516+ [[maybe_unused]] FILE* __stream,
517+ [[maybe_unused]] string_view __fmt,
518+ [[maybe_unused]] format_args __args,
519+ [[maybe_unused]] bool __write_nl) {
520+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
521+
522+ # ifndef _LIBCPP_WIN32API
523+ __print::__vprint_unicode_buffered_posix (__stream, __fmt, __args, __write_nl, __print::__is_terminal (__stream));
524+ # elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
525+ __print::__vprint_unicode_buffered_windows (__stream, __fmt, __args, __write_nl, __print::__is_terminal (__stream));
526+ # else
527+ # error "Windows builds with wchar_t disabled are not supported."
528+ # endif
529+ }
530+
327531# endif // _LIBCPP_HAS_UNICODE
328532
329533} // namespace __print
330534
331535template <class ... _Args>
332536_LIBCPP_HIDE_FROM_ABI void print (FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
333537# 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 );
538+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
338539# else // _LIBCPP_HAS_UNICODE
339- __print::__vprint_nonunicode (__stream, __fmt. get (), std::make_format_args (__args...), false ) ;
540+ constexpr bool __use_unicode = false ;
340541# endif // _LIBCPP_HAS_UNICODE
542+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t <_Args>> && ...);
543+
544+ if constexpr (__use_unicode) {
545+ if constexpr (__locksafe)
546+ __print::__vprint_unicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), false );
547+ else
548+ __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
549+ } else {
550+ if constexpr (__locksafe)
551+ __print::__vprint_nonunicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), false );
552+ else
553+ __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
554+ }
341555}
342556
343557template <class ... _Args>
@@ -348,16 +562,26 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
348562template <class ... _Args>
349563_LIBCPP_HIDE_FROM_ABI void println (FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
350564# if _LIBCPP_HAS_UNICODE
565+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
566+ # else // _LIBCPP_HAS_UNICODE
567+ constexpr bool __use_unicode = false ;
568+ # endif // _LIBCPP_HAS_UNICODE
569+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t <_Args>> && ...);
570+
351571 // Note the wording in the Standard is inefficient. The output of
352572 // std::format is a std::string which is then copied. This solution
353573 // 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
574+ if constexpr (__use_unicode) {
575+ if constexpr (__locksafe)
576+ __print::__vprint_unicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), true );
577+ else
578+ __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
579+ } else {
580+ if constexpr (__locksafe)
581+ __print::__vprint_nonunicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), true );
582+ else
583+ __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
584+ }
361585}
362586
363587template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -381,6 +605,11 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __f
381605 __print::__vprint_unicode (__stream, __fmt, __args, false );
382606}
383607
608+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
609+ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode_buffered (FILE* __stream, string_view __fmt, format_args __args) {
610+ __print::__vprint_unicode_buffered (__stream, __fmt, __args, false );
611+ }
612+
384613template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
385614_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode (string_view __fmt, format_args __args) {
386615 std::vprint_unicode (stdout, __fmt, __args);
@@ -402,6 +631,8 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(string_view __fmt, format_ar
402631
403632_LIBCPP_END_NAMESPACE_STD
404633
634+ _LIBCPP_POP_MACROS
635+
405636#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
406637
407638#endif // _LIBCPP_PRINT
0 commit comments