diff options
Diffstat (limited to 'third_party/libcxx/include/c++/v1/syncstream')
| -rw-r--r-- | third_party/libcxx/include/c++/v1/syncstream | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/third_party/libcxx/include/c++/v1/syncstream b/third_party/libcxx/include/c++/v1/syncstream new file mode 100644 index 0000000..bd64453 --- /dev/null +++ b/third_party/libcxx/include/c++/v1/syncstream @@ -0,0 +1,523 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_SYNCSTREAM +#define _LIBCPP_SYNCSTREAM + +/* + syncstream synopsis + +#include <ostream> // see [ostream.syn] + +namespace std { + template<class charT, class traits, class Allocator> + class basic_syncbuf; + + // [syncstream.syncbuf.special], specialized algorithms + template<class charT, class traits, class Allocator> + void swap(basic_syncbuf<charT, traits, Allocator>&, + basic_syncbuf<charT, traits, Allocator>&); + + using syncbuf = basic_syncbuf<char>; + using wsyncbuf = basic_syncbuf<wchar_t>; + + template<class charT, class traits, class Allocator> + class basic_osyncstream; + + using osyncstream = basic_osyncstream<char>; + using wosyncstream = basic_osyncstream<wchar_t>; + + template<class charT, class traits, class Allocator> + class basic_syncbuf : public basic_streambuf<charT, traits> { + public: + using char_type = charT; + using int_type = typename traits::int_type; + using pos_type = typename traits::pos_type; + using off_type = typename traits::off_type; + using traits_type = traits; + using allocator_type = Allocator; + + using streambuf_type = basic_streambuf<charT, traits>; + + // [syncstream.syncbuf.cons], construction and destruction + basic_syncbuf() + : basic_syncbuf(nullptr) {} + explicit basic_syncbuf(streambuf_type* obuf) + : basic_syncbuf(obuf, Allocator()) {} + basic_syncbuf(streambuf_type*, const Allocator&); + basic_syncbuf(basic_syncbuf&&); + ~basic_syncbuf(); + + // [syncstream.syncbuf.assign], assignment and swap + basic_syncbuf& operator=(basic_syncbuf&&); + void swap(basic_syncbuf&); + + // [syncstream.syncbuf.members], member functions + bool emit(); + streambuf_type* get_wrapped() const noexcept; + allocator_type get_allocator() const noexcept; + void set_emit_on_sync(bool) noexcept; + + protected: + // [syncstream.syncbuf.virtuals], overridden virtual functions + int sync() override; + + private: + streambuf_type* wrapped; // exposition only + bool emit_on_sync{}; // exposition only + }; + + // [syncstream.syncbuf.special], specialized algorithms + template<class charT, class traits, class Allocator> + void swap(basic_syncbuf<charT, traits, Allocator>&, + basic_syncbuf<charT, traits, Allocator>&); + + template<class charT, class traits, class Allocator> + class basic_osyncstream : public basic_ostream<charT, traits> { + public: + using char_type = charT; + using int_type = typename traits::int_type; + using pos_type = typename traits::pos_type; + using off_type = typename traits::off_type; + using traits_type = traits; + + using allocator_type = Allocator; + using streambuf_type = basic_streambuf<charT, traits>; + using syncbuf_type = basic_syncbuf<charT, traits, Allocator>; + + // [syncstream.osyncstream.cons], construction and destruction + basic_osyncstream(streambuf_type*, const Allocator&); + explicit basic_osyncstream(streambuf_type* obuf) + : basic_osyncstream(obuf, Allocator()) {} + basic_osyncstream(basic_ostream<charT, traits>& os, const Allocator& allocator) + : basic_osyncstream(os.rdbuf(), allocator) {} + explicit basic_osyncstream(basic_ostream<charT, traits>& os) + : basic_osyncstream(os, Allocator()) {} + basic_osyncstream(basic_osyncstream&&) noexcept; + ~basic_osyncstream(); + + // [syncstream.osyncstream.assign], assignment + basic_osyncstream& operator=(basic_osyncstream&&); + + // [syncstream.osyncstream.members], member functions + void emit(); + streambuf_type* get_wrapped() const noexcept; + syncbuf_type* rdbuf() const noexcept { return const_cast<syncbuf_type*>(addressof(sb)); } + + private: + syncbuf_type sb; // exposition only + }; +} + +*/ + +#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) +# include <__cxx03/__config> +#else +# include <__config> + +// standard-mandated includes + +// [syncstream.syn] +# include <ostream> + +# if _LIBCPP_HAS_LOCALIZATION + +# include <__mutex/lock_guard.h> +# include <__utility/move.h> +# include <ios> +# include <iosfwd> // required for declaration of default arguments +# include <streambuf> +# include <string> +# if _LIBCPP_HAS_THREADS +# include <map> +# include <shared_mutex> +# endif + +# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +# endif + +_LIBCPP_PUSH_MACROS +# include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM + +// [syncstream.syncbuf.overview]/1 +// Class template basic_syncbuf stores character data written to it, +// known as the associated output, into internal buffers allocated +// using the object's allocator. The associated output is transferred +// to the wrapped stream buffer object *wrapped when emit() is called +// or when the basic_syncbuf object is destroyed. Such transfers are +// atomic with respect to transfers by other basic_syncbuf objects +// with the same wrapped stream buffer object. +// +// This helper singleton is used to implement the required +// synchronisation guarantees. +# if _LIBCPP_HAS_THREADS +class __wrapped_streambuf_mutex { + _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex() = default; + +public: + __wrapped_streambuf_mutex(const __wrapped_streambuf_mutex&) = delete; + __wrapped_streambuf_mutex& operator=(const __wrapped_streambuf_mutex&) = delete; + + _LIBCPP_HIDE_FROM_ABI void __inc_reference([[maybe_unused]] void* __ptr) { + _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); + unique_lock __lock{__mutex_}; + ++__lut_[reinterpret_cast<uintptr_t>(__ptr)].__count; + } + + // pre: __ptr is in __lut_ + _LIBCPP_HIDE_FROM_ABI void __dec_reference([[maybe_unused]] void* __ptr) noexcept { + unique_lock __lock{__mutex_}; + + auto __it = __get_it(__ptr); + if (__it->second.__count == 1) + __lut_.erase(__it); + else + --__it->second.__count; + } + + // TODO + // This function causes emit() aquire two mutexes: + // - __mutex_ shared + // _ __get_it(__ptr)->second.__mutex exclusive + // + // Instead store a pointer to __get_it(__ptr)->second.__mutex when + // calling __inc_reference. + // + // pre: __ptr is in __lut_ + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex> __get_lock([[maybe_unused]] void* __ptr) noexcept { + shared_lock __lock{__mutex_}; + return lock_guard{__get_it(__ptr)->second.__mutex}; + } + + // This function is used for testing. + // + // It is allowed to call this function with a non-registered pointer. + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __get_count([[maybe_unused]] void* __ptr) noexcept { + _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); + shared_lock __lock{__mutex_}; + + auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr)); + return __it != __lut_.end() ? __it->second.__count : 0; + } + + [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex& __instance() noexcept { + static __wrapped_streambuf_mutex __result; + return __result; + } + +private: + struct __value { + mutex __mutex; + size_t __count{0}; + }; + + shared_mutex __mutex_; + map<uintptr_t, __value> __lut_; + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI map<uintptr_t, __value>::iterator __get_it(void* __ptr) noexcept { + _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); + + auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr)); + _LIBCPP_ASSERT_INTERNAL(__it != __lut_.end(), "using a wrapped streambuf that has not been registered"); + _LIBCPP_ASSERT_INTERNAL(__it->second.__count >= 1, "found an inactive streambuf wrapper"); + return __it; + } +}; +# endif // _LIBCPP_HAS_THREADS + +// basic_syncbuf + +// The class uses a basic_string<_CharT, _Traits, _Allocator> as +// internal buffer. Per [syncstream.syncbuf.cons]/4 +// Remarks: A copy of allocator is used to allocate memory for +// internal buffers holding the associated output. +// +// Therefore the allocator used in the constructor is passed to the +// basic_string. The class does not keep a copy of this allocator. +template <class _CharT, class _Traits, class _Allocator> +class basic_syncbuf : public basic_streambuf<_CharT, _Traits> { +public: + using char_type = _CharT; + using traits_type = _Traits; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + using allocator_type = _Allocator; + + using streambuf_type = basic_streambuf<_CharT, _Traits>; + + // [syncstream.syncbuf.cons], construction and destruction + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf() : basic_syncbuf(nullptr) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_syncbuf(streambuf_type* __obuf) : basic_syncbuf(__obuf, _Allocator()) {} + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc) + : __wrapped_(__obuf), __str_(__alloc) { + __inc_reference(); + } + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other) + : __wrapped_(__other.get_wrapped()), __str_(std::move(__other.__str_)), __emit_on_sync_(__other.__emit_on_sync_) { + __move_common(__other); + } + + _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() { +# if _LIBCPP_HAS_EXCEPTIONS + try { +# endif // _LIBCPP_HAS_EXCEPTIONS + emit(); +# if _LIBCPP_HAS_EXCEPTIONS + } catch (...) { + } +# endif // _LIBCPP_HAS_EXCEPTIONS + __dec_reference(); + } + + // [syncstream.syncbuf.assign], assignment and swap + + _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) { + // The function is specified to call emit. This call should + // propagate the exception thrown. + emit(); + __dec_reference(); + + __wrapped_ = __other.get_wrapped(); + __str_ = std::move(__other.__str_); + __emit_on_sync_ = __other.__emit_on_sync_; + + __move_common(__other); + + return *this; + } + + _LIBCPP_HIDE_FROM_ABI void swap(basic_syncbuf& __other) { + _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR( + allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(), + "violates the mandated swap precondition"); + + basic_syncbuf __tmp(std::move(__other)); + __other = std::move(*this); + *this = std::move(__tmp); + } + + // [syncstream.syncbuf.members], member functions + + _LIBCPP_HIDE_FROM_ABI bool emit() { return emit(false); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); } + + _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; } + +protected: + // [syncstream.syncbuf.virtuals], overridden virtual functions + + _LIBCPP_HIDE_FROM_ABI_VIRTUAL + int sync() override { + if (__emit_on_sync_ && !emit(true)) + return -1; + return 0; + } + + _LIBCPP_HIDE_FROM_ABI_VIRTUAL + int_type overflow(int_type __c = traits_type::eof()) override { + if (traits_type::eq_int_type(__c, traits_type::eof())) + return traits_type::not_eof(__c); + + if (this->pptr() == this->epptr()) { +# if _LIBCPP_HAS_EXCEPTIONS + try { +# endif + size_t __size = __str_.size(); + __str_.resize(__str_.capacity() + 1); + _LIBCPP_ASSERT_INTERNAL(__str_.size() > __size, "the buffer hasn't grown"); + + char_type* __p = static_cast<char_type*>(__str_.data()); + this->setp(__p, __p + __str_.size()); + this->pbump(__size); + +# if _LIBCPP_HAS_EXCEPTIONS + } catch (...) { + return traits_type::eof(); + } +# endif + } + + return this->sputc(traits_type::to_char_type(__c)); + } + +private: + streambuf_type* __wrapped_; + + // TODO Use a more generic buffer. + // That buffer should be light with almost no additional headers. Then + // it can be use here, the __retarget_buffer, and place that use + // the now removed get_temporary_buffer + + basic_string<_CharT, _Traits, _Allocator> __str_; + bool __emit_on_sync_{false}; + + _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) { + if (!__wrapped_) + return false; + +# if _LIBCPP_HAS_THREADS + lock_guard<mutex> __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_); +# endif + + bool __result = true; + if (this->pptr() != this->pbase()) { + _LIBCPP_ASSERT_INTERNAL(this->pbase() && this->pptr() && this->epptr(), "all put area pointers shold be valid"); + + // The __str_ does not know how much of its buffer is used. This + // information is extracted from the information of the base class. + __result &= (__wrapped_->sputn(this->pbase(), this->pptr() - this->pbase()) != -1); + // Clears the buffer, but keeps the contents (and) size of the + // internal buffer. + this->setp(this->pbase(), this->epptr()); + } + + if (__flush) + __result &= (__wrapped_->pubsync() != -1); + + return __result; + } + + _LIBCPP_HIDE_FROM_ABI void __move_common(basic_syncbuf& __other) { + // Adjust the put area pointers to our buffer. + char_type* __p = static_cast<char_type*>(__str_.data()); + this->setp(__p, __p + __str_.size()); + this->pbump(__other.pptr() - __other.pbase()); + + // Clear __other_ so the destructor will act as a NOP. + __other.setp(nullptr, nullptr); + __other.__wrapped_ = nullptr; + } + + _LIBCPP_HIDE_FROM_ABI void __inc_reference() { +# if _LIBCPP_HAS_THREADS + if (__wrapped_) + __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_); +# endif + } + + _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept { +# if _LIBCPP_HAS_THREADS + if (__wrapped_) + __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_); +# endif + } +}; + +using std::syncbuf; +# if _LIBCPP_HAS_WIDE_CHARACTERS +using std::wsyncbuf; +# endif + +// [syncstream.syncbuf.special], specialized algorithms +template <class _CharT, class _Traits, class _Allocator> +_LIBCPP_HIDE_FROM_ABI void +swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) { + __lhs.swap(__rhs); +} + +// basic_osyncstream + +template <class _CharT, class _Traits, class _Allocator> +class basic_osyncstream : public basic_ostream<_CharT, _Traits> { +public: + using char_type = _CharT; + using traits_type = _Traits; + using int_type = typename traits_type::int_type; + using pos_type = typename traits_type::pos_type; + using off_type = typename traits_type::off_type; + + using allocator_type = _Allocator; + using streambuf_type = basic_streambuf<char_type, traits_type>; + using syncbuf_type = basic_syncbuf<char_type, traits_type, allocator_type>; + + // [syncstream.osyncstream.cons], construction and destruction + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream(streambuf_type* __obuf, allocator_type const& __alloc) + : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(__obuf, __alloc) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(streambuf_type* __obuf) + : basic_osyncstream(__obuf, allocator_type()) {} + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_ostream<char_type, traits_type>& __os, allocator_type const& __alloc) + : basic_osyncstream(__os.rdbuf(), __alloc) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os) + : basic_osyncstream(__os, allocator_type()) {} + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_osyncstream&& __other) noexcept + : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(std::move(__other.__sb_)) { + this->set_rdbuf(std::addressof(__sb_)); + } + + // [syncstream.osyncstream.assign], assignment + + _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default; + + // [syncstream.osyncstream.members], member functions + + _LIBCPP_HIDE_FROM_ABI void emit() { + // The basic_ostream::put places the sentry in a try + // catch, this does not match the wording of the standard + // [ostream.unformatted] + // TODO validate other unformatted output functions. + typename basic_ostream<char_type, traits_type>::sentry __s(*this); + if (__s) { +# if _LIBCPP_HAS_EXCEPTIONS + try { +# endif + + if (__sb_.emit() == false) + this->setstate(ios::badbit); +# if _LIBCPP_HAS_EXCEPTIONS + } catch (...) { + this->__set_badbit_and_consider_rethrow(); + } +# endif + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept { + return const_cast<syncbuf_type*>(std::addressof(__sb_)); + } + +private: + syncbuf_type __sb_; +}; + +using std::osyncstream; +# if _LIBCPP_HAS_WIDE_CHARACTERS +using std::wosyncstream; +# endif + +# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +# endif // _LIBCPP_HAS_LOCALIZATION +#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) + +#endif // _LIBCPP_SYNCSTREAM |
