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