@@ -213,6 +213,107 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
213213# endif
214214}
215215
216+ _LIBCPP_HIDE_FROM_ABI inline void __flockfile (FILE* __stream) {
217+ # if defined(_LIBCPP_WIN32API)
218+ ::_lock_file (__stream);
219+ # elif __has_include(<unistd.h>)
220+ ::flockfile (__stream);
221+ # else
222+ # error "Provide a way to do unlocked stream I/O operations"
223+ # endif
224+ }
225+ _LIBCPP_HIDE_FROM_ABI inline void __funlockfile (FILE* __stream) {
226+ # if defined(_LIBCPP_WIN32API)
227+ ::_unlock_file (__stream);
228+ # elif __has_include(<unistd.h>)
229+ ::funlockfile (__stream);
230+ # else
231+ # error "Provide a way to do unlocked stream I/O operations"
232+ # endif
233+ }
234+
235+ _LIBCPP_HIDE_FROM_ABI inline int __fflush_unlocked (FILE* __stream) {
236+ # if defined(_LIBCPP_WIN32API)
237+ return ::_fflush_nolock (__stream);
238+ # elif __has_include(<unistd.h>)
239+ return ::fflush_unlocked (__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 size_t __fwrite_unlocked (const void * __buffer, size_t __size, size_t __n, FILE* __stream) {
246+ # if defined(_LIBCPP_WIN32API)
247+ return ::_fwrite_nolock (__buffer, __size, __n, __stream);
248+ # elif __has_include(<unistd.h>)
249+ return ::fwrite_unlocked (__buffer, __size, __n, __stream);
250+ # else
251+ # error "Provide a way to do unlocked stream I/O operations"
252+ # endif
253+ }
254+
255+ // This "buffer" is not a typical buffer but an adaptor for FILE*
256+ //
257+ // This adaptor locks the file stream, allowing it to use unlocked I/O.
258+ // This is used by the *_buffered functions in <print>. The print functions have
259+ // no wchar_t support so char is hard-coded. Since the underlaying I/O functions
260+ // encode narrow or wide in their name this avoids some `if constexpr` branches.
261+ //
262+ // The underlying functions for unlocked I/O are not in the C Standard, and
263+ // their names differ between POSIX and Windows, therefore the functions are
264+ // wrapped in this class.
265+ class __file_stream_buffer : public __format ::__output_buffer<char > {
266+ public:
267+ using value_type = char ;
268+
269+ __file_stream_buffer (const __file_stream_buffer&) = delete ;
270+ __file_stream_buffer operator =(const __file_stream_buffer&) = delete ;
271+
272+ _LIBCPP_HIDE_FROM_ABI explicit __file_stream_buffer (FILE* __stream)
273+ : __output_buffer<char>{__small_buffer_, __buffer_size, __prepare_write, nullptr }, __stream_(__stream) {
274+ __print::__flockfile (__stream_);
275+ }
276+
277+ _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer () { __print::__funlockfile (__stream_); }
278+
279+ // In order to ensure all data is written this function needs to be called.
280+ //
281+ // The class wraps C based APIs that never throw. However the Standard
282+ // requires exceptions to be throw when a write operation fails. Therefore
283+ // this function should be called before the class is destroyed.
284+ _LIBCPP_HIDE_FROM_ABI void __write_internal_buffer () && { __write_buffer (); }
285+
286+ private:
287+ FILE* __stream_;
288+
289+ // This class uses a fixed size buffer and appends the elements in
290+ // __buffer_size chunks. An alternative would be to use an allocating buffer
291+ // and append the output in a single write operation. Benchmarking showed no
292+ // performance difference.
293+ static constexpr size_t __buffer_size = 256 ;
294+ char __small_buffer_[__buffer_size];
295+
296+ _LIBCPP_HIDE_FROM_ABI void __write_buffer () {
297+ size_t __n = this ->__size ();
298+ size_t __size = __print::__fwrite_unlocked (__small_buffer_, 1 , __n, __stream_);
299+ if (__size < __n) {
300+ if (std::feof (__stream_))
301+ std::__throw_system_error (EIO, " EOF while writing the formatted output" );
302+ std::__throw_system_error (std::ferror (__stream_), " failed to write formatted output" );
303+ }
304+ }
305+
306+ _LIBCPP_HIDE_FROM_ABI void __prepare_write () {
307+ __write_buffer ();
308+ this ->__buffer_flushed ();
309+ }
310+
311+ _LIBCPP_HIDE_FROM_ABI static void
312+ __prepare_write (__output_buffer<char >& __buffer, [[maybe_unused]] size_t __size_hint) {
313+ static_cast <__file_stream_buffer&>(__buffer).__prepare_write ();
314+ }
315+ };
316+
216317template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
217318_LIBCPP_HIDE_FROM_ABI inline void
218319__vprint_nonunicode (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
@@ -229,6 +330,26 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool
229330 }
230331}
231332
333+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
334+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered (
335+ __print::__file_stream_buffer& __buffer, string_view __fmt, format_args __args, bool __write_nl) {
336+ std::__format::__vformat_to (basic_format_parse_context{__fmt, __args.__size ()},
337+ std::__format_context_create (__buffer.__make_output_iterator (), __args));
338+ if (__write_nl)
339+ __buffer.push_back (' \n ' );
340+ }
341+
342+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
343+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered (
344+ FILE* __stream , string_view __fmt, format_args __args, bool __write_nl) {
345+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
346+ __print::__file_stream_buffer __buffer (__stream);
347+
348+ __print::__vprint_nonunicode_buffered (__buffer, __fmt, __args, __write_nl);
349+
350+ std::move (__buffer).__write_internal_buffer ();
351+ }
352+
232353# if _LIBCPP_HAS_UNICODE
233354
234355// Note these helper functions are mainly used to aid testing.
@@ -246,10 +367,27 @@ __vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bo
246367 __print::__vprint_nonunicode (__stream, __fmt, __args, __write_nl);
247368}
248369
370+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
371+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_posix (
372+ FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
373+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
374+ __print::__file_stream_buffer __buffer (__stream);
375+
376+ // TODO PRINT Should flush errors throw too?
377+ if (__is_terminal)
378+ __print::__fflush_unlocked (__stream);
379+
380+ __print::__vprint_nonunicode_buffered (__buffer, __fmt, __args, __write_nl);
381+
382+ std::move (__buffer).__write_internal_buffer ();
383+ }
249384# if _LIBCPP_HAS_WIDE_CHARACTERS
385+
250386template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
251387_LIBCPP_HIDE_FROM_ABI inline void
252388__vprint_unicode_windows (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
389+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
390+
253391 if (!__is_terminal)
254392 return __print::__vprint_nonunicode (__stream, __fmt, __args, __write_nl);
255393
@@ -284,6 +422,49 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
284422 " __write_to_windows_console is not available." );
285423# endif
286424}
425+
426+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
427+ _LIBCPP_HIDE_FROM_ABI inline void
428+ __vprint_unicode_buffered_windows (FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
429+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
430+
431+ if (!__is_terminal)
432+ return __print::__vprint_nonunicode_buffered (__stream, __fmt, __args, __write_nl);
433+
434+ __print::__file_stream_buffer _ (__stream);
435+
436+ // TODO PRINT Should flush errors throw too?
437+ __print::__fflush_unlocked (__stream);
438+
439+ string __str = std::vformat (__fmt, __args);
440+ // UTF-16 uses the same number or less code units than UTF-8.
441+ // However the size of the code unit is 16 bits instead of 8 bits.
442+ //
443+ // The buffer uses the worst-case estimate and should never resize.
444+ // However when the string is large this could lead to OOM. Using a
445+ // smaller size might work, but since the buffer uses a grow factor
446+ // the final size might be larger when the estimate is wrong.
447+ //
448+ // TODO PRINT profile and improve the speed of this code.
449+ __format::__retarget_buffer<wchar_t > __buffer{__str.size ()};
450+ __unicode::__transcode (__str.begin (), __str.end (), __buffer.__make_output_iterator ());
451+ if (__write_nl)
452+ __buffer.push_back (L' \n ' );
453+
454+ [[maybe_unused]] wstring_view __view = __buffer.__view ();
455+
456+ // The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
457+ // the behavior in the test. This is not part of the public API.
458+ # ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
459+ _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION (__stream, __view);
460+ # elif defined(_LIBCPP_WIN32API)
461+ std::__write_to_windows_console (__stream, __view);
462+ # else
463+ std::__throw_runtime_error (" No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
464+ " __write_to_windows_console is not available." );
465+ # endif
466+ }
467+
287468# endif // _LIBCPP_HAS_WIDE_CHARACTERS
288469
289470template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -324,20 +505,47 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
324505# endif
325506}
326507
508+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
509+ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered (
510+ [[maybe_unused]] FILE* __stream,
511+ [[maybe_unused]] string_view __fmt,
512+ [[maybe_unused]] format_args __args,
513+ [[maybe_unused]] bool __write_nl) {
514+ _LIBCPP_ASSERT_NON_NULL (__stream, " __stream must be a valid pointer to an output C stream" );
515+
516+ # ifndef _LIBCPP_WIN32API
517+ __print::__vprint_unicode_buffered_posix (__stream, __fmt, __args, __write_nl, __print::__is_terminal (__stream));
518+ # elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
519+ __print::__vprint_unicode_buffered_windows (__stream, __fmt, __args, __write_nl, __print::__is_terminal (__stream));
520+ # else
521+ # error "Windows builds with wchar_t disabled are not supported."
522+ # endif
523+ }
524+
327525# endif // _LIBCPP_HAS_UNICODE
328526
329527} // namespace __print
330528
331529template <class ... _Args>
332530_LIBCPP_HIDE_FROM_ABI void print (FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
333531# 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 );
532+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
338533# else // _LIBCPP_HAS_UNICODE
339- __print::__vprint_nonunicode (__stream, __fmt. get (), std::make_format_args (__args...), false ) ;
534+ constexpr bool __use_unicode = false ;
340535# endif // _LIBCPP_HAS_UNICODE
536+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t <_Args>> && ...);
537+
538+ if constexpr (__use_unicode) {
539+ if constexpr (__locksafe)
540+ __print::__vprint_unicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), false );
541+ else
542+ __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
543+ } else {
544+ if constexpr (__locksafe)
545+ __print::__vprint_nonunicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), false );
546+ else
547+ __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), false );
548+ }
341549}
342550
343551template <class ... _Args>
@@ -348,16 +556,26 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
348556template <class ... _Args>
349557_LIBCPP_HIDE_FROM_ABI void println (FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
350558# if _LIBCPP_HAS_UNICODE
559+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
560+ # else // _LIBCPP_HAS_UNICODE
561+ constexpr bool __use_unicode = false ;
562+ # endif // _LIBCPP_HAS_UNICODE
563+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t <_Args>> && ...);
564+
351565 // Note the wording in the Standard is inefficient. The output of
352566 // std::format is a std::string which is then copied. This solution
353567 // 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
568+ if constexpr (__use_unicode) {
569+ if constexpr (__locksafe)
570+ __print::__vprint_unicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), true );
571+ else
572+ __print::__vprint_unicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
573+ } else {
574+ if constexpr (__locksafe)
575+ __print::__vprint_nonunicode_buffered (__stream, __fmt.get (), std::make_format_args (__args...), true );
576+ else
577+ __print::__vprint_nonunicode (__stream, __fmt.get (), std::make_format_args (__args...), true );
578+ }
361579}
362580
363581template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -381,6 +599,11 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __f
381599 __print::__vprint_unicode (__stream, __fmt, __args, false );
382600}
383601
602+ template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
603+ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode_buffered (FILE* __stream, string_view __fmt, format_args __args) {
604+ __print::__vprint_unicode_buffered (__stream, __fmt, __args, false );
605+ }
606+
384607template <class = void > // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
385608_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode (string_view __fmt, format_args __args) {
386609 std::vprint_unicode (stdout, __fmt, __args);
0 commit comments