xref: /freebsd/contrib/llvm-project/libcxx/include/syncstream (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15f757f3fSDimitry Andric// -*- C++ -*-
25f757f3fSDimitry Andric//===----------------------------------------------------------------------===//
35f757f3fSDimitry Andric//
45f757f3fSDimitry Andric// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
55f757f3fSDimitry Andric// See https://llvm.org/LICENSE.txt for license information.
65f757f3fSDimitry Andric// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
75f757f3fSDimitry Andric//
85f757f3fSDimitry Andric//===----------------------------------------------------------------------===//
95f757f3fSDimitry Andric
105f757f3fSDimitry Andric#ifndef _LIBCPP_SYNCSTREAM
115f757f3fSDimitry Andric#define _LIBCPP_SYNCSTREAM
125f757f3fSDimitry Andric
135f757f3fSDimitry Andric/*
145f757f3fSDimitry Andric    syncstream synopsis
155f757f3fSDimitry Andric
165f757f3fSDimitry Andric#include <ostream>  // see [ostream.syn]
175f757f3fSDimitry Andric
185f757f3fSDimitry Andricnamespace std {
195f757f3fSDimitry Andric    template<class charT, class traits, class Allocator>
205f757f3fSDimitry Andric    class basic_syncbuf;
215f757f3fSDimitry Andric
225f757f3fSDimitry Andric    // [syncstream.syncbuf.special], specialized algorithms
235f757f3fSDimitry Andric    template<class charT, class traits, class Allocator>
245f757f3fSDimitry Andric      void swap(basic_syncbuf<charT, traits, Allocator>&,
255f757f3fSDimitry Andric                basic_syncbuf<charT, traits, Allocator>&);
265f757f3fSDimitry Andric
275f757f3fSDimitry Andric    using syncbuf = basic_syncbuf<char>;
285f757f3fSDimitry Andric    using wsyncbuf = basic_syncbuf<wchar_t>;
295f757f3fSDimitry Andric
305f757f3fSDimitry Andric    template<class charT, class traits, class Allocator>
315f757f3fSDimitry Andric    class basic_osyncstream;
325f757f3fSDimitry Andric
335f757f3fSDimitry Andric    using osyncstream = basic_osyncstream<char>;
345f757f3fSDimitry Andric    using wosyncstream = basic_osyncstream<wchar_t>;
355f757f3fSDimitry Andric
365f757f3fSDimitry Andric    template<class charT, class traits, class Allocator>
375f757f3fSDimitry Andric    class basic_syncbuf : public basic_streambuf<charT, traits> {
385f757f3fSDimitry Andric    public:
395f757f3fSDimitry Andric        using char_type      = charT;
405f757f3fSDimitry Andric        using int_type       = typename traits::int_type;
415f757f3fSDimitry Andric        using pos_type       = typename traits::pos_type;
425f757f3fSDimitry Andric        using off_type       = typename traits::off_type;
435f757f3fSDimitry Andric        using traits_type    = traits;
445f757f3fSDimitry Andric        using allocator_type = Allocator;
455f757f3fSDimitry Andric
465f757f3fSDimitry Andric        using streambuf_type = basic_streambuf<charT, traits>;
475f757f3fSDimitry Andric
485f757f3fSDimitry Andric        // [syncstream.syncbuf.cons], construction and destruction
495f757f3fSDimitry Andric        explicit basic_syncbuf(streambuf_type* obuf = nullptr)
505f757f3fSDimitry Andric          : basic_syncbuf(obuf, Allocator()) {}
515f757f3fSDimitry Andric        basic_syncbuf(streambuf_type*, const Allocator&);
525f757f3fSDimitry Andric        basic_syncbuf(basic_syncbuf&&);
535f757f3fSDimitry Andric        ~basic_syncbuf();
545f757f3fSDimitry Andric
555f757f3fSDimitry Andric        // [syncstream.syncbuf.assign], assignment and swap
565f757f3fSDimitry Andric        basic_syncbuf& operator=(basic_syncbuf&&);
575f757f3fSDimitry Andric        void swap(basic_syncbuf&);
585f757f3fSDimitry Andric
595f757f3fSDimitry Andric        // [syncstream.syncbuf.members], member functions
605f757f3fSDimitry Andric        bool emit();
615f757f3fSDimitry Andric        streambuf_type* get_wrapped() const noexcept;
625f757f3fSDimitry Andric        allocator_type get_allocator() const noexcept;
635f757f3fSDimitry Andric        void set_emit_on_sync(bool) noexcept;
645f757f3fSDimitry Andric
655f757f3fSDimitry Andric    protected:
665f757f3fSDimitry Andric        // [syncstream.syncbuf.virtuals], overridden virtual functions
675f757f3fSDimitry Andric        int sync() override;
685f757f3fSDimitry Andric
695f757f3fSDimitry Andric    private:
705f757f3fSDimitry Andric        streambuf_type* wrapped;    // exposition only
715f757f3fSDimitry Andric        bool emit_on_sync{};        // exposition only
725f757f3fSDimitry Andric    };
735f757f3fSDimitry Andric
745f757f3fSDimitry Andric    // [syncstream.syncbuf.special], specialized algorithms
755f757f3fSDimitry Andric    template<class charT, class traits, class Allocator>
765f757f3fSDimitry Andric    void swap(basic_syncbuf<charT, traits, Allocator>&,
775f757f3fSDimitry Andric              basic_syncbuf<charT, traits, Allocator>&);
785f757f3fSDimitry Andric
795f757f3fSDimitry Andric    template<class charT, class traits, class Allocator>
805f757f3fSDimitry Andric    class basic_osyncstream : public basic_ostream<charT, traits> {
815f757f3fSDimitry Andric    public:
825f757f3fSDimitry Andric        using char_type   = charT;
835f757f3fSDimitry Andric        using int_type    = typename traits::int_type;
845f757f3fSDimitry Andric        using pos_type    = typename traits::pos_type;
855f757f3fSDimitry Andric        using off_type    = typename traits::off_type;
865f757f3fSDimitry Andric        using traits_type = traits;
875f757f3fSDimitry Andric
885f757f3fSDimitry Andric        using allocator_type = Allocator;
895f757f3fSDimitry Andric        using streambuf_type = basic_streambuf<charT, traits>;
905f757f3fSDimitry Andric        using syncbuf_type   = basic_syncbuf<charT, traits, Allocator>;
915f757f3fSDimitry Andric
925f757f3fSDimitry Andric        // [syncstream.osyncstream.cons], construction and destruction
935f757f3fSDimitry Andric        basic_osyncstream(streambuf_type*, const Allocator&);
945f757f3fSDimitry Andric        explicit basic_osyncstream(streambuf_type* obuf)
955f757f3fSDimitry Andric          : basic_osyncstream(obuf, Allocator()) {}
965f757f3fSDimitry Andric        basic_osyncstream(basic_ostream<charT, traits>& os, const Allocator& allocator)
975f757f3fSDimitry Andric          : basic_osyncstream(os.rdbuf(), allocator) {}
985f757f3fSDimitry Andric        explicit basic_osyncstream(basic_ostream<charT, traits>& os)
995f757f3fSDimitry Andric          : basic_osyncstream(os, Allocator()) {}
1005f757f3fSDimitry Andric        basic_osyncstream(basic_osyncstream&&) noexcept;
1015f757f3fSDimitry Andric        ~basic_osyncstream();
1025f757f3fSDimitry Andric
1035f757f3fSDimitry Andric        // [syncstream.osyncstream.assign], assignment
1045f757f3fSDimitry Andric        basic_osyncstream& operator=(basic_osyncstream&&);
1055f757f3fSDimitry Andric
1065f757f3fSDimitry Andric        // [syncstream.osyncstream.members], member functions
1075f757f3fSDimitry Andric        void emit();
1085f757f3fSDimitry Andric        streambuf_type* get_wrapped() const noexcept;
1095f757f3fSDimitry Andric        syncbuf_type* rdbuf() const noexcept { return const_cast<syncbuf_type*>(addressof(sb)); }
1105f757f3fSDimitry Andric
1115f757f3fSDimitry Andric    private:
1125f757f3fSDimitry Andric        syncbuf_type sb;    // exposition only
1135f757f3fSDimitry Andric    };
1145f757f3fSDimitry Andric}
1155f757f3fSDimitry Andric
1165f757f3fSDimitry Andric*/
1175f757f3fSDimitry Andric
1185f757f3fSDimitry Andric#include <__config>
1195f757f3fSDimitry Andric#include <__utility/move.h>
120*0fca6ea1SDimitry Andric#include <ios>
1215f757f3fSDimitry Andric#include <iosfwd> // required for declaration of default arguments
122*0fca6ea1SDimitry Andric#include <streambuf>
1235f757f3fSDimitry Andric#include <string>
1245f757f3fSDimitry Andric
1255f757f3fSDimitry Andric#ifndef _LIBCPP_HAS_NO_THREADS
1265f757f3fSDimitry Andric#  include <map>
1275f757f3fSDimitry Andric#  include <mutex>
1285f757f3fSDimitry Andric#  include <shared_mutex>
1295f757f3fSDimitry Andric#endif
1305f757f3fSDimitry Andric
1315f757f3fSDimitry Andric// standard-mandated includes
1325f757f3fSDimitry Andric
1335f757f3fSDimitry Andric// [syncstream.syn]
1345f757f3fSDimitry Andric#include <ostream>
1355f757f3fSDimitry Andric
1365f757f3fSDimitry Andric#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1375f757f3fSDimitry Andric#  pragma GCC system_header
1385f757f3fSDimitry Andric#endif
1395f757f3fSDimitry Andric
1405f757f3fSDimitry Andric_LIBCPP_PUSH_MACROS
1415f757f3fSDimitry Andric#include <__undef_macros>
1425f757f3fSDimitry Andric
1435f757f3fSDimitry Andric_LIBCPP_BEGIN_NAMESPACE_STD
1445f757f3fSDimitry Andric
1455f757f3fSDimitry Andric#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)
1465f757f3fSDimitry Andric
1475f757f3fSDimitry Andric// [syncstream.syncbuf.overview]/1
1485f757f3fSDimitry Andric//   Class template basic_syncbuf stores character data written to it,
1495f757f3fSDimitry Andric//   known as the associated output, into internal buffers allocated
1505f757f3fSDimitry Andric//   using the object's allocator. The associated output is transferred
1515f757f3fSDimitry Andric//   to the wrapped stream buffer object *wrapped when emit() is called
1525f757f3fSDimitry Andric//   or when the basic_syncbuf object is destroyed. Such transfers are
1535f757f3fSDimitry Andric//   atomic with respect to transfers by other basic_syncbuf objects
1545f757f3fSDimitry Andric//   with the same wrapped stream buffer object.
1555f757f3fSDimitry Andric//
1565f757f3fSDimitry Andric// This helper singleton is used to implement the required
1575f757f3fSDimitry Andric// synchronisation guarantees.
1585f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_THREADS
1595f757f3fSDimitry Andricclass __wrapped_streambuf_mutex {
1605f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex() = default;
1615f757f3fSDimitry Andric
1625f757f3fSDimitry Andricpublic:
1635f757f3fSDimitry Andric  __wrapped_streambuf_mutex(const __wrapped_streambuf_mutex&)            = delete;
1645f757f3fSDimitry Andric  __wrapped_streambuf_mutex& operator=(const __wrapped_streambuf_mutex&) = delete;
1655f757f3fSDimitry Andric
1665f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI void __inc_reference([[maybe_unused]] void* __ptr) {
1675f757f3fSDimitry Andric    _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
1685f757f3fSDimitry Andric    unique_lock __lock{__mutex_};
1695f757f3fSDimitry Andric    ++__lut_[reinterpret_cast<uintptr_t>(__ptr)].__count;
1705f757f3fSDimitry Andric  }
1715f757f3fSDimitry Andric
1725f757f3fSDimitry Andric  // pre: __ptr is in __lut_
1735f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI void __dec_reference([[maybe_unused]] void* __ptr) noexcept {
1745f757f3fSDimitry Andric    unique_lock __lock{__mutex_};
1755f757f3fSDimitry Andric
1765f757f3fSDimitry Andric    auto __it = __get_it(__ptr);
1775f757f3fSDimitry Andric    if (__it->second.__count == 1)
1785f757f3fSDimitry Andric      __lut_.erase(__it);
1795f757f3fSDimitry Andric    else
1805f757f3fSDimitry Andric      --__it->second.__count;
1815f757f3fSDimitry Andric  }
1825f757f3fSDimitry Andric
1835f757f3fSDimitry Andric  // TODO
1845f757f3fSDimitry Andric  // This function causes emit() aquire two mutexes:
1855f757f3fSDimitry Andric  // - __mutex_ shared
1865f757f3fSDimitry Andric  // _ __get_it(__ptr)->second.__mutex exclusive
1875f757f3fSDimitry Andric  //
1885f757f3fSDimitry Andric  // Instead store a pointer to __get_it(__ptr)->second.__mutex when
1895f757f3fSDimitry Andric  // calling __inc_reference.
1905f757f3fSDimitry Andric  //
1915f757f3fSDimitry Andric  // pre: __ptr is in __lut_
1925f757f3fSDimitry Andric  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex> __get_lock([[maybe_unused]] void* __ptr) noexcept {
1935f757f3fSDimitry Andric    shared_lock __lock{__mutex_};
1945f757f3fSDimitry Andric    return lock_guard{__get_it(__ptr)->second.__mutex};
1955f757f3fSDimitry Andric  }
1965f757f3fSDimitry Andric
1975f757f3fSDimitry Andric  // This function is used for testing.
1985f757f3fSDimitry Andric  //
1995f757f3fSDimitry Andric  // It is allowed to call this function with a non-registered pointer.
2005f757f3fSDimitry Andric  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __get_count([[maybe_unused]] void* __ptr) noexcept {
2015f757f3fSDimitry Andric    _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
2025f757f3fSDimitry Andric    shared_lock __lock{__mutex_};
2035f757f3fSDimitry Andric
2045f757f3fSDimitry Andric    auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
2055f757f3fSDimitry Andric    return __it != __lut_.end() ? __it->second.__count : 0;
2065f757f3fSDimitry Andric  }
2075f757f3fSDimitry Andric
2085f757f3fSDimitry Andric  [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex& __instance() noexcept {
2095f757f3fSDimitry Andric    static __wrapped_streambuf_mutex __result;
2105f757f3fSDimitry Andric    return __result;
2115f757f3fSDimitry Andric  }
2125f757f3fSDimitry Andric
2135f757f3fSDimitry Andricprivate:
2145f757f3fSDimitry Andric  struct __value {
2155f757f3fSDimitry Andric    mutex __mutex;
2165f757f3fSDimitry Andric    size_t __count{0};
2175f757f3fSDimitry Andric  };
2185f757f3fSDimitry Andric
2195f757f3fSDimitry Andric  shared_mutex __mutex_;
2205f757f3fSDimitry Andric  map<uintptr_t, __value> __lut_;
2215f757f3fSDimitry Andric
2225f757f3fSDimitry Andric  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI map<uintptr_t, __value>::iterator __get_it(void* __ptr) noexcept {
2235f757f3fSDimitry Andric    _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to");
2245f757f3fSDimitry Andric
2255f757f3fSDimitry Andric    auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr));
2265f757f3fSDimitry Andric    _LIBCPP_ASSERT_INTERNAL(__it != __lut_.end(), "using a wrapped streambuf that has not been registered");
2275f757f3fSDimitry Andric    _LIBCPP_ASSERT_INTERNAL(__it->second.__count >= 1, "found an inactive streambuf wrapper");
2285f757f3fSDimitry Andric    return __it;
2295f757f3fSDimitry Andric  }
2305f757f3fSDimitry Andric};
2315f757f3fSDimitry Andric#  endif // _LIBCPP_HAS_NO_THREADS
2325f757f3fSDimitry Andric
2335f757f3fSDimitry Andric// basic_syncbuf
2345f757f3fSDimitry Andric
2355f757f3fSDimitry Andric// The class uses a basic_string<_CharT, _Traits, _Allocator> as
2365f757f3fSDimitry Andric// internal buffer. Per [syncstream.syncbuf.cons]/4
2375f757f3fSDimitry Andric//   Remarks: A copy of allocator is used to allocate memory for
2385f757f3fSDimitry Andric//   internal buffers holding the associated output.
2395f757f3fSDimitry Andric//
2405f757f3fSDimitry Andric// Therefore the allocator used in the constructor is passed to the
2415f757f3fSDimitry Andric// basic_string. The class does not keep a copy of this allocator.
2425f757f3fSDimitry Andrictemplate <class _CharT, class _Traits, class _Allocator>
2435f757f3fSDimitry Andricclass _LIBCPP_TEMPLATE_VIS basic_syncbuf : public basic_streambuf<_CharT, _Traits> {
2445f757f3fSDimitry Andricpublic:
2455f757f3fSDimitry Andric  using char_type      = _CharT;
2465f757f3fSDimitry Andric  using traits_type    = _Traits;
2475f757f3fSDimitry Andric  using int_type       = typename traits_type::int_type;
2485f757f3fSDimitry Andric  using pos_type       = typename traits_type::pos_type;
2495f757f3fSDimitry Andric  using off_type       = typename traits_type::off_type;
2505f757f3fSDimitry Andric  using allocator_type = _Allocator;
2515f757f3fSDimitry Andric
2525f757f3fSDimitry Andric  using streambuf_type = basic_streambuf<_CharT, _Traits>;
2535f757f3fSDimitry Andric
2545f757f3fSDimitry Andric  // [syncstream.syncbuf.cons], construction and destruction
2555f757f3fSDimitry Andric
2565f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI explicit basic_syncbuf(streambuf_type* __obuf = nullptr)
2575f757f3fSDimitry Andric      : basic_syncbuf(__obuf, _Allocator()) {}
2585f757f3fSDimitry Andric
2595f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc)
2605f757f3fSDimitry Andric      : __wrapped_(__obuf), __str_(__alloc) {
2615f757f3fSDimitry Andric    __inc_reference();
2625f757f3fSDimitry Andric  }
2635f757f3fSDimitry Andric
2645f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other)
2655f757f3fSDimitry Andric      : __wrapped_(__other.get_wrapped()), __str_(std::move(__other.__str_)), __emit_on_sync_(__other.__emit_on_sync_) {
2665f757f3fSDimitry Andric    __move_common(__other);
2675f757f3fSDimitry Andric  }
2685f757f3fSDimitry Andric
2695f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() {
2705f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
2715f757f3fSDimitry Andric    try {
2725f757f3fSDimitry Andric#  endif // _LIBCPP_HAS_NO_EXCEPTIONS
2735f757f3fSDimitry Andric      emit();
2745f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
2755f757f3fSDimitry Andric    } catch (...) {
2765f757f3fSDimitry Andric    }
2775f757f3fSDimitry Andric#  endif // _LIBCPP_HAS_NO_EXCEPTIONS
2785f757f3fSDimitry Andric    __dec_reference();
2795f757f3fSDimitry Andric  }
2805f757f3fSDimitry Andric
2815f757f3fSDimitry Andric  // [syncstream.syncbuf.assign], assignment and swap
2825f757f3fSDimitry Andric
2835f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) {
2845f757f3fSDimitry Andric    // The function is specified to call emit. This call should
2855f757f3fSDimitry Andric    // propagate the exception thrown.
2865f757f3fSDimitry Andric    emit();
2875f757f3fSDimitry Andric    __dec_reference();
2885f757f3fSDimitry Andric
2895f757f3fSDimitry Andric    __wrapped_      = __other.get_wrapped();
2905f757f3fSDimitry Andric    __str_          = std::move(__other.__str_);
2915f757f3fSDimitry Andric    __emit_on_sync_ = __other.__emit_on_sync_;
2925f757f3fSDimitry Andric
2935f757f3fSDimitry Andric    __move_common(__other);
2945f757f3fSDimitry Andric
2955f757f3fSDimitry Andric    return *this;
2965f757f3fSDimitry Andric  }
2975f757f3fSDimitry Andric
2985f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI void swap(basic_syncbuf& __other) {
2995f757f3fSDimitry Andric    _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
3005f757f3fSDimitry Andric        allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(),
3015f757f3fSDimitry Andric        "violates the mandated swap precondition");
3025f757f3fSDimitry Andric
3035f757f3fSDimitry Andric    basic_syncbuf __tmp(std::move(__other));
3045f757f3fSDimitry Andric    __other = std::move(*this);
3055f757f3fSDimitry Andric    *this   = std::move(__tmp);
3065f757f3fSDimitry Andric  }
3075f757f3fSDimitry Andric
3085f757f3fSDimitry Andric  // [syncstream.syncbuf.members], member functions
3095f757f3fSDimitry Andric
3105f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI bool emit() { return emit(false); }
3115f757f3fSDimitry Andric
3125f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; }
3135f757f3fSDimitry Andric
3145f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); }
3155f757f3fSDimitry Andric
3165f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; }
3175f757f3fSDimitry Andric
3185f757f3fSDimitry Andricprotected:
3195f757f3fSDimitry Andric  // [syncstream.syncbuf.virtuals], overridden virtual functions
3205f757f3fSDimitry Andric
3215f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI_VIRTUAL
3225f757f3fSDimitry Andric  int sync() override {
3235f757f3fSDimitry Andric    if (__emit_on_sync_ && !emit(true))
3245f757f3fSDimitry Andric      return -1;
3255f757f3fSDimitry Andric    return 0;
3265f757f3fSDimitry Andric  }
3275f757f3fSDimitry Andric
3285f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI_VIRTUAL
3295f757f3fSDimitry Andric  int_type overflow(int_type __c = traits_type::eof()) override {
3305f757f3fSDimitry Andric    if (traits_type::eq_int_type(__c, traits_type::eof()))
3315f757f3fSDimitry Andric      return traits_type::not_eof(__c);
3325f757f3fSDimitry Andric
3335f757f3fSDimitry Andric    if (this->pptr() == this->epptr()) {
3345f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
3355f757f3fSDimitry Andric      try {
3365f757f3fSDimitry Andric#  endif
3375f757f3fSDimitry Andric        size_t __size = __str_.size();
3385f757f3fSDimitry Andric        __str_.resize(__str_.capacity() + 1);
3395f757f3fSDimitry Andric        _LIBCPP_ASSERT_INTERNAL(__str_.size() > __size, "the buffer hasn't grown");
3405f757f3fSDimitry Andric
3415f757f3fSDimitry Andric        char_type* __p = static_cast<char_type*>(__str_.data());
3425f757f3fSDimitry Andric        this->setp(__p, __p + __str_.size());
3435f757f3fSDimitry Andric        this->pbump(__size);
3445f757f3fSDimitry Andric
3455f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
3465f757f3fSDimitry Andric      } catch (...) {
3475f757f3fSDimitry Andric        return traits_type::eof();
3485f757f3fSDimitry Andric      }
3495f757f3fSDimitry Andric#  endif
3505f757f3fSDimitry Andric    }
3515f757f3fSDimitry Andric
3525f757f3fSDimitry Andric    return this->sputc(traits_type::to_char_type(__c));
3535f757f3fSDimitry Andric  }
3545f757f3fSDimitry Andric
3555f757f3fSDimitry Andricprivate:
3565f757f3fSDimitry Andric  streambuf_type* __wrapped_;
3575f757f3fSDimitry Andric
3585f757f3fSDimitry Andric  // TODO Use a more generic buffer.
3595f757f3fSDimitry Andric  // That buffer should be light with almost no additional headers. Then
3605f757f3fSDimitry Andric  // it can be use here, the __retarget_buffer, and place that use
3615f757f3fSDimitry Andric  // the now deprecated get_temporary_buffer
3625f757f3fSDimitry Andric
3635f757f3fSDimitry Andric  basic_string<_CharT, _Traits, _Allocator> __str_;
3645f757f3fSDimitry Andric  bool __emit_on_sync_{false};
3655f757f3fSDimitry Andric
3665f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) {
3675f757f3fSDimitry Andric    if (!__wrapped_)
3685f757f3fSDimitry Andric      return false;
3695f757f3fSDimitry Andric
3705f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_THREADS
3715f757f3fSDimitry Andric    lock_guard<mutex> __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_);
3725f757f3fSDimitry Andric#  endif
3735f757f3fSDimitry Andric
3745f757f3fSDimitry Andric    bool __result = true;
3755f757f3fSDimitry Andric    if (this->pptr() != this->pbase()) {
3765f757f3fSDimitry Andric      _LIBCPP_ASSERT_INTERNAL(this->pbase() && this->pptr() && this->epptr(), "all put area pointers shold be valid");
3775f757f3fSDimitry Andric
3785f757f3fSDimitry Andric      // The __str_ does not know how much of its buffer is used. This
3795f757f3fSDimitry Andric      // information is extracted from the information of the base class.
3805f757f3fSDimitry Andric      __result &= (__wrapped_->sputn(this->pbase(), this->pptr() - this->pbase()) != -1);
3815f757f3fSDimitry Andric      // Clears the buffer, but keeps the contents (and) size of the
3825f757f3fSDimitry Andric      // internal buffer.
3835f757f3fSDimitry Andric      this->setp(this->pbase(), this->epptr());
3845f757f3fSDimitry Andric    }
3855f757f3fSDimitry Andric
3865f757f3fSDimitry Andric    if (__flush)
3875f757f3fSDimitry Andric      __result &= (__wrapped_->pubsync() != -1);
3885f757f3fSDimitry Andric
3895f757f3fSDimitry Andric    return __result;
3905f757f3fSDimitry Andric  }
3915f757f3fSDimitry Andric
3925f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI void __move_common(basic_syncbuf& __other) {
3935f757f3fSDimitry Andric    // Adjust the put area pointers to our buffer.
3945f757f3fSDimitry Andric    char_type* __p = static_cast<char_type*>(__str_.data());
3955f757f3fSDimitry Andric    this->setp(__p, __p + __str_.size());
3965f757f3fSDimitry Andric    this->pbump(__other.pptr() - __other.pbase());
3975f757f3fSDimitry Andric
3985f757f3fSDimitry Andric    // Clear __other_ so the destructor will act as a NOP.
3995f757f3fSDimitry Andric    __other.setp(nullptr, nullptr);
4005f757f3fSDimitry Andric    __other.__wrapped_ = nullptr;
4015f757f3fSDimitry Andric  }
4025f757f3fSDimitry Andric
4035f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI void __inc_reference() {
4045f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_THREADS
4055f757f3fSDimitry Andric    if (__wrapped_)
4065f757f3fSDimitry Andric      __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_);
4075f757f3fSDimitry Andric#  endif
4085f757f3fSDimitry Andric  }
4095f757f3fSDimitry Andric
4105f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept {
4115f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_THREADS
4125f757f3fSDimitry Andric    if (__wrapped_)
4135f757f3fSDimitry Andric      __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_);
4145f757f3fSDimitry Andric#  endif
4155f757f3fSDimitry Andric  }
4165f757f3fSDimitry Andric};
4175f757f3fSDimitry Andric
4185f757f3fSDimitry Andricusing std::syncbuf;
4195f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
4205f757f3fSDimitry Andricusing std::wsyncbuf;
4215f757f3fSDimitry Andric#  endif
4225f757f3fSDimitry Andric
4235f757f3fSDimitry Andric// [syncstream.syncbuf.special], specialized algorithms
4245f757f3fSDimitry Andrictemplate <class _CharT, class _Traits, class _Allocator>
4255f757f3fSDimitry Andric_LIBCPP_HIDE_FROM_ABI void
4265f757f3fSDimitry Andricswap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) {
4275f757f3fSDimitry Andric  __lhs.swap(__rhs);
4285f757f3fSDimitry Andric}
4295f757f3fSDimitry Andric
4305f757f3fSDimitry Andric// basic_osyncstream
4315f757f3fSDimitry Andric
4325f757f3fSDimitry Andrictemplate <class _CharT, class _Traits, class _Allocator>
4335f757f3fSDimitry Andricclass _LIBCPP_TEMPLATE_VIS basic_osyncstream : public basic_ostream<_CharT, _Traits> {
4345f757f3fSDimitry Andricpublic:
4355f757f3fSDimitry Andric  using char_type   = _CharT;
4365f757f3fSDimitry Andric  using traits_type = _Traits;
4375f757f3fSDimitry Andric  using int_type    = typename traits_type::int_type;
4385f757f3fSDimitry Andric  using pos_type    = typename traits_type::pos_type;
4395f757f3fSDimitry Andric  using off_type    = typename traits_type::off_type;
4405f757f3fSDimitry Andric
4415f757f3fSDimitry Andric  using allocator_type = _Allocator;
4425f757f3fSDimitry Andric  using streambuf_type = basic_streambuf<char_type, traits_type>;
4435f757f3fSDimitry Andric  using syncbuf_type   = basic_syncbuf<char_type, traits_type, allocator_type>;
4445f757f3fSDimitry Andric
4455f757f3fSDimitry Andric  // [syncstream.osyncstream.cons], construction and destruction
4465f757f3fSDimitry Andric
4475f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI basic_osyncstream(streambuf_type* __obuf, allocator_type const& __alloc)
4485f757f3fSDimitry Andric      : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(__obuf, __alloc) {}
4495f757f3fSDimitry Andric
4505f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(streambuf_type* __obuf)
4515f757f3fSDimitry Andric      : basic_osyncstream(__obuf, allocator_type()) {}
4525f757f3fSDimitry Andric
4535f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_ostream<char_type, traits_type>& __os, allocator_type const& __alloc)
4545f757f3fSDimitry Andric      : basic_osyncstream(__os.rdbuf(), __alloc) {}
4555f757f3fSDimitry Andric
4565f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
4575f757f3fSDimitry Andric      : basic_osyncstream(__os, allocator_type()) {}
4585f757f3fSDimitry Andric
4595f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_osyncstream&& __other) noexcept
4605f757f3fSDimitry Andric      : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(std::move(__other.__sb_)) {
4615f757f3fSDimitry Andric    this->set_rdbuf(std::addressof(__sb_));
4625f757f3fSDimitry Andric  }
4635f757f3fSDimitry Andric
4645f757f3fSDimitry Andric  // [syncstream.osyncstream.assign], assignment
4655f757f3fSDimitry Andric
4665f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default;
4675f757f3fSDimitry Andric
4685f757f3fSDimitry Andric  // [syncstream.osyncstream.members], member functions
4695f757f3fSDimitry Andric
4705f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI void emit() {
4715f757f3fSDimitry Andric    // The basic_ostream::put places the sentry in a try
4725f757f3fSDimitry Andric    // catch, this does not match the wording of the standard
4735f757f3fSDimitry Andric    // [ostream.unformatted]
4745f757f3fSDimitry Andric    // TODO validate other unformatted output functions.
4755f757f3fSDimitry Andric    typename basic_ostream<char_type, traits_type>::sentry __s(*this);
4765f757f3fSDimitry Andric    if (__s) {
4775f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
4785f757f3fSDimitry Andric      try {
4795f757f3fSDimitry Andric#  endif
4805f757f3fSDimitry Andric
4815f757f3fSDimitry Andric        if (__sb_.emit() == false)
4825f757f3fSDimitry Andric          this->setstate(ios::badbit);
4835f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
4845f757f3fSDimitry Andric      } catch (...) {
4855f757f3fSDimitry Andric        this->__set_badbit_and_consider_rethrow();
4865f757f3fSDimitry Andric      }
4875f757f3fSDimitry Andric#  endif
4885f757f3fSDimitry Andric    }
4895f757f3fSDimitry Andric  }
4905f757f3fSDimitry Andric
4915f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); }
4925f757f3fSDimitry Andric
4935f757f3fSDimitry Andric  _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept {
4945f757f3fSDimitry Andric    return const_cast<syncbuf_type*>(std::addressof(__sb_));
4955f757f3fSDimitry Andric  }
4965f757f3fSDimitry Andric
4975f757f3fSDimitry Andricprivate:
4985f757f3fSDimitry Andric  syncbuf_type __sb_;
4995f757f3fSDimitry Andric};
5005f757f3fSDimitry Andric
5015f757f3fSDimitry Andricusing std::osyncstream;
5025f757f3fSDimitry Andric#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
5035f757f3fSDimitry Andricusing std::wosyncstream;
5045f757f3fSDimitry Andric#  endif
5055f757f3fSDimitry Andric
5065f757f3fSDimitry Andric#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)
5075f757f3fSDimitry Andric
5085f757f3fSDimitry Andric_LIBCPP_END_NAMESPACE_STD
5095f757f3fSDimitry Andric
5105f757f3fSDimitry Andric_LIBCPP_POP_MACROS
5115f757f3fSDimitry Andric
5125f757f3fSDimitry Andric#endif // _LIBCPP_SYNCSTREAM
513