From 73a7634f261d63e018ae338f53d7a308c2bc7006 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 17 Jun 2025 12:23:18 -0400 Subject: [PATCH 1/2] [libc++] Refactor basic_filebuf::overflow() Refactor the function to streamline the logic so it matches the specification in [filebuf.virtuals] more closely. In particular, avoid modifying the put area pointers when we loop around after a partial codecvt conversion. Note that we're technically not up-to-spec in this implementation, since the Standard says that we shouldn't try more than once after a partial codecvt conversion. However, this refactoring attempts not to change any functionality. --- libcxx/include/fstream | 57 ++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/libcxx/include/fstream b/libcxx/include/fstream index 00aa00ff7e9cd..f9cbac2ab0578 100644 --- a/libcxx/include/fstream +++ b/libcxx/include/fstream @@ -835,35 +835,50 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits> } if (this->pptr() != this->pbase()) { if (__always_noconv_) { - size_t __nmemb = static_cast(this->pptr() - this->pbase()); - if (std::fwrite(this->pbase(), sizeof(char_type), __nmemb, __file_) != __nmemb) + size_t __n = static_cast(this->pptr() - this->pbase()); + if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n) return traits_type::eof(); } else { - char* __extbe = __extbuf_; - codecvt_base::result __r; + if (!__cv_) + std::__throw_bad_cast(); + + // See [filebuf.virtuals] + char_type* __b = this->pbase(); + char_type* __p = this->pptr(); + const char_type* __end; + char* __extbuf_end = __extbuf_; do { - if (!__cv_) - std::__throw_bad_cast(); - - const char_type* __e; - __r = __cv_->out(__st_, this->pbase(), this->pptr(), __e, __extbuf_, __extbuf_ + __ebs_, __extbe); - if (__e == this->pbase()) + codecvt_base::result __r = __cv_->out(__st_, __b, __p, __end, __extbuf_, __extbuf_ + __ebs_, __extbuf_end); + if (__end == __b) return traits_type::eof(); + + // No conversion needed: output characters directly to the file, done. if (__r == codecvt_base::noconv) { - size_t __nmemb = static_cast(this->pptr() - this->pbase()); - if (std::fwrite(this->pbase(), 1, __nmemb, __file_) != __nmemb) + size_t __n = static_cast(__p - __b); + if (std::fwrite(__b, 1, __n, __file_) != __n) return traits_type::eof(); - } else if (__r == codecvt_base::ok || __r == codecvt_base::partial) { - size_t __nmemb = static_cast(__extbe - __extbuf_); - if (std::fwrite(__extbuf_, 1, __nmemb, __file_) != __nmemb) + break; + + // Conversion successful: output the converted characters to the file, done. + } else if (__r == codecvt_base::ok) { + size_t __n = static_cast(__extbuf_end - __extbuf_); + if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) + return traits_type::eof(); + break; + + // Conversion partially successful: output converted characters to the file and repeat with the + // remaining characters. + } else if (__r == codecvt_base::partial) { + size_t __n = static_cast(__extbuf_end - __extbuf_); + if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) return traits_type::eof(); - if (__r == codecvt_base::partial) { - this->setp(const_cast(__e), this->pptr()); - this->__pbump(this->epptr() - this->pbase()); - } - } else + __b = const_cast(__end); + continue; + + } else { return traits_type::eof(); - } while (__r == codecvt_base::partial); + } + } while (true); } this->setp(__pb_save, __epb_save); } From 98f6169a69fe647baf2aea537932724271841c46 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Fri, 20 Jun 2025 12:01:05 -0400 Subject: [PATCH 2/2] Use early return --- libcxx/include/fstream | 92 ++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/libcxx/include/fstream b/libcxx/include/fstream index f9cbac2ab0578..c86f709bedb80 100644 --- a/libcxx/include/fstream +++ b/libcxx/include/fstream @@ -833,55 +833,59 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits> *this->pptr() = traits_type::to_char_type(__c); this->pbump(1); } - if (this->pptr() != this->pbase()) { - if (__always_noconv_) { - size_t __n = static_cast(this->pptr() - this->pbase()); - if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n) + + // There is nothing to write, early return + if (this->pptr() == this->pbase()) { + return traits_type::not_eof(__c); + } + + if (__always_noconv_) { + size_t __n = static_cast(this->pptr() - this->pbase()); + if (std::fwrite(this->pbase(), sizeof(char_type), __n, __file_) != __n) + return traits_type::eof(); + } else { + if (!__cv_) + std::__throw_bad_cast(); + + // See [filebuf.virtuals] + char_type* __b = this->pbase(); + char_type* __p = this->pptr(); + const char_type* __end; + char* __extbuf_end = __extbuf_; + do { + codecvt_base::result __r = __cv_->out(__st_, __b, __p, __end, __extbuf_, __extbuf_ + __ebs_, __extbuf_end); + if (__end == __b) return traits_type::eof(); - } else { - if (!__cv_) - std::__throw_bad_cast(); - - // See [filebuf.virtuals] - char_type* __b = this->pbase(); - char_type* __p = this->pptr(); - const char_type* __end; - char* __extbuf_end = __extbuf_; - do { - codecvt_base::result __r = __cv_->out(__st_, __b, __p, __end, __extbuf_, __extbuf_ + __ebs_, __extbuf_end); - if (__end == __b) + + // No conversion needed: output characters directly to the file, done. + if (__r == codecvt_base::noconv) { + size_t __n = static_cast(__p - __b); + if (std::fwrite(__b, 1, __n, __file_) != __n) return traits_type::eof(); + break; - // No conversion needed: output characters directly to the file, done. - if (__r == codecvt_base::noconv) { - size_t __n = static_cast(__p - __b); - if (std::fwrite(__b, 1, __n, __file_) != __n) - return traits_type::eof(); - break; - - // Conversion successful: output the converted characters to the file, done. - } else if (__r == codecvt_base::ok) { - size_t __n = static_cast(__extbuf_end - __extbuf_); - if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) - return traits_type::eof(); - break; - - // Conversion partially successful: output converted characters to the file and repeat with the - // remaining characters. - } else if (__r == codecvt_base::partial) { - size_t __n = static_cast(__extbuf_end - __extbuf_); - if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) - return traits_type::eof(); - __b = const_cast(__end); - continue; - - } else { + // Conversion successful: output the converted characters to the file, done. + } else if (__r == codecvt_base::ok) { + size_t __n = static_cast(__extbuf_end - __extbuf_); + if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) return traits_type::eof(); - } - } while (true); - } - this->setp(__pb_save, __epb_save); + break; + + // Conversion partially successful: output converted characters to the file and repeat with the + // remaining characters. + } else if (__r == codecvt_base::partial) { + size_t __n = static_cast(__extbuf_end - __extbuf_); + if (std::fwrite(__extbuf_, 1, __n, __file_) != __n) + return traits_type::eof(); + __b = const_cast(__end); + continue; + + } else { + return traits_type::eof(); + } + } while (true); } + this->setp(__pb_save, __epb_save); return traits_type::not_eof(__c); }