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