From 52b27c2bd627c4b83d94b945748faf237cc28a3c Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 24 Jun 2025 13:58:49 -0400 Subject: [PATCH] [libc++] Refactor basic_filebuf::overflow() (#144793) 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 | 83 ++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/libcxx/include/fstream b/libcxx/include/fstream index 00aa00ff7e9c..c86f709bedb8 100644 --- a/libcxx/include/fstream +++ b/libcxx/include/fstream @@ -833,40 +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 __nmemb = static_cast(this->pptr() - this->pbase()); - if (std::fwrite(this->pbase(), sizeof(char_type), __nmemb, __file_) != __nmemb) - return traits_type::eof(); - } else { - char* __extbe = __extbuf_; - codecvt_base::result __r; - 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()) - return traits_type::eof(); - if (__r == codecvt_base::noconv) { - size_t __nmemb = static_cast(this->pptr() - this->pbase()); - if (std::fwrite(this->pbase(), 1, __nmemb, __file_) != __nmemb) - 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) - return traits_type::eof(); - if (__r == codecvt_base::partial) { - this->setp(const_cast(__e), this->pptr()); - this->__pbump(this->epptr() - this->pbase()); - } - } else - return traits_type::eof(); - } while (__r == codecvt_base::partial); - } - this->setp(__pb_save, __epb_save); + // 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(); + + // 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 { + return traits_type::eof(); + } + } while (true); + } + this->setp(__pb_save, __epb_save); return traits_type::not_eof(__c); }