2 //===----------------------------------------------------------------------===//
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 //===----------------------------------------------------------------------===//
10 #ifndef _LIBCPP_SYNCSTREAM
11 #define _LIBCPP_SYNCSTREAM
16 #include <__cxx03/ostream> // see [ostream.syn]
19 template<class charT, class traits, class Allocator>
22 // [syncstream.syncbuf.special], specialized algorithms
23 template<class charT, class traits, class Allocator>
24 void swap(basic_syncbuf<charT, traits, Allocator>&,
25 basic_syncbuf<charT, traits, Allocator>&);
27 using syncbuf = basic_syncbuf<char>;
28 using wsyncbuf = basic_syncbuf<wchar_t>;
30 template<class charT, class traits, class Allocator>
31 class basic_osyncstream;
33 using osyncstream = basic_osyncstream<char>;
34 using wosyncstream = basic_osyncstream<wchar_t>;
36 template<class charT, class traits, class Allocator>
37 class basic_syncbuf : public basic_streambuf<charT, traits> {
39 using char_type = charT;
40 using int_type = typename traits::int_type;
41 using pos_type = typename traits::pos_type;
42 using off_type = typename traits::off_type;
43 using traits_type = traits;
44 using allocator_type = Allocator;
46 using streambuf_type = basic_streambuf<charT, traits>;
48 // [syncstream.syncbuf.cons], construction and destruction
49 explicit basic_syncbuf(streambuf_type* obuf = nullptr)
50 : basic_syncbuf(obuf, Allocator()) {}
51 basic_syncbuf(streambuf_type*, const Allocator&);
52 basic_syncbuf(basic_syncbuf&&);
55 // [syncstream.syncbuf.assign], assignment and swap
56 basic_syncbuf& operator=(basic_syncbuf&&);
57 void swap(basic_syncbuf&);
59 // [syncstream.syncbuf.members], member functions
61 streambuf_type* get_wrapped() const noexcept;
62 allocator_type get_allocator() const noexcept;
63 void set_emit_on_sync(bool) noexcept;
66 // [syncstream.syncbuf.virtuals], overridden virtual functions
70 streambuf_type* wrapped; // exposition only
71 bool emit_on_sync{}; // exposition only
74 // [syncstream.syncbuf.special], specialized algorithms
75 template<class charT, class traits, class Allocator>
76 void swap(basic_syncbuf<charT, traits, Allocator>&,
77 basic_syncbuf<charT, traits, Allocator>&);
79 template<class charT, class traits, class Allocator>
80 class basic_osyncstream : public basic_ostream<charT, traits> {
82 using char_type = charT;
83 using int_type = typename traits::int_type;
84 using pos_type = typename traits::pos_type;
85 using off_type = typename traits::off_type;
86 using traits_type = traits;
88 using allocator_type = Allocator;
89 using streambuf_type = basic_streambuf<charT, traits>;
90 using syncbuf_type = basic_syncbuf<charT, traits, Allocator>;
92 // [syncstream.osyncstream.cons], construction and destruction
93 basic_osyncstream(streambuf_type*, const Allocator&);
94 explicit basic_osyncstream(streambuf_type* obuf)
95 : basic_osyncstream(obuf, Allocator()) {}
96 basic_osyncstream(basic_ostream<charT, traits>& os, const Allocator& allocator)
97 : basic_osyncstream(os.rdbuf(), allocator) {}
98 explicit basic_osyncstream(basic_ostream<charT, traits>& os)
99 : basic_osyncstream(os, Allocator()) {}
100 basic_osyncstream(basic_osyncstream&&) noexcept;
101 ~basic_osyncstream();
103 // [syncstream.osyncstream.assign], assignment
104 basic_osyncstream& operator=(basic_osyncstream&&);
106 // [syncstream.osyncstream.members], member functions
108 streambuf_type* get_wrapped() const noexcept;
109 syncbuf_type* rdbuf() const noexcept { return const_cast<syncbuf_type*>(addressof(sb)); }
112 syncbuf_type sb; // exposition only
118 #include <__cxx03/__config>
119 #include <__cxx03/__utility/move.h>
120 #include <__cxx03/ios>
121 #include <__cxx03/iosfwd> // required for declaration of default arguments
122 #include <__cxx03/streambuf>
123 #include <__cxx03/string>
125 #ifndef _LIBCPP_HAS_NO_THREADS
126 # include <__cxx03/map>
127 # include <__cxx03/mutex>
128 # include <__cxx03/shared_mutex>
131 // standard-mandated includes
134 #include <__cxx03/ostream>
136 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
137 # pragma GCC system_header
141 #include <__cxx03/__undef_macros>
143 _LIBCPP_BEGIN_NAMESPACE_STD
145 #if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)
147 // [syncstream.syncbuf.overview]/1
148 // Class template basic_syncbuf stores character data written to it,
149 // known as the associated output, into internal buffers allocated
150 // using the object's allocator. The associated output is transferred
151 // to the wrapped stream buffer object *wrapped when emit() is called
152 // or when the basic_syncbuf object is destroyed. Such transfers are
153 // atomic with respect to transfers by other basic_syncbuf objects
154 // with the same wrapped stream buffer object.
156 // This helper singleton is used to implement the required
157 // synchronisation guarantees.
158 # ifndef _LIBCPP_HAS_NO_THREADS
159 class __wrapped_streambuf_mutex {
160 _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex() = default;
163 __wrapped_streambuf_mutex(const __wrapped_streambuf_mutex&) = delete;
164 __wrapped_streambuf_mutex& operator=(const __wrapped_streambuf_mutex&) = delete;
166 _LIBCPP_HIDE_FROM_ABI void __inc_reference([[maybe_unused]] void* __ptr) {
167 _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
168 unique_lock __lock{__mutex_};
169 ++__lut_[reinterpret_cast<uintptr_t>(__ptr)].__count;
172 // pre: __ptr is in __lut_
173 _LIBCPP_HIDE_FROM_ABI void __dec_reference([[maybe_unused]] void* __ptr) noexcept {
174 unique_lock __lock{__mutex_};
176 auto __it = __get_it(__ptr);
177 if (__it->second.__count == 1)
180 --__it->second.__count;
184 // This function causes emit() aquire two mutexes:
186 // _ __get_it(__ptr)->second.__mutex exclusive
188 // Instead store a pointer to __get_it(__ptr)->second.__mutex when
189 // calling __inc_reference.
191 // pre: __ptr is in __lut_
192 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex> __get_lock([[maybe_unused]] void* __ptr) noexcept {
193 shared_lock __lock{__mutex_};
194 return lock_guard{__get_it(__ptr)->second.__mutex};
197 // This function is used for testing.
199 // It is allowed to call this function with a non-registered pointer.
200 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __get_count([[maybe_unused]] void* __ptr) noexcept {
201 _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
202 shared_lock __lock{__mutex_};
204 auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
205 return __it != __lut_.end() ? __it->second.__count : 0;
208 [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex& __instance() noexcept {
209 static __wrapped_streambuf_mutex __result;
219 shared_mutex __mutex_;
220 map<uintptr_t, __value> __lut_;
222 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI map<uintptr_t, __value>::iterator __get_it(void* __ptr) noexcept {
223 _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
225 auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
226 _LIBCPP_ASSERT_INTERNAL(__it != __lut_.end(), "using a wrapped streambuf that has not been registered");
227 _LIBCPP_ASSERT_INTERNAL(__it->second.__count >= 1, "found an inactive streambuf wrapper");
231 # endif // _LIBCPP_HAS_NO_THREADS
235 // The class uses a basic_string<_CharT, _Traits, _Allocator> as
236 // internal buffer. Per [syncstream.syncbuf.cons]/4
237 // Remarks: A copy of allocator is used to allocate memory for
238 // internal buffers holding the associated output.
240 // Therefore the allocator used in the constructor is passed to the
241 // basic_string. The class does not keep a copy of this allocator.
242 template <class _CharT, class _Traits, class _Allocator>
243 class _LIBCPP_TEMPLATE_VIS basic_syncbuf : public basic_streambuf<_CharT, _Traits> {
245 using char_type = _CharT;
246 using traits_type = _Traits;
247 using int_type = typename traits_type::int_type;
248 using pos_type = typename traits_type::pos_type;
249 using off_type = typename traits_type::off_type;
250 using allocator_type = _Allocator;
252 using streambuf_type = basic_streambuf<_CharT, _Traits>;
254 // [syncstream.syncbuf.cons], construction and destruction
256 _LIBCPP_HIDE_FROM_ABI explicit basic_syncbuf(streambuf_type* __obuf = nullptr)
257 : basic_syncbuf(__obuf, _Allocator()) {}
259 _LIBCPP_HIDE_FROM_ABI basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc)
260 : __wrapped_(__obuf), __str_(__alloc) {
264 _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other)
265 : __wrapped_(__other.get_wrapped()), __str_(std::move(__other.__str_)), __emit_on_sync_(__other.__emit_on_sync_) {
266 __move_common(__other);
269 _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() {
270 # ifndef _LIBCPP_HAS_NO_EXCEPTIONS
272 # endif // _LIBCPP_HAS_NO_EXCEPTIONS
274 # ifndef _LIBCPP_HAS_NO_EXCEPTIONS
277 # endif // _LIBCPP_HAS_NO_EXCEPTIONS
281 // [syncstream.syncbuf.assign], assignment and swap
283 _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) {
284 // The function is specified to call emit. This call should
285 // propagate the exception thrown.
289 __wrapped_ = __other.get_wrapped();
290 __str_ = std::move(__other.__str_);
291 __emit_on_sync_ = __other.__emit_on_sync_;
293 __move_common(__other);
298 _LIBCPP_HIDE_FROM_ABI void swap(basic_syncbuf& __other) {
299 _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
300 allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(),
301 "violates the mandated swap precondition");
303 basic_syncbuf __tmp(std::move(__other));
304 __other = std::move(*this);
305 *this = std::move(__tmp);
308 // [syncstream.syncbuf.members], member functions
310 _LIBCPP_HIDE_FROM_ABI bool emit() { return emit(false); }
312 _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; }
314 _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); }
316 _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; }
319 // [syncstream.syncbuf.virtuals], overridden virtual functions
321 _LIBCPP_HIDE_FROM_ABI_VIRTUAL
322 int sync() override {
323 if (__emit_on_sync_ && !emit(true))
328 _LIBCPP_HIDE_FROM_ABI_VIRTUAL
329 int_type overflow(int_type __c = traits_type::eof()) override {
330 if (traits_type::eq_int_type(__c, traits_type::eof()))
331 return traits_type::not_eof(__c);
333 if (this->pptr() == this->epptr()) {
334 # ifndef _LIBCPP_HAS_NO_EXCEPTIONS
337 size_t __size = __str_.size();
338 __str_.resize(__str_.capacity() + 1);
339 _LIBCPP_ASSERT_INTERNAL(__str_.size() > __size, "the buffer hasn't grown");
341 char_type* __p = static_cast<char_type*>(__str_.data());
342 this->setp(__p, __p + __str_.size());
345 # ifndef _LIBCPP_HAS_NO_EXCEPTIONS
347 return traits_type::eof();
352 return this->sputc(traits_type::to_char_type(__c));
356 streambuf_type* __wrapped_;
358 // TODO Use a more generic buffer.
359 // That buffer should be light with almost no additional headers. Then
360 // it can be use here, the __retarget_buffer, and place that use
361 // the now deprecated get_temporary_buffer
363 basic_string<_CharT, _Traits, _Allocator> __str_;
364 bool __emit_on_sync_{false};
366 _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) {
370 # ifndef _LIBCPP_HAS_NO_THREADS
371 lock_guard<mutex> __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_);
374 bool __result = true;
375 if (this->pptr() != this->pbase()) {
376 _LIBCPP_ASSERT_INTERNAL(this->pbase() && this->pptr() && this->epptr(), "all put area pointers shold be valid");
378 // The __str_ does not know how much of its buffer is used. This
379 // information is extracted from the information of the base class.
380 __result &= (__wrapped_->sputn(this->pbase(), this->pptr() - this->pbase()) != -1);
381 // Clears the buffer, but keeps the contents (and) size of the
383 this->setp(this->pbase(), this->epptr());
387 __result &= (__wrapped_->pubsync() != -1);
392 _LIBCPP_HIDE_FROM_ABI void __move_common(basic_syncbuf& __other) {
393 // Adjust the put area pointers to our buffer.
394 char_type* __p = static_cast<char_type*>(__str_.data());
395 this->setp(__p, __p + __str_.size());
396 this->pbump(__other.pptr() - __other.pbase());
398 // Clear __other_ so the destructor will act as a NOP.
399 __other.setp(nullptr, nullptr);
400 __other.__wrapped_ = nullptr;
403 _LIBCPP_HIDE_FROM_ABI void __inc_reference() {
404 # ifndef _LIBCPP_HAS_NO_THREADS
406 __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_);
410 _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept {
411 # ifndef _LIBCPP_HAS_NO_THREADS
413 __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_);
419 # ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
423 // [syncstream.syncbuf.special], specialized algorithms
424 template <class _CharT, class _Traits, class _Allocator>
425 _LIBCPP_HIDE_FROM_ABI void
426 swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) {
432 template <class _CharT, class _Traits, class _Allocator>
433 class _LIBCPP_TEMPLATE_VIS basic_osyncstream : public basic_ostream<_CharT, _Traits> {
435 using char_type = _CharT;
436 using traits_type = _Traits;
437 using int_type = typename traits_type::int_type;
438 using pos_type = typename traits_type::pos_type;
439 using off_type = typename traits_type::off_type;
441 using allocator_type = _Allocator;
442 using streambuf_type = basic_streambuf<char_type, traits_type>;
443 using syncbuf_type = basic_syncbuf<char_type, traits_type, allocator_type>;
445 // [syncstream.osyncstream.cons], construction and destruction
447 _LIBCPP_HIDE_FROM_ABI basic_osyncstream(streambuf_type* __obuf, allocator_type const& __alloc)
448 : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(__obuf, __alloc) {}
450 _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(streambuf_type* __obuf)
451 : basic_osyncstream(__obuf, allocator_type()) {}
453 _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_ostream<char_type, traits_type>& __os, allocator_type const& __alloc)
454 : basic_osyncstream(__os.rdbuf(), __alloc) {}
456 _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
457 : basic_osyncstream(__os, allocator_type()) {}
459 _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_osyncstream&& __other) noexcept
460 : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(std::move(__other.__sb_)) {
461 this->set_rdbuf(std::addressof(__sb_));
464 // [syncstream.osyncstream.assign], assignment
466 _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default;
468 // [syncstream.osyncstream.members], member functions
470 _LIBCPP_HIDE_FROM_ABI void emit() {
471 // The basic_ostream::put places the sentry in a try
472 // catch, this does not match the wording of the standard
473 // [ostream.unformatted]
474 // TODO validate other unformatted output functions.
475 typename basic_ostream<char_type, traits_type>::sentry __s(*this);
477 # ifndef _LIBCPP_HAS_NO_EXCEPTIONS
481 if (__sb_.emit() == false)
482 this->setstate(ios::badbit);
483 # ifndef _LIBCPP_HAS_NO_EXCEPTIONS
485 this->__set_badbit_and_consider_rethrow();
491 _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); }
493 _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept {
494 return const_cast<syncbuf_type*>(std::addressof(__sb_));
501 using std::osyncstream;
502 # ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
503 using std::wosyncstream;
506 #endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)
508 _LIBCPP_END_NAMESPACE_STD
512 #endif // _LIBCPP_SYNCSTREAM