xref: /llvm-project/libcxx/include/syncstream (revision 24e70e3930724ce499ad05d669bfbc4423c542e0)
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/syncstream>
122#else
123#  include <__config>
124
125#  if _LIBCPP_HAS_LOCALIZATION
126
127#    include <__mutex/lock_guard.h>
128#    include <__utility/move.h>
129#    include <ios>
130#    include <iosfwd> // required for declaration of default arguments
131#    include <streambuf>
132#    include <string>
133
134#    if _LIBCPP_HAS_THREADS
135#      include <map>
136#      include <shared_mutex>
137#    endif
138
139// standard-mandated includes
140
141// [syncstream.syn]
142#    include <ostream>
143
144#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
145#      pragma GCC system_header
146#    endif
147
148_LIBCPP_PUSH_MACROS
149#    include <__undef_macros>
150
151_LIBCPP_BEGIN_NAMESPACE_STD
152
153#    if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM
154
155// [syncstream.syncbuf.overview]/1
156//   Class template basic_syncbuf stores character data written to it,
157//   known as the associated output, into internal buffers allocated
158//   using the object's allocator. The associated output is transferred
159//   to the wrapped stream buffer object *wrapped when emit() is called
160//   or when the basic_syncbuf object is destroyed. Such transfers are
161//   atomic with respect to transfers by other basic_syncbuf objects
162//   with the same wrapped stream buffer object.
163//
164// This helper singleton is used to implement the required
165// synchronisation guarantees.
166#      if _LIBCPP_HAS_THREADS
167class __wrapped_streambuf_mutex {
168  _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex() = default;
169
170public:
171  __wrapped_streambuf_mutex(const __wrapped_streambuf_mutex&)            = delete;
172  __wrapped_streambuf_mutex& operator=(const __wrapped_streambuf_mutex&) = delete;
173
174  _LIBCPP_HIDE_FROM_ABI void __inc_reference([[maybe_unused]] void* __ptr) {
175    _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
176    unique_lock __lock{__mutex_};
177    ++__lut_[reinterpret_cast<uintptr_t>(__ptr)].__count;
178  }
179
180  // pre: __ptr is in __lut_
181  _LIBCPP_HIDE_FROM_ABI void __dec_reference([[maybe_unused]] void* __ptr) noexcept {
182    unique_lock __lock{__mutex_};
183
184    auto __it = __get_it(__ptr);
185    if (__it->second.__count == 1)
186      __lut_.erase(__it);
187    else
188      --__it->second.__count;
189  }
190
191  // TODO
192  // This function causes emit() aquire two mutexes:
193  // - __mutex_ shared
194  // _ __get_it(__ptr)->second.__mutex exclusive
195  //
196  // Instead store a pointer to __get_it(__ptr)->second.__mutex when
197  // calling __inc_reference.
198  //
199  // pre: __ptr is in __lut_
200  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex> __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 _LIBCPP_TEMPLATE_VIS 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  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; }
322
323  _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 _LIBCPP_TEMPLATE_VIS 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  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); }
501
502  _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#    endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM
516
517_LIBCPP_END_NAMESPACE_STD
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
525