//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef _LIBCPP___LOCALE_DIR_WBUFFER_CONVERT_H #define _LIBCPP___LOCALE_DIR_WBUFFER_CONVERT_H #include <__algorithm/reverse.h> #include <__config> #include <__string/char_traits.h> #include #include #if _LIBCPP_HAS_LOCALIZATION # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header # endif # if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_WSTRING_CONVERT) _LIBCPP_PUSH_MACROS # include <__undef_macros> _LIBCPP_BEGIN_NAMESPACE_STD template > class _LIBCPP_DEPRECATED_IN_CXX17 wbuffer_convert : public basic_streambuf<_Elem, _Tr> { public: // types: typedef _Elem char_type; typedef _Tr traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; typedef typename _Codecvt::state_type state_type; private: char* __extbuf_; const char* __extbufnext_; const char* __extbufend_; char __extbuf_min_[8]; size_t __ebs_; char_type* __intbuf_; size_t __ibs_; streambuf* __bufptr_; _Codecvt* __cv_; state_type __st_; ios_base::openmode __cm_; bool __owns_eb_; bool __owns_ib_; bool __always_noconv_; public: # ifndef _LIBCPP_CXX03_LANG _LIBCPP_HIDE_FROM_ABI wbuffer_convert() : wbuffer_convert(nullptr) {} explicit _LIBCPP_HIDE_FROM_ABI wbuffer_convert(streambuf* __bytebuf, _Codecvt* __pcvt = new _Codecvt, state_type __state = state_type()); # else _LIBCPP_EXPLICIT_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI wbuffer_convert(streambuf* __bytebuf = nullptr, _Codecvt* __pcvt = new _Codecvt, state_type __state = state_type()); # endif _LIBCPP_HIDE_FROM_ABI ~wbuffer_convert(); _LIBCPP_HIDE_FROM_ABI streambuf* rdbuf() const { return __bufptr_; } _LIBCPP_HIDE_FROM_ABI streambuf* rdbuf(streambuf* __bytebuf) { streambuf* __r = __bufptr_; __bufptr_ = __bytebuf; return __r; } wbuffer_convert(const wbuffer_convert&) = delete; wbuffer_convert& operator=(const wbuffer_convert&) = delete; _LIBCPP_HIDE_FROM_ABI state_type state() const { return __st_; } protected: _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual int_type underflow(); _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual int_type pbackfail(int_type __c = traits_type::eof()); _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual int_type overflow(int_type __c = traits_type::eof()); _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual basic_streambuf* setbuf(char_type* __s, streamsize __n); _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual pos_type seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __wch = ios_base::in | ios_base::out); _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual pos_type seekpos(pos_type __sp, ios_base::openmode __wch = ios_base::in | ios_base::out); _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual int sync(); private: _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool __read_mode(); _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __write_mode(); _LIBCPP_HIDE_FROM_ABI_VIRTUAL wbuffer_convert* __close(); }; _LIBCPP_SUPPRESS_DEPRECATED_PUSH template wbuffer_convert<_Codecvt, _Elem, _Tr>::wbuffer_convert(streambuf* __bytebuf, _Codecvt* __pcvt, state_type __state) : __extbuf_(nullptr), __extbufnext_(nullptr), __extbufend_(nullptr), __ebs_(0), __intbuf_(0), __ibs_(0), __bufptr_(__bytebuf), __cv_(__pcvt), __st_(__state), __cm_(0), __owns_eb_(false), __owns_ib_(false), __always_noconv_(__cv_ ? __cv_->always_noconv() : false) { setbuf(0, 4096); } template wbuffer_convert<_Codecvt, _Elem, _Tr>::~wbuffer_convert() { __close(); delete __cv_; if (__owns_eb_) delete[] __extbuf_; if (__owns_ib_) delete[] __intbuf_; } template typename wbuffer_convert<_Codecvt, _Elem, _Tr>::int_type wbuffer_convert<_Codecvt, _Elem, _Tr>::underflow() { _LIBCPP_SUPPRESS_DEPRECATED_POP if (__cv_ == 0 || __bufptr_ == nullptr) return traits_type::eof(); bool __initial = __read_mode(); char_type __1buf; if (this->gptr() == 0) this->setg(std::addressof(__1buf), std::addressof(__1buf) + 1, std::addressof(__1buf) + 1); const size_t __unget_sz = __initial ? 0 : std::min((this->egptr() - this->eback()) / 2, 4); int_type __c = traits_type::eof(); if (this->gptr() == this->egptr()) { std::memmove(this->eback(), this->egptr() - __unget_sz, __unget_sz * sizeof(char_type)); if (__always_noconv_) { streamsize __nmemb = static_cast(this->egptr() - this->eback() - __unget_sz); __nmemb = __bufptr_->sgetn((char*)this->eback() + __unget_sz, __nmemb); if (__nmemb != 0) { this->setg(this->eback(), this->eback() + __unget_sz, this->eback() + __unget_sz + __nmemb); __c = *this->gptr(); } } else { if (__extbufend_ != __extbufnext_) { _LIBCPP_ASSERT_NON_NULL(__extbufnext_ != nullptr, "underflow moving from nullptr"); _LIBCPP_ASSERT_NON_NULL(__extbuf_ != nullptr, "underflow moving into nullptr"); std::memmove(__extbuf_, __extbufnext_, __extbufend_ - __extbufnext_); } __extbufnext_ = __extbuf_ + (__extbufend_ - __extbufnext_); __extbufend_ = __extbuf_ + (__extbuf_ == __extbuf_min_ ? sizeof(__extbuf_min_) : __ebs_); streamsize __nmemb = std::min(static_cast(this->egptr() - this->eback() - __unget_sz), static_cast(__extbufend_ - __extbufnext_)); codecvt_base::result __r; // FIXME: Do we ever need to restore the state here? // state_type __svs = __st_; streamsize __nr = __bufptr_->sgetn(const_cast(__extbufnext_), __nmemb); if (__nr != 0) { __extbufend_ = __extbufnext_ + __nr; char_type* __inext; __r = __cv_->in( __st_, __extbuf_, __extbufend_, __extbufnext_, this->eback() + __unget_sz, this->egptr(), __inext); if (__r == codecvt_base::noconv) { this->setg((char_type*)__extbuf_, (char_type*)__extbuf_, (char_type*)const_cast(__extbufend_)); __c = *this->gptr(); } else if (__inext != this->eback() + __unget_sz) { this->setg(this->eback(), this->eback() + __unget_sz, __inext); __c = *this->gptr(); } } } } else __c = *this->gptr(); if (this->eback() == std::addressof(__1buf)) this->setg(0, 0, 0); return __c; } _LIBCPP_SUPPRESS_DEPRECATED_PUSH template typename wbuffer_convert<_Codecvt, _Elem, _Tr>::int_type wbuffer_convert<_Codecvt, _Elem, _Tr>::pbackfail(int_type __c) { _LIBCPP_SUPPRESS_DEPRECATED_POP if (__cv_ != 0 && __bufptr_ && this->eback() < this->gptr()) { if (traits_type::eq_int_type(__c, traits_type::eof())) { this->gbump(-1); return traits_type::not_eof(__c); } if (traits_type::eq(traits_type::to_char_type(__c), this->gptr()[-1])) { this->gbump(-1); *this->gptr() = traits_type::to_char_type(__c); return __c; } } return traits_type::eof(); } _LIBCPP_SUPPRESS_DEPRECATED_PUSH template typename wbuffer_convert<_Codecvt, _Elem, _Tr>::int_type wbuffer_convert<_Codecvt, _Elem, _Tr>::overflow(int_type __c) { _LIBCPP_SUPPRESS_DEPRECATED_POP if (__cv_ == 0 || !__bufptr_) return traits_type::eof(); __write_mode(); char_type __1buf; char_type* __pb_save = this->pbase(); char_type* __epb_save = this->epptr(); if (!traits_type::eq_int_type(__c, traits_type::eof())) { if (this->pptr() == 0) this->setp(std::addressof(__1buf), std::addressof(__1buf) + 1); *this->pptr() = traits_type::to_char_type(__c); this->pbump(1); } if (this->pptr() != this->pbase()) { if (__always_noconv_) { streamsize __nmemb = static_cast(this->pptr() - this->pbase()); if (__bufptr_->sputn((const char*)this->pbase(), __nmemb) != __nmemb) return traits_type::eof(); } else { char* __extbe = __extbuf_; codecvt_base::result __r; do { 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) { streamsize __nmemb = static_cast(this->pptr() - this->pbase()); if (__bufptr_->sputn((const char*)this->pbase(), __nmemb) != __nmemb) return traits_type::eof(); } else if (__r == codecvt_base::ok || __r == codecvt_base::partial) { streamsize __nmemb = static_cast(__extbe - __extbuf_); if (__bufptr_->sputn(__extbuf_, __nmemb) != __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); } return traits_type::not_eof(__c); } _LIBCPP_SUPPRESS_DEPRECATED_PUSH template basic_streambuf<_Elem, _Tr>* wbuffer_convert<_Codecvt, _Elem, _Tr>::setbuf(char_type* __s, streamsize __n) { _LIBCPP_SUPPRESS_DEPRECATED_POP this->setg(0, 0, 0); this->setp(0, 0); if (__owns_eb_) delete[] __extbuf_; if (__owns_ib_) delete[] __intbuf_; __ebs_ = __n; if (__ebs_ > sizeof(__extbuf_min_)) { if (__always_noconv_ && __s) { __extbuf_ = (char*)__s; __owns_eb_ = false; } else { __extbuf_ = new char[__ebs_]; __owns_eb_ = true; } } else { __extbuf_ = __extbuf_min_; __ebs_ = sizeof(__extbuf_min_); __owns_eb_ = false; } if (!__always_noconv_) { __ibs_ = max(__n, sizeof(__extbuf_min_)); if (__s && __ibs_ >= sizeof(__extbuf_min_)) { __intbuf_ = __s; __owns_ib_ = false; } else { __intbuf_ = new char_type[__ibs_]; __owns_ib_ = true; } } else { __ibs_ = 0; __intbuf_ = 0; __owns_ib_ = false; } return this; } _LIBCPP_SUPPRESS_DEPRECATED_PUSH template typename wbuffer_convert<_Codecvt, _Elem, _Tr>::pos_type wbuffer_convert<_Codecvt, _Elem, _Tr>::seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __om) { int __width = __cv_->encoding(); if (__cv_ == 0 || !__bufptr_ || (__width <= 0 && __off != 0) || sync()) return pos_type(off_type(-1)); // __width > 0 || __off == 0, now check __way if (__way != ios_base::beg && __way != ios_base::cur && __way != ios_base::end) return pos_type(off_type(-1)); pos_type __r = __bufptr_->pubseekoff(__width * __off, __way, __om); __r.state(__st_); return __r; } template typename wbuffer_convert<_Codecvt, _Elem, _Tr>::pos_type wbuffer_convert<_Codecvt, _Elem, _Tr>::seekpos(pos_type __sp, ios_base::openmode __wch) { if (__cv_ == 0 || !__bufptr_ || sync()) return pos_type(off_type(-1)); if (__bufptr_->pubseekpos(__sp, __wch) == pos_type(off_type(-1))) return pos_type(off_type(-1)); return __sp; } template int wbuffer_convert<_Codecvt, _Elem, _Tr>::sync() { _LIBCPP_SUPPRESS_DEPRECATED_POP if (__cv_ == 0 || !__bufptr_) return 0; if (__cm_ & ios_base::out) { if (this->pptr() != this->pbase()) if (overflow() == traits_type::eof()) return -1; codecvt_base::result __r; do { char* __extbe; __r = __cv_->unshift(__st_, __extbuf_, __extbuf_ + __ebs_, __extbe); streamsize __nmemb = static_cast(__extbe - __extbuf_); if (__bufptr_->sputn(__extbuf_, __nmemb) != __nmemb) return -1; } while (__r == codecvt_base::partial); if (__r == codecvt_base::error) return -1; if (__bufptr_->pubsync()) return -1; } else if (__cm_ & ios_base::in) { off_type __c; if (__always_noconv_) __c = this->egptr() - this->gptr(); else { int __width = __cv_->encoding(); __c = __extbufend_ - __extbufnext_; if (__width > 0) __c += __width * (this->egptr() - this->gptr()); else { if (this->gptr() != this->egptr()) { std::reverse(this->gptr(), this->egptr()); codecvt_base::result __r; const char_type* __e = this->gptr(); char* __extbe; do { __r = __cv_->out(__st_, __e, this->egptr(), __e, __extbuf_, __extbuf_ + __ebs_, __extbe); switch (__r) { case codecvt_base::noconv: __c += this->egptr() - this->gptr(); break; case codecvt_base::ok: case codecvt_base::partial: __c += __extbe - __extbuf_; break; default: return -1; } } while (__r == codecvt_base::partial); } } } if (__bufptr_->pubseekoff(-__c, ios_base::cur, __cm_) == pos_type(off_type(-1))) return -1; this->setg(0, 0, 0); __cm_ = 0; } return 0; } _LIBCPP_SUPPRESS_DEPRECATED_PUSH template bool wbuffer_convert<_Codecvt, _Elem, _Tr>::__read_mode() { if (!(__cm_ & ios_base::in)) { this->setp(0, 0); if (__always_noconv_) this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_); else this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_); __cm_ = ios_base::in; return true; } return false; } template void wbuffer_convert<_Codecvt, _Elem, _Tr>::__write_mode() { if (!(__cm_ & ios_base::out)) { this->setg(0, 0, 0); if (__ebs_ > sizeof(__extbuf_min_)) { if (__always_noconv_) this->setp((char_type*)__extbuf_, (char_type*)__extbuf_ + (__ebs_ - 1)); else this->setp(__intbuf_, __intbuf_ + (__ibs_ - 1)); } else this->setp(0, 0); __cm_ = ios_base::out; } } template wbuffer_convert<_Codecvt, _Elem, _Tr>* wbuffer_convert<_Codecvt, _Elem, _Tr>::__close() { wbuffer_convert* __rt = nullptr; if (__cv_ != nullptr && __bufptr_ != nullptr) { __rt = this; if ((__cm_ & ios_base::out) && sync()) __rt = nullptr; } return __rt; } _LIBCPP_SUPPRESS_DEPRECATED_POP _LIBCPP_END_NAMESPACE_STD _LIBCPP_POP_MACROS # endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_WSTRING_CONVERT) #endif // _LIBCPP_HAS_LOCALIZATION #endif // _LIBCPP___LOCALE_DIR_WBUFFER_CONVERT_H