524 lines · plain
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-exception7//8//===----------------------------------------------------------------------===//9 10#ifndef _LIBCPP_SYNCSTREAM11#define _LIBCPP_SYNCSTREAM12 13/*14 syncstream synopsis15 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 algorithms23 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 destruction49 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 swap58 basic_syncbuf& operator=(basic_syncbuf&&);59 void swap(basic_syncbuf&);60 61 // [syncstream.syncbuf.members], member functions62 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 functions69 int sync() override;70 71 private:72 streambuf_type* wrapped; // exposition only73 bool emit_on_sync{}; // exposition only74 };75 76 // [syncstream.syncbuf.special], specialized algorithms77 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 destruction95 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], assignment106 basic_osyncstream& operator=(basic_osyncstream&&);107 108 // [syncstream.osyncstream.members], member functions109 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 only115 };116}117 118*/119 120#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)121# include <__cxx03/__config>122#else123# include <__config>124 125// standard-mandated includes126 127// [syncstream.syn]128# include <ostream>129 130# if _LIBCPP_HAS_LOCALIZATION131 132# include <__mutex/lock_guard.h>133# include <__utility/move.h>134# include <ios>135# include <iosfwd> // required for declaration of default arguments136# include <streambuf>137# include <string>138# if _LIBCPP_HAS_THREADS139# include <map>140# include <shared_mutex>141# endif142 143# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)144# pragma GCC system_header145# endif146 147_LIBCPP_PUSH_MACROS148# include <__undef_macros>149 150_LIBCPP_BEGIN_NAMESPACE_STD151 152# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM153 154// [syncstream.syncbuf.overview]/1155// Class template basic_syncbuf stores character data written to it,156// known as the associated output, into internal buffers allocated157// using the object's allocator. The associated output is transferred158// to the wrapped stream buffer object *wrapped when emit() is called159// or when the basic_syncbuf object is destroyed. Such transfers are160// atomic with respect to transfers by other basic_syncbuf objects161// with the same wrapped stream buffer object.162//163// This helper singleton is used to implement the required164// synchronisation guarantees.165# if _LIBCPP_HAS_THREADS166class __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 else187 --__it->second.__count;188 }189 190 // TODO191 // This function causes emit() aquire two mutexes:192 // - __mutex_ shared193 // _ __get_it(__ptr)->second.__mutex exclusive194 //195 // Instead store a pointer to __get_it(__ptr)->second.__mutex when196 // 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_THREADS239 240// basic_syncbuf241 242// The class uses a basic_string<_CharT, _Traits, _Allocator> as243// internal buffer. Per [syncstream.syncbuf.cons]/4244// Remarks: A copy of allocator is used to allocate memory for245// internal buffers holding the associated output.246//247// Therefore the allocator used in the constructor is passed to the248// 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 destruction262 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_EXCEPTIONS279 try {280# endif // _LIBCPP_HAS_EXCEPTIONS281 emit();282# if _LIBCPP_HAS_EXCEPTIONS283 } catch (...) {284 }285# endif // _LIBCPP_HAS_EXCEPTIONS286 __dec_reference();287 }288 289 // [syncstream.syncbuf.assign], assignment and swap290 291 _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) {292 // The function is specified to call emit. This call should293 // 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 functions317 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 functions328 329 _LIBCPP_HIDE_FROM_ABI_VIRTUAL330 int sync() override {331 if (__emit_on_sync_ && !emit(true))332 return -1;333 return 0;334 }335 336 _LIBCPP_HIDE_FROM_ABI_VIRTUAL337 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_EXCEPTIONS343 try {344# endif345 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_EXCEPTIONS354 } catch (...) {355 return traits_type::eof();356 }357# endif358 }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. Then368 // it can be use here, the __retarget_buffer, and place that use369 // the now removed get_temporary_buffer370 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_THREADS379 lock_guard<mutex> __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_);380# endif381 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. This387 // 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 the390 // 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_THREADS413 if (__wrapped_)414 __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_);415# endif416 }417 418 _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept {419# if _LIBCPP_HAS_THREADS420 if (__wrapped_)421 __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_);422# endif423 }424};425 426using std::syncbuf;427# if _LIBCPP_HAS_WIDE_CHARACTERS428using std::wsyncbuf;429# endif430 431// [syncstream.syncbuf.special], specialized algorithms432template <class _CharT, class _Traits, class _Allocator>433_LIBCPP_HIDE_FROM_ABI void434swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) {435 __lhs.swap(__rhs);436}437 438// basic_osyncstream439 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 destruction454 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) noexcept468 : 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], assignment473 474 _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default;475 476 // [syncstream.osyncstream.members], member functions477 478 _LIBCPP_HIDE_FROM_ABI void emit() {479 // The basic_ostream::put places the sentry in a try480 // catch, this does not match the wording of the standard481 // [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_EXCEPTIONS486 try {487# endif488 489 if (__sb_.emit() == false)490 this->setstate(ios::badbit);491# if _LIBCPP_HAS_EXCEPTIONS492 } catch (...) {493 this->__set_badbit_and_consider_rethrow();494 }495# endif496 }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_CHARACTERS511using std::wosyncstream;512# endif513 514# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM515 516_LIBCPP_END_NAMESPACE_STD517 518_LIBCPP_POP_MACROS519 520# endif // _LIBCPP_HAS_LOCALIZATION521#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)522 523#endif // _LIBCPP_SYNCSTREAM524