xref: /freebsd/contrib/llvm-project/libcxx/include/syncstream (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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        explicit basic_syncbuf(streambuf_type* obuf = nullptr)
50          : basic_syncbuf(obuf, Allocator()) {}
51        basic_syncbuf(streambuf_type*, const Allocator&);
52        basic_syncbuf(basic_syncbuf&&);
53        ~basic_syncbuf();
54
55        // [syncstream.syncbuf.assign], assignment and swap
56        basic_syncbuf& operator=(basic_syncbuf&&);
57        void swap(basic_syncbuf&);
58
59        // [syncstream.syncbuf.members], member functions
60        bool emit();
61        streambuf_type* get_wrapped() const noexcept;
62        allocator_type get_allocator() const noexcept;
63        void set_emit_on_sync(bool) noexcept;
64
65    protected:
66        // [syncstream.syncbuf.virtuals], overridden virtual functions
67        int sync() override;
68
69    private:
70        streambuf_type* wrapped;    // exposition only
71        bool emit_on_sync{};        // exposition only
72    };
73
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>&);
78
79    template<class charT, class traits, class Allocator>
80    class basic_osyncstream : public basic_ostream<charT, traits> {
81    public:
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;
87
88        using allocator_type = Allocator;
89        using streambuf_type = basic_streambuf<charT, traits>;
90        using syncbuf_type   = basic_syncbuf<charT, traits, Allocator>;
91
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();
102
103        // [syncstream.osyncstream.assign], assignment
104        basic_osyncstream& operator=(basic_osyncstream&&);
105
106        // [syncstream.osyncstream.members], member functions
107        void emit();
108        streambuf_type* get_wrapped() const noexcept;
109        syncbuf_type* rdbuf() const noexcept { return const_cast<syncbuf_type*>(addressof(sb)); }
110
111    private:
112        syncbuf_type sb;    // exposition only
113    };
114}
115
116*/
117
118#include <__config>
119#include <__utility/move.h>
120#include <ios>
121#include <iosfwd> // required for declaration of default arguments
122#include <streambuf>
123#include <string>
124
125#ifndef _LIBCPP_HAS_NO_THREADS
126#  include <map>
127#  include <mutex>
128#  include <shared_mutex>
129#endif
130
131// standard-mandated includes
132
133// [syncstream.syn]
134#include <ostream>
135
136#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
137#  pragma GCC system_header
138#endif
139
140_LIBCPP_PUSH_MACROS
141#include <__undef_macros>
142
143_LIBCPP_BEGIN_NAMESPACE_STD
144
145#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)
146
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.
155//
156// This helper singleton is used to implement the required
157// synchronisation guarantees.
158#  ifndef _LIBCPP_HAS_NO_THREADS
159class __wrapped_streambuf_mutex {
160  _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex() = default;
161
162public:
163  __wrapped_streambuf_mutex(const __wrapped_streambuf_mutex&)            = delete;
164  __wrapped_streambuf_mutex& operator=(const __wrapped_streambuf_mutex&) = delete;
165
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;
170  }
171
172  // pre: __ptr is in __lut_
173  _LIBCPP_HIDE_FROM_ABI void __dec_reference([[maybe_unused]] void* __ptr) noexcept {
174    unique_lock __lock{__mutex_};
175
176    auto __it = __get_it(__ptr);
177    if (__it->second.__count == 1)
178      __lut_.erase(__it);
179    else
180      --__it->second.__count;
181  }
182
183  // TODO
184  // This function causes emit() aquire two mutexes:
185  // - __mutex_ shared
186  // _ __get_it(__ptr)->second.__mutex exclusive
187  //
188  // Instead store a pointer to __get_it(__ptr)->second.__mutex when
189  // calling __inc_reference.
190  //
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};
195  }
196
197  // This function is used for testing.
198  //
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_};
203
204    auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
205    return __it != __lut_.end() ? __it->second.__count : 0;
206  }
207
208  [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex& __instance() noexcept {
209    static __wrapped_streambuf_mutex __result;
210    return __result;
211  }
212
213private:
214  struct __value {
215    mutex __mutex;
216    size_t __count{0};
217  };
218
219  shared_mutex __mutex_;
220  map<uintptr_t, __value> __lut_;
221
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");
224
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");
228    return __it;
229  }
230};
231#  endif // _LIBCPP_HAS_NO_THREADS
232
233// basic_syncbuf
234
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.
239//
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.
242template <class _CharT, class _Traits, class _Allocator>
243class _LIBCPP_TEMPLATE_VIS basic_syncbuf : public basic_streambuf<_CharT, _Traits> {
244public:
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;
251
252  using streambuf_type = basic_streambuf<_CharT, _Traits>;
253
254  // [syncstream.syncbuf.cons], construction and destruction
255
256  _LIBCPP_HIDE_FROM_ABI explicit basic_syncbuf(streambuf_type* __obuf = nullptr)
257      : basic_syncbuf(__obuf, _Allocator()) {}
258
259  _LIBCPP_HIDE_FROM_ABI basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc)
260      : __wrapped_(__obuf), __str_(__alloc) {
261    __inc_reference();
262  }
263
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);
267  }
268
269  _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() {
270#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
271    try {
272#  endif // _LIBCPP_HAS_NO_EXCEPTIONS
273      emit();
274#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
275    } catch (...) {
276    }
277#  endif // _LIBCPP_HAS_NO_EXCEPTIONS
278    __dec_reference();
279  }
280
281  // [syncstream.syncbuf.assign], assignment and swap
282
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.
286    emit();
287    __dec_reference();
288
289    __wrapped_      = __other.get_wrapped();
290    __str_          = std::move(__other.__str_);
291    __emit_on_sync_ = __other.__emit_on_sync_;
292
293    __move_common(__other);
294
295    return *this;
296  }
297
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");
302
303    basic_syncbuf __tmp(std::move(__other));
304    __other = std::move(*this);
305    *this   = std::move(__tmp);
306  }
307
308  // [syncstream.syncbuf.members], member functions
309
310  _LIBCPP_HIDE_FROM_ABI bool emit() { return emit(false); }
311
312  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; }
313
314  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); }
315
316  _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; }
317
318protected:
319  // [syncstream.syncbuf.virtuals], overridden virtual functions
320
321  _LIBCPP_HIDE_FROM_ABI_VIRTUAL
322  int sync() override {
323    if (__emit_on_sync_ && !emit(true))
324      return -1;
325    return 0;
326  }
327
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);
332
333    if (this->pptr() == this->epptr()) {
334#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
335      try {
336#  endif
337        size_t __size = __str_.size();
338        __str_.resize(__str_.capacity() + 1);
339        _LIBCPP_ASSERT_INTERNAL(__str_.size() > __size, "the buffer hasn't grown");
340
341        char_type* __p = static_cast<char_type*>(__str_.data());
342        this->setp(__p, __p + __str_.size());
343        this->pbump(__size);
344
345#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
346      } catch (...) {
347        return traits_type::eof();
348      }
349#  endif
350    }
351
352    return this->sputc(traits_type::to_char_type(__c));
353  }
354
355private:
356  streambuf_type* __wrapped_;
357
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
362
363  basic_string<_CharT, _Traits, _Allocator> __str_;
364  bool __emit_on_sync_{false};
365
366  _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) {
367    if (!__wrapped_)
368      return false;
369
370#  ifndef _LIBCPP_HAS_NO_THREADS
371    lock_guard<mutex> __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_);
372#  endif
373
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");
377
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
382      // internal buffer.
383      this->setp(this->pbase(), this->epptr());
384    }
385
386    if (__flush)
387      __result &= (__wrapped_->pubsync() != -1);
388
389    return __result;
390  }
391
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());
397
398    // Clear __other_ so the destructor will act as a NOP.
399    __other.setp(nullptr, nullptr);
400    __other.__wrapped_ = nullptr;
401  }
402
403  _LIBCPP_HIDE_FROM_ABI void __inc_reference() {
404#  ifndef _LIBCPP_HAS_NO_THREADS
405    if (__wrapped_)
406      __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_);
407#  endif
408  }
409
410  _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept {
411#  ifndef _LIBCPP_HAS_NO_THREADS
412    if (__wrapped_)
413      __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_);
414#  endif
415  }
416};
417
418using std::syncbuf;
419#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
420using std::wsyncbuf;
421#  endif
422
423// [syncstream.syncbuf.special], specialized algorithms
424template <class _CharT, class _Traits, class _Allocator>
425_LIBCPP_HIDE_FROM_ABI void
426swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) {
427  __lhs.swap(__rhs);
428}
429
430// basic_osyncstream
431
432template <class _CharT, class _Traits, class _Allocator>
433class _LIBCPP_TEMPLATE_VIS basic_osyncstream : public basic_ostream<_CharT, _Traits> {
434public:
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;
440
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>;
444
445  // [syncstream.osyncstream.cons], construction and destruction
446
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) {}
449
450  _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(streambuf_type* __obuf)
451      : basic_osyncstream(__obuf, allocator_type()) {}
452
453  _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_ostream<char_type, traits_type>& __os, allocator_type const& __alloc)
454      : basic_osyncstream(__os.rdbuf(), __alloc) {}
455
456  _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
457      : basic_osyncstream(__os, allocator_type()) {}
458
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_));
462  }
463
464  // [syncstream.osyncstream.assign], assignment
465
466  _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default;
467
468  // [syncstream.osyncstream.members], member functions
469
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);
476    if (__s) {
477#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
478      try {
479#  endif
480
481        if (__sb_.emit() == false)
482          this->setstate(ios::badbit);
483#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
484      } catch (...) {
485        this->__set_badbit_and_consider_rethrow();
486      }
487#  endif
488    }
489  }
490
491  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); }
492
493  _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept {
494    return const_cast<syncbuf_type*>(std::addressof(__sb_));
495  }
496
497private:
498  syncbuf_type __sb_;
499};
500
501using std::osyncstream;
502#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
503using std::wosyncstream;
504#  endif
505
506#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)
507
508_LIBCPP_END_NAMESPACE_STD
509
510_LIBCPP_POP_MACROS
511
512#endif // _LIBCPP_SYNCSTREAM
513