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