STL/stl/inc/syncstream

382 строки
12 KiB
C++

// syncstream standard header
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#pragma once
#ifndef _SYNCSTREAM_
#define _SYNCSTREAM_
#include <yvals.h>
#if _STL_COMPILER_PREPROCESSOR
#if !_HAS_CXX20
#pragma message("The contents of <syncstream> are available only with C++20 or later.")
#else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv
#include <memory>
#include <ostream>
#include <shared_mutex>
#include <streambuf>
#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#pragma push_macro("emit")
#undef new
#undef emit
_EXTERN_C
_NODISCARD _STD shared_mutex* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept;
void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept;
_END_EXTERN_C
_STD_BEGIN
// CLASS TEMPLATE _Basic_syncbuf_impl
template <class _Elem, class _Traits>
class _Basic_syncbuf_impl : public basic_streambuf<_Elem, _Traits> {
public:
void set_emit_on_sync(const bool _Val) noexcept {
_Emit_on_sync = _Val;
}
virtual bool _Do_emit() = 0;
#ifdef _ENABLE_STL_INTERNAL_CHECK
_NODISCARD bool _Stl_internal_check_get_emit_on_sync() const noexcept {
return _Emit_on_sync;
}
#endif // _ENABLE_STL_INTERNAL_CHECK
protected:
using _Mysb = basic_streambuf<_Elem, _Traits>;
_Basic_syncbuf_impl() = default;
_Basic_syncbuf_impl(_Basic_syncbuf_impl&& _Right) {
_Swap(_Right);
}
void _Swap(_Basic_syncbuf_impl& _Right) { // see LWG-3498 regarding noexcept
_Mysb::swap(_Right);
_STD swap(_Emit_on_sync, _Right._Emit_on_sync);
_STD swap(_Sync_recorded, _Right._Sync_recorded);
}
bool _Emit_on_sync{false};
bool _Sync_recorded{false};
};
// CLASS TEMPLATE basic_syncbuf
template <class _Elem, class _Traits, class _Alloc>
class basic_syncbuf : public _Basic_syncbuf_impl<_Elem, _Traits> {
public:
using int_type = typename _Traits::int_type;
using pos_type = typename _Traits::pos_type;
using off_type = typename _Traits::off_type;
using allocator_type = _Alloc;
using streambuf_type = basic_streambuf<_Elem, _Traits>;
using _Mybase = _Basic_syncbuf_impl<_Elem, _Traits>;
using _Pointer = typename allocator_traits<_Alloc>::pointer;
using _Size_type = typename allocator_traits<_Alloc>::size_type;
using _Mybase::set_emit_on_sync;
basic_syncbuf() = default;
explicit basic_syncbuf(streambuf_type* _Strbuf) : basic_syncbuf(_Strbuf, _Alloc{}) {}
basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_)
: _Wrapped(_Strbuf), _Mypair{_One_then_variadic_args_t{}, _Al_, nullptr} {
if (_Wrapped) {
auto& _Mutex = _Get_mutex();
_Mutex = __std_acquire_shared_mutex_for_instance(_Wrapped);
if (!_Mutex) {
_Xbad_alloc();
}
}
_Init();
}
basic_syncbuf(basic_syncbuf&& _Right) : _Mypair{_One_then_variadic_args_t{}, _STD move(_Right._Getal()), nullptr} {
_Swap_except_al(_Right);
}
~basic_syncbuf() {
_Emit();
_Tidy();
}
basic_syncbuf& operator=(basic_syncbuf&& _Right) { // see LWG-3498 regarding noexcept
emit();
if (this != _STD addressof(_Right)) {
_Move_assign(_STD move(_Right), _Choose_pocma<_Alloc>{});
}
return *this;
}
void swap(basic_syncbuf& _Right) { // see LWG-3498 regarding noexcept
if (this != _STD addressof(_Right)) {
_Pocs(_Getal(), _Right._Getal());
_Swap_except_al(_Right);
}
}
bool emit() {
if (!_Wrapped) {
return false;
}
bool _Result = true;
const _Size_type _Data_size = _Get_data_size();
_Elem* const _Begin_seq_ptr = streambuf_type::pbase();
if (_Data_size > 0 || _Mybase::_Sync_recorded) {
scoped_lock _Guard(*_Get_mutex());
if (_Data_size > 0
&& _Data_size
!= static_cast<_Size_type>(
_Wrapped->sputn(_Begin_seq_ptr, static_cast<streamsize>(_Data_size)))) {
_Result = false;
}
if (_Mybase::_Sync_recorded) {
if (_Wrapped->pubsync() == -1) {
_Result = false;
}
}
}
_Mybase::_Sync_recorded = false;
streambuf_type::setp(_Begin_seq_ptr, streambuf_type::epptr()); // reset written data
return _Result;
}
_NODISCARD streambuf_type* get_wrapped() const noexcept {
return _Wrapped;
}
_NODISCARD allocator_type get_allocator() const noexcept {
return _Mypair._Get_first();
}
protected:
virtual int sync() override {
_Mybase::_Sync_recorded = true;
if (_Mybase::_Emit_on_sync) {
if (!emit()) {
return -1;
}
}
return 0;
}
virtual int_type overflow(int_type _Current_elem) override {
if (!_Wrapped) {
return _Traits::eof();
}
const bool _Chk_eof = _Traits::eq_int_type(_Current_elem, _Traits::eof());
if (_Chk_eof) {
return _Traits::not_eof(_Current_elem);
}
auto& _Al = _Getal();
const _Size_type _Buf_size = _Get_buffer_size();
const _Size_type _Max_allocation = allocator_traits<_Alloc>::max_size(_Al);
if (_Buf_size == _Max_allocation) {
return _Traits::eof();
}
const _Size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation);
const _Elem* const _Old_ptr = streambuf_type::pbase();
const _Size_type _Old_data_size = _Get_data_size();
_Elem* const _New_ptr = _Unfancy(_Al.allocate(_New_capacity));
_Traits::copy(_New_ptr, _Old_ptr, _Old_data_size);
streambuf_type::setp(_New_ptr, _New_ptr + _Old_data_size, _New_ptr + _New_capacity);
streambuf_type::sputc(_Traits::to_char_type(_Current_elem));
return _Current_elem;
}
private:
static constexpr _Size_type _Min_size = 32; // constant for minimum buffer size
void _Init() {
_Elem* const _New_ptr = _Unfancy(_Getal().allocate(_Min_size));
streambuf_type::setp(_New_ptr, _New_ptr + _Min_size);
}
void _Tidy() noexcept {
const _Size_type _Buf_size = _Get_buffer_size();
if (0 < _Buf_size) {
_Getal().deallocate(_Refancy<_Pointer>(streambuf_type::pbase()), _Buf_size);
}
streambuf_type::setp(nullptr, nullptr, nullptr);
if (_Wrapped) {
__std_release_shared_mutex_for_instance(_Wrapped);
_Wrapped = nullptr;
_Get_mutex() = nullptr;
}
}
void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) { // see LWG-3498 regarding noexcept
_Tidy();
_Pocma(_Getal(), _Right._Getal());
_Swap_except_al(_Right);
}
void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) { // see LWG-3498 regarding noexcept
_Tidy();
_Pocma(_Getal(), _Right._Getal());
_Swap_except_al(_Right);
}
void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { // see LWG-3498 regarding noexcept
auto& _Al = _Getal();
if (_Al == _Right._Getal()) {
_Move_assign(_STD move(_Right), _Equal_allocators{});
} else {
_Tidy();
const _Size_type _Right_buf_size = _Right._Get_buffer_size();
const _Size_type _Right_data_size = _Right._Get_data_size();
_Elem* const _New_ptr = _Unfancy(_Al.allocate(_Right_buf_size));
_Traits::copy(_New_ptr, _Right.pbase(), _Right_data_size);
streambuf_type::setp(_New_ptr, _New_ptr + _Right_data_size, _New_ptr + _Right_buf_size);
_STD swap(streambuf_type::_Plocale, _Right._Plocale);
_STD swap(_Mybase::_Emit_on_sync, _Right._Emit_on_sync);
_STD swap(_Mybase::_Sync_recorded, _Right._Sync_recorded);
_STD swap(_Wrapped, _Right._Wrapped);
_STD swap(_Get_mutex(), _Right._Get_mutex());
_Right._Tidy();
}
}
void _Swap_except_al(basic_syncbuf& _Right) { // see LWG-3498 regarding noexcept
_Mybase::_Swap(_Right);
_STD swap(_Wrapped, _Right._Wrapped);
_STD swap(_Get_mutex(), _Right._Get_mutex());
}
virtual bool _Do_emit() override {
return emit();
}
bool _Emit() noexcept {
_TRY_BEGIN
return emit();
_CATCH_ALL
return false;
_CATCH_END
}
_NODISCARD static constexpr _Size_type _Calculate_growth(
const _Size_type _Oldsize, const _Size_type _Newsize, const _Size_type _Maxsize) {
if (_Oldsize > _Maxsize - _Oldsize / 2) {
return _Maxsize; // geometric growth would overflow
}
const _Size_type _Geometric = _Oldsize + _Oldsize / 2;
if (_Geometric < _Newsize) {
return _Newsize; // geometric growth would be insufficient
}
return _Geometric; // geometric growth is sufficient
}
_NODISCARD _Size_type _Get_data_size() const noexcept {
return static_cast<_Size_type>(streambuf_type::pptr() - streambuf_type::pbase());
}
_NODISCARD _Size_type _Get_buffer_size() const noexcept {
return static_cast<_Size_type>(streambuf_type::epptr() - streambuf_type::pbase());
}
_NODISCARD _Alloc& _Getal() noexcept {
return _Mypair._Get_first();
}
_NODISCARD shared_mutex*& _Get_mutex() noexcept {
return _Mypair._Myval2;
}
streambuf_type* _Wrapped{nullptr};
_Compressed_pair<_Alloc, shared_mutex*> _Mypair{_Zero_then_variadic_args_t{}, nullptr};
};
template <class _Elem, class _Traits, class _Alloc>
void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left,
basic_syncbuf<_Elem, _Traits, _Alloc>& _Right) { // see LWG-3498 regarding noexcept
_Left.swap(_Right);
}
// CLASS TEMPLATE basic_osyncstream
template <class _Elem, class _Traits, class _Alloc>
class basic_osyncstream : public basic_ostream<_Elem, _Traits> {
public:
using char_type = _Elem;
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 = _Alloc;
using streambuf_type = basic_streambuf<_Elem, _Traits>;
using syncbuf_type = basic_syncbuf<_Elem, _Traits, _Alloc>;
using _Mybase = basic_ostream<_Elem, _Traits>;
basic_osyncstream(streambuf_type* _Strbuf, const _Alloc& _Al)
: _Mybase(_STD addressof(_Sync_buf)), _Sync_buf(_Strbuf, _Al) {}
explicit basic_osyncstream(streambuf_type* _Strbuf) : basic_osyncstream(_Strbuf, _Alloc{}) {}
basic_osyncstream(basic_ostream<_Elem, _Traits>& _Ostr, const _Alloc& _Al)
: basic_osyncstream(_Ostr.rdbuf(), _Al) {}
explicit basic_osyncstream(basic_ostream<_Elem, _Traits>& _Ostr) : basic_osyncstream(_Ostr, _Alloc{}) {}
basic_osyncstream(basic_osyncstream&& _Right) noexcept
: _Mybase(_STD move(_Right)), _Sync_buf(_STD move(_Right._Sync_buf)) {
_Mybase::set_rdbuf(_STD addressof(_Sync_buf));
}
~basic_osyncstream() = default;
basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default;
void emit() {
if (!_Sync_buf.emit()) {
_Mybase::setstate(ios::badbit);
}
}
_NODISCARD streambuf_type* get_wrapped() const noexcept {
return _Sync_buf.get_wrapped();
}
_NODISCARD syncbuf_type* rdbuf() const noexcept {
return const_cast<syncbuf_type*>(_STD addressof(_Sync_buf));
}
private:
syncbuf_type _Sync_buf;
};
_STD_END
#pragma pop_macro("emit")
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _HAS_CXX20
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _SYNCSTREAM_