master
  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_LIBCPP_BEGIN_NAMESPACE_STD
151
152#    if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM
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  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex> __get_lock([[maybe_unused]] void* __ptr) noexcept {
200    shared_lock __lock{__mutex_};
201    return lock_guard{__get_it(__ptr)->second.__mutex};
202  }
203
204  // This function is used for testing.
205  //
206  // It is allowed to call this function with a non-registered pointer.
207  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __get_count([[maybe_unused]] void* __ptr) noexcept {
208    _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
209    shared_lock __lock{__mutex_};
210
211    auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
212    return __it != __lut_.end() ? __it->second.__count : 0;
213  }
214
215  [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex& __instance() noexcept {
216    static __wrapped_streambuf_mutex __result;
217    return __result;
218  }
219
220private:
221  struct __value {
222    mutex __mutex;
223    size_t __count{0};
224  };
225
226  shared_mutex __mutex_;
227  map<uintptr_t, __value> __lut_;
228
229  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI map<uintptr_t, __value>::iterator __get_it(void* __ptr) noexcept {
230    _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
231
232    auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
233    _LIBCPP_ASSERT_INTERNAL(__it != __lut_.end(), "using a wrapped streambuf that has not been registered");
234    _LIBCPP_ASSERT_INTERNAL(__it->second.__count >= 1, "found an inactive streambuf wrapper");
235    return __it;
236  }
237};
238#      endif // _LIBCPP_HAS_THREADS
239
240// basic_syncbuf
241
242// The class uses a basic_string<_CharT, _Traits, _Allocator> as
243// internal buffer. Per [syncstream.syncbuf.cons]/4
244//   Remarks: A copy of allocator is used to allocate memory for
245//   internal buffers holding the associated output.
246//
247// Therefore the allocator used in the constructor is passed to the
248// basic_string. The class does not keep a copy of this allocator.
249template <class _CharT, class _Traits, class _Allocator>
250class basic_syncbuf : public basic_streambuf<_CharT, _Traits> {
251public:
252  using char_type      = _CharT;
253  using traits_type    = _Traits;
254  using int_type       = typename traits_type::int_type;
255  using pos_type       = typename traits_type::pos_type;
256  using off_type       = typename traits_type::off_type;
257  using allocator_type = _Allocator;
258
259  using streambuf_type = basic_streambuf<_CharT, _Traits>;
260
261  // [syncstream.syncbuf.cons], construction and destruction
262
263  _LIBCPP_HIDE_FROM_ABI basic_syncbuf() : basic_syncbuf(nullptr) {}
264
265  _LIBCPP_HIDE_FROM_ABI explicit basic_syncbuf(streambuf_type* __obuf) : basic_syncbuf(__obuf, _Allocator()) {}
266
267  _LIBCPP_HIDE_FROM_ABI basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc)
268      : __wrapped_(__obuf), __str_(__alloc) {
269    __inc_reference();
270  }
271
272  _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other)
273      : __wrapped_(__other.get_wrapped()), __str_(std::move(__other.__str_)), __emit_on_sync_(__other.__emit_on_sync_) {
274    __move_common(__other);
275  }
276
277  _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() {
278#      if _LIBCPP_HAS_EXCEPTIONS
279    try {
280#      endif // _LIBCPP_HAS_EXCEPTIONS
281      emit();
282#      if _LIBCPP_HAS_EXCEPTIONS
283    } catch (...) {
284    }
285#      endif // _LIBCPP_HAS_EXCEPTIONS
286    __dec_reference();
287  }
288
289  // [syncstream.syncbuf.assign], assignment and swap
290
291  _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) {
292    // The function is specified to call emit. This call should
293    // propagate the exception thrown.
294    emit();
295    __dec_reference();
296
297    __wrapped_      = __other.get_wrapped();
298    __str_          = std::move(__other.__str_);
299    __emit_on_sync_ = __other.__emit_on_sync_;
300
301    __move_common(__other);
302
303    return *this;
304  }
305
306  _LIBCPP_HIDE_FROM_ABI void swap(basic_syncbuf& __other) {
307    _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
308        allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(),
309        "violates the mandated swap precondition");
310
311    basic_syncbuf __tmp(std::move(__other));
312    __other = std::move(*this);
313    *this   = std::move(__tmp);
314  }
315
316  // [syncstream.syncbuf.members], member functions
317
318  _LIBCPP_HIDE_FROM_ABI bool emit() { return emit(false); }
319
320  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; }
321
322  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); }
323
324  _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; }
325
326protected:
327  // [syncstream.syncbuf.virtuals], overridden virtual functions
328
329  _LIBCPP_HIDE_FROM_ABI_VIRTUAL
330  int sync() override {
331    if (__emit_on_sync_ && !emit(true))
332      return -1;
333    return 0;
334  }
335
336  _LIBCPP_HIDE_FROM_ABI_VIRTUAL
337  int_type overflow(int_type __c = traits_type::eof()) override {
338    if (traits_type::eq_int_type(__c, traits_type::eof()))
339      return traits_type::not_eof(__c);
340
341    if (this->pptr() == this->epptr()) {
342#      if _LIBCPP_HAS_EXCEPTIONS
343      try {
344#      endif
345        size_t __size = __str_.size();
346        __str_.resize(__str_.capacity() + 1);
347        _LIBCPP_ASSERT_INTERNAL(__str_.size() > __size, "the buffer hasn't grown");
348
349        char_type* __p = static_cast<char_type*>(__str_.data());
350        this->setp(__p, __p + __str_.size());
351        this->pbump(__size);
352
353#      if _LIBCPP_HAS_EXCEPTIONS
354      } catch (...) {
355        return traits_type::eof();
356      }
357#      endif
358    }
359
360    return this->sputc(traits_type::to_char_type(__c));
361  }
362
363private:
364  streambuf_type* __wrapped_;
365
366  // TODO Use a more generic buffer.
367  // That buffer should be light with almost no additional headers. Then
368  // it can be use here, the __retarget_buffer, and place that use
369  // the now removed get_temporary_buffer
370
371  basic_string<_CharT, _Traits, _Allocator> __str_;
372  bool __emit_on_sync_{false};
373
374  _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) {
375    if (!__wrapped_)
376      return false;
377
378#      if _LIBCPP_HAS_THREADS
379    lock_guard<mutex> __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_);
380#      endif
381
382    bool __result = true;
383    if (this->pptr() != this->pbase()) {
384      _LIBCPP_ASSERT_INTERNAL(this->pbase() && this->pptr() && this->epptr(), "all put area pointers shold be valid");
385
386      // The __str_ does not know how much of its buffer is used. This
387      // information is extracted from the information of the base class.
388      __result &= (__wrapped_->sputn(this->pbase(), this->pptr() - this->pbase()) != -1);
389      // Clears the buffer, but keeps the contents (and) size of the
390      // internal buffer.
391      this->setp(this->pbase(), this->epptr());
392    }
393
394    if (__flush)
395      __result &= (__wrapped_->pubsync() != -1);
396
397    return __result;
398  }
399
400  _LIBCPP_HIDE_FROM_ABI void __move_common(basic_syncbuf& __other) {
401    // Adjust the put area pointers to our buffer.
402    char_type* __p = static_cast<char_type*>(__str_.data());
403    this->setp(__p, __p + __str_.size());
404    this->pbump(__other.pptr() - __other.pbase());
405
406    // Clear __other_ so the destructor will act as a NOP.
407    __other.setp(nullptr, nullptr);
408    __other.__wrapped_ = nullptr;
409  }
410
411  _LIBCPP_HIDE_FROM_ABI void __inc_reference() {
412#      if _LIBCPP_HAS_THREADS
413    if (__wrapped_)
414      __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_);
415#      endif
416  }
417
418  _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept {
419#      if _LIBCPP_HAS_THREADS
420    if (__wrapped_)
421      __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_);
422#      endif
423  }
424};
425
426using std::syncbuf;
427#      if _LIBCPP_HAS_WIDE_CHARACTERS
428using std::wsyncbuf;
429#      endif
430
431// [syncstream.syncbuf.special], specialized algorithms
432template <class _CharT, class _Traits, class _Allocator>
433_LIBCPP_HIDE_FROM_ABI void
434swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) {
435  __lhs.swap(__rhs);
436}
437
438// basic_osyncstream
439
440template <class _CharT, class _Traits, class _Allocator>
441class basic_osyncstream : public basic_ostream<_CharT, _Traits> {
442public:
443  using char_type   = _CharT;
444  using traits_type = _Traits;
445  using int_type    = typename traits_type::int_type;
446  using pos_type    = typename traits_type::pos_type;
447  using off_type    = typename traits_type::off_type;
448
449  using allocator_type = _Allocator;
450  using streambuf_type = basic_streambuf<char_type, traits_type>;
451  using syncbuf_type   = basic_syncbuf<char_type, traits_type, allocator_type>;
452
453  // [syncstream.osyncstream.cons], construction and destruction
454
455  _LIBCPP_HIDE_FROM_ABI basic_osyncstream(streambuf_type* __obuf, allocator_type const& __alloc)
456      : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(__obuf, __alloc) {}
457
458  _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(streambuf_type* __obuf)
459      : basic_osyncstream(__obuf, allocator_type()) {}
460
461  _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_ostream<char_type, traits_type>& __os, allocator_type const& __alloc)
462      : basic_osyncstream(__os.rdbuf(), __alloc) {}
463
464  _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
465      : basic_osyncstream(__os, allocator_type()) {}
466
467  _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_osyncstream&& __other) noexcept
468      : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(std::move(__other.__sb_)) {
469    this->set_rdbuf(std::addressof(__sb_));
470  }
471
472  // [syncstream.osyncstream.assign], assignment
473
474  _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default;
475
476  // [syncstream.osyncstream.members], member functions
477
478  _LIBCPP_HIDE_FROM_ABI void emit() {
479    // The basic_ostream::put places the sentry in a try
480    // catch, this does not match the wording of the standard
481    // [ostream.unformatted]
482    // TODO validate other unformatted output functions.
483    typename basic_ostream<char_type, traits_type>::sentry __s(*this);
484    if (__s) {
485#      if _LIBCPP_HAS_EXCEPTIONS
486      try {
487#      endif
488
489        if (__sb_.emit() == false)
490          this->setstate(ios::badbit);
491#      if _LIBCPP_HAS_EXCEPTIONS
492      } catch (...) {
493        this->__set_badbit_and_consider_rethrow();
494      }
495#      endif
496    }
497  }
498
499  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); }
500
501  _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept {
502    return const_cast<syncbuf_type*>(std::addressof(__sb_));
503  }
504
505private:
506  syncbuf_type __sb_;
507};
508
509using std::osyncstream;
510#      if _LIBCPP_HAS_WIDE_CHARACTERS
511using std::wosyncstream;
512#      endif
513
514#    endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM
515
516_LIBCPP_END_NAMESPACE_STD
517
518_LIBCPP_POP_MACROS
519
520#  endif // _LIBCPP_HAS_LOCALIZATION
521#endif   // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
522
523#endif // _LIBCPP_SYNCSTREAM