master
  1//===----------------------------------------------------------------------===//
  2//
  3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4// See https://llvm.org/LICENSE.txt for license information.
  5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6//
  7//===----------------------------------------------------------------------===//
  8
  9#ifndef _LIBCPP___LOCALE_DIR_WBUFFER_CONVERT_H
 10#define _LIBCPP___LOCALE_DIR_WBUFFER_CONVERT_H
 11
 12#include <__algorithm/reverse.h>
 13#include <__config>
 14#include <__string/char_traits.h>
 15#include <ios>
 16#include <streambuf>
 17
 18#if _LIBCPP_HAS_LOCALIZATION
 19
 20#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 21#    pragma GCC system_header
 22#  endif
 23
 24#  if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_WSTRING_CONVERT)
 25
 26_LIBCPP_PUSH_MACROS
 27#    include <__undef_macros>
 28
 29_LIBCPP_BEGIN_NAMESPACE_STD
 30
 31template <class _Codecvt, class _Elem = wchar_t, class _Tr = char_traits<_Elem> >
 32class _LIBCPP_DEPRECATED_IN_CXX17 wbuffer_convert : public basic_streambuf<_Elem, _Tr> {
 33public:
 34  // types:
 35  typedef _Elem char_type;
 36  typedef _Tr traits_type;
 37  typedef typename traits_type::int_type int_type;
 38  typedef typename traits_type::pos_type pos_type;
 39  typedef typename traits_type::off_type off_type;
 40  typedef typename _Codecvt::state_type state_type;
 41
 42private:
 43  char* __extbuf_;
 44  const char* __extbufnext_;
 45  const char* __extbufend_;
 46  char __extbuf_min_[8];
 47  size_t __ebs_;
 48  char_type* __intbuf_;
 49  size_t __ibs_;
 50  streambuf* __bufptr_;
 51  _Codecvt* __cv_;
 52  state_type __st_;
 53  ios_base::openmode __cm_;
 54  bool __owns_eb_;
 55  bool __owns_ib_;
 56  bool __always_noconv_;
 57
 58public:
 59#    ifndef _LIBCPP_CXX03_LANG
 60  _LIBCPP_HIDE_FROM_ABI wbuffer_convert() : wbuffer_convert(nullptr) {}
 61  explicit _LIBCPP_HIDE_FROM_ABI
 62  wbuffer_convert(streambuf* __bytebuf, _Codecvt* __pcvt = new _Codecvt, state_type __state = state_type());
 63#    else
 64  _LIBCPP_EXPLICIT_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI
 65  wbuffer_convert(streambuf* __bytebuf = nullptr, _Codecvt* __pcvt = new _Codecvt, state_type __state = state_type());
 66#    endif
 67
 68  _LIBCPP_HIDE_FROM_ABI ~wbuffer_convert();
 69
 70  _LIBCPP_HIDE_FROM_ABI streambuf* rdbuf() const { return __bufptr_; }
 71  _LIBCPP_HIDE_FROM_ABI streambuf* rdbuf(streambuf* __bytebuf) {
 72    streambuf* __r = __bufptr_;
 73    __bufptr_      = __bytebuf;
 74    return __r;
 75  }
 76
 77  wbuffer_convert(const wbuffer_convert&)            = delete;
 78  wbuffer_convert& operator=(const wbuffer_convert&) = delete;
 79
 80  _LIBCPP_HIDE_FROM_ABI state_type state() const { return __st_; }
 81
 82protected:
 83  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual int_type underflow();
 84  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual int_type pbackfail(int_type __c = traits_type::eof());
 85  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual int_type overflow(int_type __c = traits_type::eof());
 86  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual basic_streambuf<char_type, traits_type>* setbuf(char_type* __s, streamsize __n);
 87  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual pos_type
 88  seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __wch = ios_base::in | ios_base::out);
 89  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual pos_type
 90  seekpos(pos_type __sp, ios_base::openmode __wch = ios_base::in | ios_base::out);
 91  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual int sync();
 92
 93private:
 94  _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool __read_mode();
 95  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __write_mode();
 96  _LIBCPP_HIDE_FROM_ABI_VIRTUAL wbuffer_convert* __close();
 97};
 98
 99_LIBCPP_SUPPRESS_DEPRECATED_PUSH
100template <class _Codecvt, class _Elem, class _Tr>
101wbuffer_convert<_Codecvt, _Elem, _Tr>::wbuffer_convert(streambuf* __bytebuf, _Codecvt* __pcvt, state_type __state)
102    : __extbuf_(nullptr),
103      __extbufnext_(nullptr),
104      __extbufend_(nullptr),
105      __ebs_(0),
106      __intbuf_(0),
107      __ibs_(0),
108      __bufptr_(__bytebuf),
109      __cv_(__pcvt),
110      __st_(__state),
111      __cm_(0),
112      __owns_eb_(false),
113      __owns_ib_(false),
114      __always_noconv_(__cv_ ? __cv_->always_noconv() : false) {
115  setbuf(0, 4096);
116}
117
118template <class _Codecvt, class _Elem, class _Tr>
119wbuffer_convert<_Codecvt, _Elem, _Tr>::~wbuffer_convert() {
120  __close();
121  delete __cv_;
122  if (__owns_eb_)
123    delete[] __extbuf_;
124  if (__owns_ib_)
125    delete[] __intbuf_;
126}
127
128template <class _Codecvt, class _Elem, class _Tr>
129typename wbuffer_convert<_Codecvt, _Elem, _Tr>::int_type wbuffer_convert<_Codecvt, _Elem, _Tr>::underflow() {
130  _LIBCPP_SUPPRESS_DEPRECATED_POP
131  if (__cv_ == 0 || __bufptr_ == nullptr)
132    return traits_type::eof();
133  bool __initial = __read_mode();
134  char_type __1buf;
135  if (this->gptr() == 0)
136    this->setg(std::addressof(__1buf), std::addressof(__1buf) + 1, std::addressof(__1buf) + 1);
137  const size_t __unget_sz = __initial ? 0 : std::min<size_t>((this->egptr() - this->eback()) / 2, 4);
138  int_type __c            = traits_type::eof();
139  if (this->gptr() == this->egptr()) {
140    std::memmove(this->eback(), this->egptr() - __unget_sz, __unget_sz * sizeof(char_type));
141    if (__always_noconv_) {
142      streamsize __nmemb = static_cast<streamsize>(this->egptr() - this->eback() - __unget_sz);
143      __nmemb            = __bufptr_->sgetn((char*)this->eback() + __unget_sz, __nmemb);
144      if (__nmemb != 0) {
145        this->setg(this->eback(), this->eback() + __unget_sz, this->eback() + __unget_sz + __nmemb);
146        __c = *this->gptr();
147      }
148    } else {
149      if (__extbufend_ != __extbufnext_) {
150        _LIBCPP_ASSERT_NON_NULL(__extbufnext_ != nullptr, "underflow moving from nullptr");
151        _LIBCPP_ASSERT_NON_NULL(__extbuf_ != nullptr, "underflow moving into nullptr");
152        std::memmove(__extbuf_, __extbufnext_, __extbufend_ - __extbufnext_);
153      }
154      __extbufnext_      = __extbuf_ + (__extbufend_ - __extbufnext_);
155      __extbufend_       = __extbuf_ + (__extbuf_ == __extbuf_min_ ? sizeof(__extbuf_min_) : __ebs_);
156      streamsize __nmemb = std::min(static_cast<streamsize>(this->egptr() - this->eback() - __unget_sz),
157                                    static_cast<streamsize>(__extbufend_ - __extbufnext_));
158      codecvt_base::result __r;
159      // FIXME: Do we ever need to restore the state here?
160      // state_type __svs = __st_;
161      streamsize __nr = __bufptr_->sgetn(const_cast<char*>(__extbufnext_), __nmemb);
162      if (__nr != 0) {
163        __extbufend_ = __extbufnext_ + __nr;
164        char_type* __inext;
165        __r = __cv_->in(
166            __st_, __extbuf_, __extbufend_, __extbufnext_, this->eback() + __unget_sz, this->egptr(), __inext);
167        if (__r == codecvt_base::noconv) {
168          this->setg((char_type*)__extbuf_, (char_type*)__extbuf_, (char_type*)const_cast<char*>(__extbufend_));
169          __c = *this->gptr();
170        } else if (__inext != this->eback() + __unget_sz) {
171          this->setg(this->eback(), this->eback() + __unget_sz, __inext);
172          __c = *this->gptr();
173        }
174      }
175    }
176  } else
177    __c = *this->gptr();
178  if (this->eback() == std::addressof(__1buf))
179    this->setg(0, 0, 0);
180  return __c;
181}
182
183_LIBCPP_SUPPRESS_DEPRECATED_PUSH
184template <class _Codecvt, class _Elem, class _Tr>
185typename wbuffer_convert<_Codecvt, _Elem, _Tr>::int_type
186wbuffer_convert<_Codecvt, _Elem, _Tr>::pbackfail(int_type __c) {
187  _LIBCPP_SUPPRESS_DEPRECATED_POP
188  if (__cv_ != 0 && __bufptr_ && this->eback() < this->gptr()) {
189    if (traits_type::eq_int_type(__c, traits_type::eof())) {
190      this->gbump(-1);
191      return traits_type::not_eof(__c);
192    }
193    if (traits_type::eq(traits_type::to_char_type(__c), this->gptr()[-1])) {
194      this->gbump(-1);
195      *this->gptr() = traits_type::to_char_type(__c);
196      return __c;
197    }
198  }
199  return traits_type::eof();
200}
201
202_LIBCPP_SUPPRESS_DEPRECATED_PUSH
203template <class _Codecvt, class _Elem, class _Tr>
204typename wbuffer_convert<_Codecvt, _Elem, _Tr>::int_type wbuffer_convert<_Codecvt, _Elem, _Tr>::overflow(int_type __c) {
205  _LIBCPP_SUPPRESS_DEPRECATED_POP
206  if (__cv_ == 0 || !__bufptr_)
207    return traits_type::eof();
208  __write_mode();
209  char_type __1buf;
210  char_type* __pb_save  = this->pbase();
211  char_type* __epb_save = this->epptr();
212  if (!traits_type::eq_int_type(__c, traits_type::eof())) {
213    if (this->pptr() == 0)
214      this->setp(std::addressof(__1buf), std::addressof(__1buf) + 1);
215    *this->pptr() = traits_type::to_char_type(__c);
216    this->pbump(1);
217  }
218  if (this->pptr() != this->pbase()) {
219    if (__always_noconv_) {
220      streamsize __nmemb = static_cast<streamsize>(this->pptr() - this->pbase());
221      if (__bufptr_->sputn((const char*)this->pbase(), __nmemb) != __nmemb)
222        return traits_type::eof();
223    } else {
224      char* __extbe = __extbuf_;
225      codecvt_base::result __r;
226      do {
227        const char_type* __e;
228        __r = __cv_->out(__st_, this->pbase(), this->pptr(), __e, __extbuf_, __extbuf_ + __ebs_, __extbe);
229        if (__e == this->pbase())
230          return traits_type::eof();
231        if (__r == codecvt_base::noconv) {
232          streamsize __nmemb = static_cast<size_t>(this->pptr() - this->pbase());
233          if (__bufptr_->sputn((const char*)this->pbase(), __nmemb) != __nmemb)
234            return traits_type::eof();
235        } else if (__r == codecvt_base::ok || __r == codecvt_base::partial) {
236          streamsize __nmemb = static_cast<size_t>(__extbe - __extbuf_);
237          if (__bufptr_->sputn(__extbuf_, __nmemb) != __nmemb)
238            return traits_type::eof();
239          if (__r == codecvt_base::partial) {
240            this->setp(const_cast<char_type*>(__e), this->pptr());
241            this->__pbump(this->epptr() - this->pbase());
242          }
243        } else
244          return traits_type::eof();
245      } while (__r == codecvt_base::partial);
246    }
247    this->setp(__pb_save, __epb_save);
248  }
249  return traits_type::not_eof(__c);
250}
251
252_LIBCPP_SUPPRESS_DEPRECATED_PUSH
253template <class _Codecvt, class _Elem, class _Tr>
254basic_streambuf<_Elem, _Tr>* wbuffer_convert<_Codecvt, _Elem, _Tr>::setbuf(char_type* __s, streamsize __n) {
255  _LIBCPP_SUPPRESS_DEPRECATED_POP
256  this->setg(0, 0, 0);
257  this->setp(0, 0);
258  if (__owns_eb_)
259    delete[] __extbuf_;
260  if (__owns_ib_)
261    delete[] __intbuf_;
262  __ebs_ = __n;
263  if (__ebs_ > sizeof(__extbuf_min_)) {
264    if (__always_noconv_ && __s) {
265      __extbuf_  = (char*)__s;
266      __owns_eb_ = false;
267    } else {
268      __extbuf_  = new char[__ebs_];
269      __owns_eb_ = true;
270    }
271  } else {
272    __extbuf_  = __extbuf_min_;
273    __ebs_     = sizeof(__extbuf_min_);
274    __owns_eb_ = false;
275  }
276  if (!__always_noconv_) {
277    __ibs_ = max<streamsize>(__n, sizeof(__extbuf_min_));
278    if (__s && __ibs_ >= sizeof(__extbuf_min_)) {
279      __intbuf_  = __s;
280      __owns_ib_ = false;
281    } else {
282      __intbuf_  = new char_type[__ibs_];
283      __owns_ib_ = true;
284    }
285  } else {
286    __ibs_     = 0;
287    __intbuf_  = 0;
288    __owns_ib_ = false;
289  }
290  return this;
291}
292
293_LIBCPP_SUPPRESS_DEPRECATED_PUSH
294template <class _Codecvt, class _Elem, class _Tr>
295typename wbuffer_convert<_Codecvt, _Elem, _Tr>::pos_type
296wbuffer_convert<_Codecvt, _Elem, _Tr>::seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __om) {
297  int __width = __cv_->encoding();
298  if (__cv_ == 0 || !__bufptr_ || (__width <= 0 && __off != 0) || sync())
299    return pos_type(off_type(-1));
300  // __width > 0 || __off == 0, now check __way
301  if (__way != ios_base::beg && __way != ios_base::cur && __way != ios_base::end)
302    return pos_type(off_type(-1));
303  pos_type __r = __bufptr_->pubseekoff(__width * __off, __way, __om);
304  __r.state(__st_);
305  return __r;
306}
307
308template <class _Codecvt, class _Elem, class _Tr>
309typename wbuffer_convert<_Codecvt, _Elem, _Tr>::pos_type
310wbuffer_convert<_Codecvt, _Elem, _Tr>::seekpos(pos_type __sp, ios_base::openmode __wch) {
311  if (__cv_ == 0 || !__bufptr_ || sync())
312    return pos_type(off_type(-1));
313  if (__bufptr_->pubseekpos(__sp, __wch) == pos_type(off_type(-1)))
314    return pos_type(off_type(-1));
315  return __sp;
316}
317
318template <class _Codecvt, class _Elem, class _Tr>
319int wbuffer_convert<_Codecvt, _Elem, _Tr>::sync() {
320  _LIBCPP_SUPPRESS_DEPRECATED_POP
321  if (__cv_ == 0 || !__bufptr_)
322    return 0;
323  if (__cm_ & ios_base::out) {
324    if (this->pptr() != this->pbase())
325      if (overflow() == traits_type::eof())
326        return -1;
327    codecvt_base::result __r;
328    do {
329      char* __extbe;
330      __r                = __cv_->unshift(__st_, __extbuf_, __extbuf_ + __ebs_, __extbe);
331      streamsize __nmemb = static_cast<streamsize>(__extbe - __extbuf_);
332      if (__bufptr_->sputn(__extbuf_, __nmemb) != __nmemb)
333        return -1;
334    } while (__r == codecvt_base::partial);
335    if (__r == codecvt_base::error)
336      return -1;
337    if (__bufptr_->pubsync())
338      return -1;
339  } else if (__cm_ & ios_base::in) {
340    off_type __c;
341    if (__always_noconv_)
342      __c = this->egptr() - this->gptr();
343    else {
344      int __width = __cv_->encoding();
345      __c         = __extbufend_ - __extbufnext_;
346      if (__width > 0)
347        __c += __width * (this->egptr() - this->gptr());
348      else {
349        if (this->gptr() != this->egptr()) {
350          std::reverse(this->gptr(), this->egptr());
351          codecvt_base::result __r;
352          const char_type* __e = this->gptr();
353          char* __extbe;
354          do {
355            __r = __cv_->out(__st_, __e, this->egptr(), __e, __extbuf_, __extbuf_ + __ebs_, __extbe);
356            switch (__r) {
357            case codecvt_base::noconv:
358              __c += this->egptr() - this->gptr();
359              break;
360            case codecvt_base::ok:
361            case codecvt_base::partial:
362              __c += __extbe - __extbuf_;
363              break;
364            default:
365              return -1;
366            }
367          } while (__r == codecvt_base::partial);
368        }
369      }
370    }
371    if (__bufptr_->pubseekoff(-__c, ios_base::cur, __cm_) == pos_type(off_type(-1)))
372      return -1;
373    this->setg(0, 0, 0);
374    __cm_ = 0;
375  }
376  return 0;
377}
378
379_LIBCPP_SUPPRESS_DEPRECATED_PUSH
380template <class _Codecvt, class _Elem, class _Tr>
381bool wbuffer_convert<_Codecvt, _Elem, _Tr>::__read_mode() {
382  if (!(__cm_ & ios_base::in)) {
383    this->setp(0, 0);
384    if (__always_noconv_)
385      this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_);
386    else
387      this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_);
388    __cm_ = ios_base::in;
389    return true;
390  }
391  return false;
392}
393
394template <class _Codecvt, class _Elem, class _Tr>
395void wbuffer_convert<_Codecvt, _Elem, _Tr>::__write_mode() {
396  if (!(__cm_ & ios_base::out)) {
397    this->setg(0, 0, 0);
398    if (__ebs_ > sizeof(__extbuf_min_)) {
399      if (__always_noconv_)
400        this->setp((char_type*)__extbuf_, (char_type*)__extbuf_ + (__ebs_ - 1));
401      else
402        this->setp(__intbuf_, __intbuf_ + (__ibs_ - 1));
403    } else
404      this->setp(0, 0);
405    __cm_ = ios_base::out;
406  }
407}
408
409template <class _Codecvt, class _Elem, class _Tr>
410wbuffer_convert<_Codecvt, _Elem, _Tr>* wbuffer_convert<_Codecvt, _Elem, _Tr>::__close() {
411  wbuffer_convert* __rt = nullptr;
412  if (__cv_ != nullptr && __bufptr_ != nullptr) {
413    __rt = this;
414    if ((__cm_ & ios_base::out) && sync())
415      __rt = nullptr;
416  }
417  return __rt;
418}
419
420_LIBCPP_SUPPRESS_DEPRECATED_POP
421
422_LIBCPP_END_NAMESPACE_STD
423
424_LIBCPP_POP_MACROS
425
426#  endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_WSTRING_CONVERT)
427
428#endif // _LIBCPP_HAS_LOCALIZATION
429
430#endif // _LIBCPP___LOCALE_DIR_WBUFFER_CONVERT_H