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