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