1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
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
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP_SYNCSTREAM
11#define _LIBCPP_SYNCSTREAM
12
13/*
14 syncstream synopsis
15
16#include <ostream> // see [ostream.syn]
17
18namespace std {
19 template<class charT, class traits, class Allocator>
20 class basic_syncbuf;
21
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>&);
26
27 using syncbuf = basic_syncbuf<char>;
28 using wsyncbuf = basic_syncbuf<wchar_t>;
29
30 template<class charT, class traits, class Allocator>
31 class basic_osyncstream;
32
33 using osyncstream = basic_osyncstream<char>;
34 using wosyncstream = basic_osyncstream<wchar_t>;
35
36 template<class charT, class traits, class Allocator>
37 class basic_syncbuf : public basic_streambuf<charT, traits> {
38 public:
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;
45
46 using streambuf_type = basic_streambuf<charT, traits>;
47
48 // [syncstream.syncbuf.cons], construction and destruction
49 basic_syncbuf()
50 : basic_syncbuf(nullptr) {}
51 explicit basic_syncbuf(streambuf_type* obuf)
52 : basic_syncbuf(obuf, Allocator()) {}
53 basic_syncbuf(streambuf_type*, const Allocator&);
54 basic_syncbuf(basic_syncbuf&&);
55 ~basic_syncbuf();
56
57 // [syncstream.syncbuf.assign], assignment and swap
58 basic_syncbuf& operator=(basic_syncbuf&&);
59 void swap(basic_syncbuf&);
60
61 // [syncstream.syncbuf.members], member functions
62 bool emit();
63 streambuf_type* get_wrapped() const noexcept;
64 allocator_type get_allocator() const noexcept;
65 void set_emit_on_sync(bool) noexcept;
66
67 protected:
68 // [syncstream.syncbuf.virtuals], overridden virtual functions
69 int sync() override;
70
71 private:
72 streambuf_type* wrapped; // exposition only
73 bool emit_on_sync{}; // exposition only
74 };
75
76 // [syncstream.syncbuf.special], specialized algorithms
77 template<class charT, class traits, class Allocator>
78 void swap(basic_syncbuf<charT, traits, Allocator>&,
79 basic_syncbuf<charT, traits, Allocator>&);
80
81 template<class charT, class traits, class Allocator>
82 class basic_osyncstream : public basic_ostream<charT, traits> {
83 public:
84 using char_type = charT;
85 using int_type = typename traits::int_type;
86 using pos_type = typename traits::pos_type;
87 using off_type = typename traits::off_type;
88 using traits_type = traits;
89
90 using allocator_type = Allocator;
91 using streambuf_type = basic_streambuf<charT, traits>;
92 using syncbuf_type = basic_syncbuf<charT, traits, Allocator>;
93
94 // [syncstream.osyncstream.cons], construction and destruction
95 basic_osyncstream(streambuf_type*, const Allocator&);
96 explicit basic_osyncstream(streambuf_type* obuf)
97 : basic_osyncstream(obuf, Allocator()) {}
98 basic_osyncstream(basic_ostream<charT, traits>& os, const Allocator& allocator)
99 : basic_osyncstream(os.rdbuf(), allocator) {}
100 explicit basic_osyncstream(basic_ostream<charT, traits>& os)
101 : basic_osyncstream(os, Allocator()) {}
102 basic_osyncstream(basic_osyncstream&&) noexcept;
103 ~basic_osyncstream();
104
105 // [syncstream.osyncstream.assign], assignment
106 basic_osyncstream& operator=(basic_osyncstream&&);
107
108 // [syncstream.osyncstream.members], member functions
109 void emit();
110 streambuf_type* get_wrapped() const noexcept;
111 syncbuf_type* rdbuf() const noexcept { return const_cast<syncbuf_type*>(addressof(sb)); }
112
113 private:
114 syncbuf_type sb; // exposition only
115 };
116}
117
118*/
119
120#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
121# include <__cxx03/__config>
122#else
123# include <__config>
124
125// standard-mandated includes
126
127// [syncstream.syn]
128# include <ostream>
129
130# if _LIBCPP_HAS_LOCALIZATION
131
132# include <__mutex/lock_guard.h>
133# include <__utility/move.h>
134# include <ios>
135# include <iosfwd> // required for declaration of default arguments
136# include <streambuf>
137# include <string>
138# if _LIBCPP_HAS_THREADS
139# include <map>
140# include <shared_mutex>
141# endif
142
143# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
144# pragma GCC system_header
145# endif
146
147_LIBCPP_PUSH_MACROS
148# include <__undef_macros>
149
150# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM
151
152_LIBCPP_BEGIN_NAMESPACE_STD
153
154// [syncstream.syncbuf.overview]/1
155// Class template basic_syncbuf stores character data written to it,
156// known as the associated output, into internal buffers allocated
157// using the object's allocator. The associated output is transferred
158// to the wrapped stream buffer object *wrapped when emit() is called
159// or when the basic_syncbuf object is destroyed. Such transfers are
160// atomic with respect to transfers by other basic_syncbuf objects
161// with the same wrapped stream buffer object.
162//
163// This helper singleton is used to implement the required
164// synchronisation guarantees.
165# if _LIBCPP_HAS_THREADS
166class __wrapped_streambuf_mutex {
167 _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex() = default;
168
169public:
170 __wrapped_streambuf_mutex(const __wrapped_streambuf_mutex&) = delete;
171 __wrapped_streambuf_mutex& operator=(const __wrapped_streambuf_mutex&) = delete;
172
173 _LIBCPP_HIDE_FROM_ABI void __inc_reference([[maybe_unused]] void* __ptr) {
174 _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
175 unique_lock __lock{__mutex_};
176 ++__lut_[reinterpret_cast<uintptr_t>(__ptr)].__count;
177 }
178
179 // pre: __ptr is in __lut_
180 _LIBCPP_HIDE_FROM_ABI void __dec_reference([[maybe_unused]] void* __ptr) noexcept {
181 unique_lock __lock{__mutex_};
182
183 auto __it = __get_it(__ptr);
184 if (__it->second.__count == 1)
185 __lut_.erase(__it);
186 else
187 --__it->second.__count;
188 }
189
190 // TODO
191 // This function causes emit() aquire two mutexes:
192 // - __mutex_ shared
193 // _ __get_it(__ptr)->second.__mutex exclusive
194 //
195 // Instead store a pointer to __get_it(__ptr)->second.__mutex when
196 // calling __inc_reference.
197 //
198 // pre: __ptr is in __lut_
199 _LIBCPP_NO_THREAD_SAFETY_ANALYSIS [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex>
200 __get_lock([[maybe_unused]] void* __ptr) noexcept {
201 shared_lock __lock{__mutex_};
202 return lock_guard{__get_it(__ptr)->second.__mutex};
203 }
204
205 // This function is used for testing.
206 //
207 // It is allowed to call this function with a non-registered pointer.
208 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __get_count([[maybe_unused]] void* __ptr) noexcept {
209 _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
210 shared_lock __lock{__mutex_};
211
212 auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
213 return __it != __lut_.end() ? __it->second.__count : 0;
214 }
215
216 [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex& __instance() noexcept {
217 static __wrapped_streambuf_mutex __result;
218 return __result;
219 }
220
221private:
222 struct __value {
223 mutex __mutex;
224 size_t __count{0};
225 };
226
227 shared_mutex __mutex_;
228 map<uintptr_t, __value> __lut_;
229
230 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI map<uintptr_t, __value>::iterator __get_it(void* __ptr) noexcept {
231 _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
232
233 auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
234 _LIBCPP_ASSERT_INTERNAL(__it != __lut_.end(), "using a wrapped streambuf that has not been registered");
235 _LIBCPP_ASSERT_INTERNAL(__it->second.__count >= 1, "found an inactive streambuf wrapper");
236 return __it;
237 }
238};
239# endif // _LIBCPP_HAS_THREADS
240
241// basic_syncbuf
242
243// The class uses a basic_string<_CharT, _Traits, _Allocator> as
244// internal buffer. Per [syncstream.syncbuf.cons]/4
245// Remarks: A copy of allocator is used to allocate memory for
246// internal buffers holding the associated output.
247//
248// Therefore the allocator used in the constructor is passed to the
249// basic_string. The class does not keep a copy of this allocator.
250template <class _CharT, class _Traits, class _Allocator>
251class basic_syncbuf : public basic_streambuf<_CharT, _Traits> {
252public:
253 using char_type = _CharT;
254 using traits_type = _Traits;
255 using int_type = typename traits_type::int_type;
256 using pos_type = typename traits_type::pos_type;
257 using off_type = typename traits_type::off_type;
258 using allocator_type = _Allocator;
259
260 using streambuf_type = basic_streambuf<_CharT, _Traits>;
261
262 // [syncstream.syncbuf.cons], construction and destruction
263
264 _LIBCPP_HIDE_FROM_ABI basic_syncbuf() : basic_syncbuf(nullptr) {}
265
266 _LIBCPP_HIDE_FROM_ABI explicit basic_syncbuf(streambuf_type* __obuf) : basic_syncbuf(__obuf, _Allocator()) {}
267
268 _LIBCPP_HIDE_FROM_ABI basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc)
269 : __wrapped_(__obuf), __str_(__alloc) {
270 __inc_reference();
271 }
272
273 _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other)
274 : __wrapped_(__other.get_wrapped()), __str_(std::move(__other.__str_)), __emit_on_sync_(__other.__emit_on_sync_) {
275 __move_common(__other);
276 }
277
278 _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() {
279# if _LIBCPP_HAS_EXCEPTIONS
280 try {
281# endif // _LIBCPP_HAS_EXCEPTIONS
282 emit();
283# if _LIBCPP_HAS_EXCEPTIONS
284 } catch (...) {
285 }
286# endif // _LIBCPP_HAS_EXCEPTIONS
287 __dec_reference();
288 }
289
290 // [syncstream.syncbuf.assign], assignment and swap
291
292 _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) {
293 // The function is specified to call emit. This call should
294 // propagate the exception thrown.
295 emit();
296 __dec_reference();
297
298 __wrapped_ = __other.get_wrapped();
299 __str_ = std::move(__other.__str_);
300 __emit_on_sync_ = __other.__emit_on_sync_;
301
302 __move_common(__other);
303
304 return *this;
305 }
306
307 _LIBCPP_HIDE_FROM_ABI void swap(basic_syncbuf& __other) {
308 _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
309 allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(),
310 "violates the mandated swap precondition");
311
312 basic_syncbuf __tmp(std::move(__other));
313 __other = std::move(*this);
314 *this = std::move(__tmp);
315 }
316
317 // [syncstream.syncbuf.members], member functions
318
319 _LIBCPP_HIDE_FROM_ABI bool emit() { return emit(false); }
320
321 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; }
322
323 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); }
324
325 _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; }
326
327protected:
328 // [syncstream.syncbuf.virtuals], overridden virtual functions
329
330 _LIBCPP_HIDE_FROM_ABI_VIRTUAL
331 int sync() override {
332 if (__emit_on_sync_ && !emit(true))
333 return -1;
334 return 0;
335 }
336
337 _LIBCPP_HIDE_FROM_ABI_VIRTUAL
338 int_type overflow(int_type __c = traits_type::eof()) override {
339 if (traits_type::eq_int_type(__c, traits_type::eof()))
340 return traits_type::not_eof(__c);
341
342 if (this->pptr() == this->epptr()) {
343# if _LIBCPP_HAS_EXCEPTIONS
344 try {
345# endif
346 size_t __size = __str_.size();
347 __str_.resize(__str_.capacity() + 1);
348 _LIBCPP_ASSERT_INTERNAL(__str_.size() > __size, "the buffer hasn't grown");
349
350 char_type* __p = static_cast<char_type*>(__str_.data());
351 this->setp(__p, __p + __str_.size());
352 this->pbump(__size);
353
354# if _LIBCPP_HAS_EXCEPTIONS
355 } catch (...) {
356 return traits_type::eof();
357 }
358# endif
359 }
360
361 return this->sputc(traits_type::to_char_type(__c));
362 }
363
364private:
365 streambuf_type* __wrapped_;
366
367 // TODO Use a more generic buffer.
368 // That buffer should be light with almost no additional headers. Then
369 // it can be use here, the __retarget_buffer, and place that use
370 // the now removed get_temporary_buffer
371
372 basic_string<_CharT, _Traits, _Allocator> __str_;
373 bool __emit_on_sync_{false};
374
375 _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) {
376 if (!__wrapped_)
377 return false;
378
379# if _LIBCPP_HAS_THREADS
380 lock_guard<mutex> __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_);
381# endif
382
383 bool __result = true;
384 if (this->pptr() != this->pbase()) {
385 _LIBCPP_ASSERT_INTERNAL(this->pbase() && this->pptr() && this->epptr(), "all put area pointers shold be valid");
386
387 // The __str_ does not know how much of its buffer is used. This
388 // information is extracted from the information of the base class.
389 __result &= (__wrapped_->sputn(this->pbase(), this->pptr() - this->pbase()) != -1);
390 // Clears the buffer, but keeps the contents (and) size of the
391 // internal buffer.
392 this->setp(this->pbase(), this->epptr());
393 }
394
395 if (__flush)
396 __result &= (__wrapped_->pubsync() != -1);
397
398 return __result;
399 }
400
401 _LIBCPP_HIDE_FROM_ABI void __move_common(basic_syncbuf& __other) {
402 // Adjust the put area pointers to our buffer.
403 char_type* __p = static_cast<char_type*>(__str_.data());
404 this->setp(__p, __p + __str_.size());
405 this->pbump(__other.pptr() - __other.pbase());
406
407 // Clear __other_ so the destructor will act as a NOP.
408 __other.setp(nullptr, nullptr);
409 __other.__wrapped_ = nullptr;
410 }
411
412 _LIBCPP_HIDE_FROM_ABI void __inc_reference() {
413# if _LIBCPP_HAS_THREADS
414 if (__wrapped_)
415 __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_);
416# endif
417 }
418
419 _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept {
420# if _LIBCPP_HAS_THREADS
421 if (__wrapped_)
422 __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_);
423# endif
424 }
425};
426
427using std::syncbuf;
428# if _LIBCPP_HAS_WIDE_CHARACTERS
429using std::wsyncbuf;
430# endif
431
432// [syncstream.syncbuf.special], specialized algorithms
433template <class _CharT, class _Traits, class _Allocator>
434_LIBCPP_HIDE_FROM_ABI void
435swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) {
436 __lhs.swap(__rhs);
437}
438
439// basic_osyncstream
440
441template <class _CharT, class _Traits, class _Allocator>
442class basic_osyncstream : public basic_ostream<_CharT, _Traits> {
443public:
444 using char_type = _CharT;
445 using traits_type = _Traits;
446 using int_type = typename traits_type::int_type;
447 using pos_type = typename traits_type::pos_type;
448 using off_type = typename traits_type::off_type;
449
450 using allocator_type = _Allocator;
451 using streambuf_type = basic_streambuf<char_type, traits_type>;
452 using syncbuf_type = basic_syncbuf<char_type, traits_type, allocator_type>;
453
454 // [syncstream.osyncstream.cons], construction and destruction
455
456 _LIBCPP_HIDE_FROM_ABI basic_osyncstream(streambuf_type* __obuf, allocator_type const& __alloc)
457 : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(__obuf, __alloc) {}
458
459 _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(streambuf_type* __obuf)
460 : basic_osyncstream(__obuf, allocator_type()) {}
461
462 _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_ostream<char_type, traits_type>& __os, allocator_type const& __alloc)
463 : basic_osyncstream(__os.rdbuf(), __alloc) {}
464
465 _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
466 : basic_osyncstream(__os, allocator_type()) {}
467
468 _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_osyncstream&& __other) noexcept
469 : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(std::move(__other.__sb_)) {
470 this->set_rdbuf(std::addressof(__sb_));
471 }
472
473 // [syncstream.osyncstream.assign], assignment
474
475 _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default;
476
477 // [syncstream.osyncstream.members], member functions
478
479 _LIBCPP_HIDE_FROM_ABI void emit() {
480 // The basic_ostream::put places the sentry in a try
481 // catch, this does not match the wording of the standard
482 // [ostream.unformatted]
483 // TODO validate other unformatted output functions.
484 typename basic_ostream<char_type, traits_type>::sentry __s(*this);
485 if (__s) {
486# if _LIBCPP_HAS_EXCEPTIONS
487 try {
488# endif
489
490 if (__sb_.emit() == false)
491 this->setstate(ios::badbit);
492# if _LIBCPP_HAS_EXCEPTIONS
493 } catch (...) {
494 this->__set_badbit_and_consider_rethrow();
495 }
496# endif
497 }
498 }
499
500 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); }
501
502 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept {
503 return const_cast<syncbuf_type*>(std::addressof(__sb_));
504 }
505
506private:
507 syncbuf_type __sb_;
508};
509
510using std::osyncstream;
511# if _LIBCPP_HAS_WIDE_CHARACTERS
512using std::wosyncstream;
513# endif
514
515_LIBCPP_END_NAMESPACE_STD
516
517# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM
518
519_LIBCPP_POP_MACROS
520
521# endif // _LIBCPP_HAS_LOCALIZATION
522#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
523
524#endif // _LIBCPP_SYNCSTREAM