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