From ffcc38e3a317a9cae07915db323dd242b880b208 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 12 May 2024 15:29:24 +0800 Subject: [PATCH 1/2] Optimize `println()` for `FILE*` --- stl/inc/__msvc_print.hpp | 3 +++ stl/inc/print | 56 +++++++++++++++++++++++++++++++++++++++- stl/src/print.cpp | 18 +++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/stl/inc/__msvc_print.hpp b/stl/inc/__msvc_print.hpp index edaedc8f3d..2f9c4963cc 100644 --- a/stl/inc/__msvc_print.hpp +++ b/stl/inc/__msvc_print.hpp @@ -55,6 +55,9 @@ _NODISCARD _Success_(return == __std_win_error::_Success) __std_win_error __stdcall __std_print_to_unicode_console(_In_ __std_unicode_console_handle _Console_handle, _In_reads_(_Str_size) const char* _Str, _In_ size_t _Str_size) noexcept; +_NODISCARD _Success_(return == __std_win_error::_Success) __std_win_error + __stdcall __std_print_newline_only_to_unicode_console(_In_ __std_unicode_console_handle _Console_handle) noexcept; + } // extern "C" _STD_BEGIN diff --git a/stl/inc/print b/stl/inc/print index 13f12ea046..b7afce38f8 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -84,6 +84,52 @@ inline void _Print_noformat_unicode(FILE* const _Stream, const string_view _Str) } } +inline void _Print_newline_only_unicode(FILE* const _Stream) { + const __std_unicode_console_retrieval_result _Unicode_console_retrieval_result{ + __std_get_unicode_console_handle_from_file_stream(_Stream)}; + + // See the documentation for __std_unicode_console_retrieval_result to understand why we do this. + bool _Is_unicode_console; + +#pragma warning(push) +#pragma warning(disable : 4061) // enumerator not explicitly handled by switch label + switch (_Unicode_console_retrieval_result._Error) { + case __std_win_error::_Success: + _Is_unicode_console = true; + break; + + case __std_win_error::_File_not_found: + _Is_unicode_console = false; + break; + + case __std_win_error::_Not_supported: + [[unlikely]] return; + + default: + [[unlikely]] _STD _Throw_system_error_from_std_win_error(_Unicode_console_retrieval_result._Error); + } +#pragma warning(pop) + + if (_Is_unicode_console) { + const bool _Was_flush_successful = _CSTD fflush(_Stream) == 0; + if (!_Was_flush_successful) [[unlikely]] { + _Throw_system_error(static_cast(errno)); + } + + const __std_win_error _Console_print_result = + __std_print_newline_only_to_unicode_console(_Unicode_console_retrieval_result._Console_handle); + if (_Console_print_result != __std_win_error::_Success) [[unlikely]] { + _STD _Throw_system_error_from_std_win_error(_Console_print_result); + } + } else { + const bool _Was_write_successful = _CSTD fputc('\n', _Stream) == '\n'; + + if (!_Was_write_successful) [[unlikely]] { + _Throw_system_error(static_cast(errno)); + } + } +} + inline void _Vprint_unicode_impl( const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { string _Output_str = _STD vformat(_Fmt_str, _Fmt_args); @@ -133,7 +179,15 @@ void println(FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&. _EXPORT_STD template // improves throughput, see GH-2329 void println(FILE* _Stream) { - _STD print(_Stream, "\n"); + if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { + _STD _Print_newline_only_unicode(_Stream); + } else { + const bool _Was_write_successful = _CSTD fputc('\n', _Stream) == '\n'; + + if (!_Was_write_successful) [[unlikely]] { + _Throw_system_error(static_cast(errno)); + } + } } _EXPORT_STD template // improves throughput, see GH-2329 diff --git a/stl/src/print.cpp b/stl/src/print.cpp index fcd7d263e3..1e235717c5 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -266,4 +266,22 @@ extern "C" { } } +[[nodiscard]] _Success_(return == __std_win_error::_Success) __std_win_error + __stdcall __std_print_newline_only_to_unicode_console( + _In_ const __std_unicode_console_handle _Console_handle) noexcept { + if (_Console_handle == __std_unicode_console_handle::_Invalid) [[unlikely]] { + return __std_win_error::_Invalid_parameter; + } + + const auto _Actual_console_handle = reinterpret_cast(_Console_handle); + + const BOOL _Write_result = WriteConsoleW(_Actual_console_handle, L"\n", static_cast(1), nullptr, nullptr); + + if (!_Write_result) [[unlikely]] { + return static_cast<__std_win_error>(GetLastError()); + } + + return __std_win_error::_Success; +} + } // extern "C" From 3fdadd23bf1b510446ac068f27522f90750ec74a Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 12 May 2024 15:56:24 +0800 Subject: [PATCH 2/2] Optimize `println()` for `ostream` --- stl/inc/__msvc_filebuf.hpp | 3 ++ stl/inc/ostream | 103 ++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/stl/inc/__msvc_filebuf.hpp b/stl/inc/__msvc_filebuf.hpp index 4d8e76dbc1..ed1438aacf 100644 --- a/stl/inc/__msvc_filebuf.hpp +++ b/stl/inc/__msvc_filebuf.hpp @@ -386,6 +386,9 @@ class basic_filebuf : public basic_streambuf<_Elem, _Traits> { // stream buffer #if _HAS_CXX23 && defined(_CPPRTTI) template friend ios_base::iostate _Print_noformat_unicode(ostream&, string_view); + + template + friend ios_base::iostate _Print_newline_only_unicode(ostream&); #endif protected: diff --git a/stl/inc/ostream b/stl/inc/ostream index b682a8b68b..98dbaf43dd 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -1111,6 +1111,24 @@ ios_base::iostate _Print_noformat_nonunicode(ostream& _Ostr, const string_view _ return _State; } +template +ios_base::iostate _Print_newline_only_nonunicode(ostream& _Ostr) { + // *LOCKED* + // + // This function is called from a context in which an ostream::sentry has been + // constructed. + ios_base::iostate _State = ios_base::goodbit; + + _TRY_IO_BEGIN + const auto _Was_insertion_successful = _Ostr.rdbuf()->sputc('\n') == '\n'; + if (!_Was_insertion_successful) [[unlikely]] { + _State |= ios_base::badbit; + } + _CATCH_IO_(ios_base, _Ostr) + + return _State; +} + template void _Vprint_nonunicode_impl( const _Add_newline _Add_nl, ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) { @@ -1203,6 +1221,76 @@ ios_base::iostate _Print_noformat_unicode(ostream& _Ostr, const string_view _Str return _State; } +template +ios_base::iostate _Print_newline_only_unicode(ostream& _Ostr) { + // *LOCKED* + // + // This function is called from a context in which an ostream::sentry has been + // constructed. + ios_base::iostate _State = ios_base::goodbit; + + // The std::ostream& overload of vprint_unicode() expects you to determine whether the stream refers + // to a unicode console or not based solely on the std::ostream& provided. That class simply doesn't + // provide enough information to know this in every case. The best we can do (without breaking ABI) + // is check if the underlying std::streambuf object of the std::ostream& actually refers to a std::filebuf + // object. This requires the use of a dynamic_cast. (This is also why the std::ostream& overloads of + // std::print() et al. are deleted when RTTI is disabled.) + streambuf* const _Streambuf = _Ostr.rdbuf(); + const auto _Filebuf = dynamic_cast<_Filebuf_type*>(_Streambuf); + + // If we got nullptr, then it probably isn't being used for a unicode console... + if (_Filebuf == nullptr) { + _State |= _STD _Print_newline_only_nonunicode(_Ostr); + return _State; + } + + FILE* const _File_stream = _Filebuf->_Myfile; + const __std_unicode_console_retrieval_result _Unicode_console_retrieval_result{ + __std_get_unicode_console_handle_from_file_stream(_File_stream)}; + + // See the documentation for __std_unicode_console_retrieval_result to understand why we do this. + bool _Is_unicode_console; + +#pragma warning(push) +#pragma warning(disable : 4061) // enumerator not explicitly handled by switch label + switch (_Unicode_console_retrieval_result._Error) { + case __std_win_error::_Success: + _Is_unicode_console = true; + break; + + case __std_win_error::_File_not_found: + _Is_unicode_console = false; + break; + + case __std_win_error::_Not_supported: + [[unlikely]] return _State; + + default: + [[unlikely]] return ios_base::failbit; + } +#pragma warning(pop) + + if (_Is_unicode_console) { + _TRY_IO_BEGIN + const bool _Was_flush_successful = _Ostr.rdbuf()->pubsync() != -1; + if (!_Was_flush_successful) [[unlikely]] { + _State |= ios_base::badbit; + return _State; + } + + const __std_win_error _Unicode_console_print_result = + __std_print_newline_only_to_unicode_console(_Unicode_console_retrieval_result._Console_handle); + if (_Unicode_console_print_result != __std_win_error::_Success) [[unlikely]] { + _State |= ios_base::badbit; + } + _CATCH_IO_(ios_base, _Ostr) + } else { + _State |= _STD _Print_newline_only_nonunicode(_Ostr); + } + + return _State; +} + template void _Vprint_unicode_impl( const _Add_newline _Add_nl, ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) { @@ -1274,7 +1362,20 @@ void println(ostream& _Ostr, const format_string<_Types...> _Fmt, _Types&&... _A _EXPORT_STD template // improves throughput, see GH-2329 void println(ostream& _Ostr) { - _STD print(_Ostr, "\n"); + const ostream::sentry _Ok(_Ostr); + ios_base::iostate _State = ios_base::goodbit; + + if (!_Ok) [[unlikely]] { + _State |= ios_base::badbit; + } else [[likely]] { + if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { + _State |= _STD _Print_newline_only_unicode(_Ostr); + } else { + _State |= _STD _Print_newline_only_nonunicode(_Ostr); + } + } + + _Ostr.setstate(_State); } _EXPORT_STD template // improves throughput, see GH-2329