xref: /freebsd/contrib/llvm-project/libcxx/include/__locale_dir/wbuffer_convert.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
31 template <class _Codecvt, class _Elem = wchar_t, class _Tr = char_traits<_Elem> >
32 class _LIBCPP_DEPRECATED_IN_CXX17 wbuffer_convert : public basic_streambuf<_Elem, _Tr> {
33 public:
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 
42 private:
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 
58 public:
59 #    ifndef _LIBCPP_CXX03_LANG
wbuffer_convert()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 
rdbuf()70   _LIBCPP_HIDE_FROM_ABI streambuf* rdbuf() const { return __bufptr_; }
rdbuf(streambuf * __bytebuf)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 
state()80   _LIBCPP_HIDE_FROM_ABI state_type state() const { return __st_; }
81 
82 protected:
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 
93 private:
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
100 template <class _Codecvt, class _Elem, class _Tr>
wbuffer_convert(streambuf * __bytebuf,_Codecvt * __pcvt,state_type __state)101 wbuffer_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 
118 template <class _Codecvt, class _Elem, class _Tr>
~wbuffer_convert()119 wbuffer_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 
128 template <class _Codecvt, class _Elem, class _Tr>
underflow()129 typename 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
184 template <class _Codecvt, class _Elem, class _Tr>
185 typename wbuffer_convert<_Codecvt, _Elem, _Tr>::int_type
pbackfail(int_type __c)186 wbuffer_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
203 template <class _Codecvt, class _Elem, class _Tr>
overflow(int_type __c)204 typename 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
253 template <class _Codecvt, class _Elem, class _Tr>
setbuf(char_type * __s,streamsize __n)254 basic_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
294 template <class _Codecvt, class _Elem, class _Tr>
295 typename wbuffer_convert<_Codecvt, _Elem, _Tr>::pos_type
seekoff(off_type __off,ios_base::seekdir __way,ios_base::openmode __om)296 wbuffer_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 
308 template <class _Codecvt, class _Elem, class _Tr>
309 typename wbuffer_convert<_Codecvt, _Elem, _Tr>::pos_type
seekpos(pos_type __sp,ios_base::openmode __wch)310 wbuffer_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 
318 template <class _Codecvt, class _Elem, class _Tr>
sync()319 int 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
380 template <class _Codecvt, class _Elem, class _Tr>
__read_mode()381 bool 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 
394 template <class _Codecvt, class _Elem, class _Tr>
__write_mode()395 void 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 
409 template <class _Codecvt, class _Elem, class _Tr>
__close()410 wbuffer_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
431