Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize println() overloads added in P3142R0 #4672

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions stl/inc/__msvc_filebuf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,9 @@ class basic_filebuf : public basic_streambuf<_Elem, _Traits> { // stream buffer
#if _HAS_CXX23 && defined(_CPPRTTI)
template <class _Filebuf_type>
friend ios_base::iostate _Print_noformat_unicode(ostream&, string_view);

template <class _Filebuf_type>
friend ios_base::iostate _Print_newline_only_unicode(ostream&);
#endif

protected:
Expand Down
3 changes: 3 additions & 0 deletions stl/inc/__msvc_print.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
103 changes: 102 additions & 1 deletion stl/inc/ostream
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,24 @@ ios_base::iostate _Print_noformat_nonunicode(ostream& _Ostr, const string_view _
return _State;
}

template <int = 0>
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 <int = 0>
void _Vprint_nonunicode_impl(
const _Add_newline _Add_nl, ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) {
Expand Down Expand Up @@ -187,6 +205,76 @@ ios_base::iostate _Print_noformat_unicode(ostream& _Ostr, const string_view _Str
return _State;
}

template <class _Filebuf_type = filebuf>
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 <int = 0>
void _Vprint_unicode_impl(
const _Add_newline _Add_nl, ostream& _Ostr, const string_view _Fmt_str, const format_args _Fmt_args) {
Expand Down Expand Up @@ -258,7 +346,20 @@ void println(ostream& _Ostr, const format_string<_Types...> _Fmt, _Types&&... _A

_EXPORT_STD template <int = 0> // 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 <int = 0> // improves throughput, see GH-2329
Expand Down
56 changes: 55 additions & 1 deletion stl/inc/print
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,52 @@ inline void _Vprint_unicode_noformat_impl(FILE* const _Stream, const string_view
_STD _Do_on_maybe_unicode_console(_Stream, _Unicode_console, _Fallback);
}

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<errc>(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<errc>(errno));
}
}
}

// Format to intermediate string buffer, then print to unicode console/file
inline void _Vprint_unicode_buffered_impl(
const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) {
Expand Down Expand Up @@ -277,7 +323,15 @@ void println(FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&.

_EXPORT_STD template <int = 0> // 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<errc>(errno));
}
}
}

_EXPORT_STD template <int = 0> // improves throughput, see GH-2329
Expand Down
18 changes: 18 additions & 0 deletions stl/src/print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,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<HANDLE>(_Console_handle);

const BOOL _Write_result = WriteConsoleW(_Actual_console_handle, L"\n", static_cast<DWORD>(1), nullptr, nullptr);

if (!_Write_result) [[unlikely]] {
return static_cast<__std_win_error>(GetLastError());
}

return __std_win_error::_Success;
}

} // extern "C"