aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/cxxsupp/libcxx/include/syncstream
blob: e6f35b6f428eda04ed0bee5fda816abb52e4fe72 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
// -*- 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
        explicit basic_syncbuf(streambuf_type* obuf = nullptr)
          : 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
    };
}

*/

#include <__config>
#include <__utility/move.h>
#include <ios>
#include <iosfwd> // required for declaration of default arguments
#include <streambuf>
#include <string>

#ifndef _LIBCPP_HAS_NO_THREADS
#  include <map>
#  include <mutex>
#  include <shared_mutex>
#endif

// standard-mandated includes

// [syncstream.syn]
#include <ostream>

#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 && !defined(_LIBCPP_HAS_NO_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.
#  ifndef _LIBCPP_HAS_NO_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_NO_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 _LIBCPP_TEMPLATE_VIS 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 explicit basic_syncbuf(streambuf_type* __obuf = nullptr)
      : 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() {
#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
    try {
#  endif // _LIBCPP_HAS_NO_EXCEPTIONS
      emit();
#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
    } catch (...) {
    }
#  endif // _LIBCPP_HAS_NO_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); }

  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; }

  _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()) {
#  ifndef _LIBCPP_HAS_NO_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);

#  ifndef _LIBCPP_HAS_NO_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 deprecated 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;

#  ifndef _LIBCPP_HAS_NO_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() {
#  ifndef _LIBCPP_HAS_NO_THREADS
    if (__wrapped_)
      __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_);
#  endif
  }

  _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept {
#  ifndef _LIBCPP_HAS_NO_THREADS
    if (__wrapped_)
      __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_);
#  endif
  }
};

using std::syncbuf;
#  ifndef _LIBCPP_HAS_NO_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 _LIBCPP_TEMPLATE_VIS 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) {
#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
      try {
#  endif

        if (__sb_.emit() == false)
          this->setstate(ios::badbit);
#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
      } catch (...) {
        this->__set_badbit_and_consider_rethrow();
      }
#  endif
    }
  }

  _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); }

  _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept {
    return const_cast<syncbuf_type*>(std::addressof(__sb_));
  }

private:
  syncbuf_type __sb_;
};

using std::osyncstream;
#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
using std::wosyncstream;
#  endif

#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS

#endif // _LIBCPP_SYNCSTREAM