diff --git a/libcxx/include/syncstream b/libcxx/include/syncstream new file mode 100644 --- /dev/null +++ b/libcxx/include/syncstream @@ -0,0 +1,231 @@ +// -*- C++ -*- +//===--------------------------- sstream ----------------------------------===// +// +// 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 <__config> +#include +#include +#include + +#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 > 17 + +// basic_stringbuf + +template +class _LIBCPP_TEMPLATE_VIS basic_syncbuf + : public basic_streambuf<_CharT, _Traits> +{ +public: + typedef _CharT char_type; + typedef _Traits traits_type; + typedef typename traits_type::int_type int_type; + typedef typename traits_type::pos_type pos_type; + typedef typename traits_type::off_type off_type; + typedef _Allocator allocator_type; + + typedef basic_streambuf<_CharT, _Traits> streambuf_type; + typedef _VSTD::shared_ptr<_VSTD::mutex> mutex_ptr_type; + + // [syncstream.syncbuf.cons], construction and destruction + + inline _LIBCPP_INLINE_VISIBILITY + explicit basic_syncbuf(streambuf_type* __obuf = nullptr) + : basic_syncbuf(__obuf, _Allocator()) { } + + inline _LIBCPP_INLINE_VISIBILITY + basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc); + + inline _LIBCPP_INLINE_VISIBILITY + basic_syncbuf(basic_syncbuf&& __other); + + inline _LIBCPP_INLINE_VISIBILITY + ~basic_syncbuf(); + + // [syncstream.syncbuf.assign], assignment and swap + + inline _LIBCPP_INLINE_VISIBILITY + basic_syncbuf& operator=(basic_syncbuf&& __other); + + inline _LIBCPP_INLINE_VISIBILITY + void swap(basic_syncbuf&& __other); + + // [syncstream.syncbuf.members], member functions + + inline _LIBCPP_INLINE_VISIBILITY + bool emit(); + + inline _LIBCPP_INLINE_VISIBILITY + streambuf_type* get_wrapped() const noexcept; + + inline _LIBCPP_INLINE_VISIBILITY + allocator_type get_allocator() const noexcept; + + inline _LIBCPP_INLINE_VISIBILITY + void set_emit_on_sync(bool __b) noexcept; + +protected: + + // [syncstream.syncbuf.virtuals], overridden virtual functions + + int sync() override; + +private: + streambuf_type* __wrapped; + allocator_type __alloc; + mutex_ptr_type __mtx_ptr; // this needs to be a pointer so it can be passed around. + bool __emit_on_sync; + bool __should_flush; +}; + +template +inline _LIBCPP_INLINE_VISIBILITY +basic_syncbuf<_CharT, _Traits, _Allocator>::basic_syncbuf(streambuf_type* __obuf, + _Allocator const& __alloc) + : __wrapped(__obuf), + __alloc(__alloc) +{ + if (__mtx_ptr == nullptr) + __mtx_ptr = _VSTD::make_shared<_VSTD::mutex>(); +} + +template +inline _LIBCPP_INLINE_VISIBILITY +basic_syncbuf<_CharT, _Traits, _Allocator>::basic_syncbuf(basic_syncbuf&& __other) + : __wrapped(_VSTD::move(__other.get_wrapped())), + __alloc (_VSTD::move(__other.get_allocator())) +{ + __other.__mtx_ptr.swap(__mtx_ptr); + __other.__wrapped = nullptr; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +basic_syncbuf<_CharT, _Traits, _Allocator>::~basic_syncbuf() +{ + emit(); +} + +// [syncstream.syncbuf.assign], assignment and swap + +template +inline _LIBCPP_INLINE_VISIBILITY +basic_syncbuf<_CharT, _Traits, _Allocator>& +basic_syncbuf<_CharT, _Traits, _Allocator>::operator=(basic_syncbuf&& __other) +{ + __wrapped = _VSTD::move(__other.get_wrapped()); + __alloc = _VSTD::move(__other.get_allocator()); + __other.__mtx_ptr.swap(__mtx_ptr); + __other.__wrapped = nullptr; + return *this; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +void +basic_syncbuf<_CharT, _Traits, _Allocator>::swap(basic_syncbuf&& __other) +{ + basic_syncbuf __tmp(_VSTD::move(__other)); + __other = _VSTD::move(*this); + *this = _VSTD::move(__tmp); +} + +// [syncstream.syncbuf.members], member functions + +template +inline _LIBCPP_INLINE_VISIBILITY +bool +basic_syncbuf<_CharT, _Traits, _Allocator>::emit() +{ + bool __result = true; + if (__wrapped == nullptr || __mtx_ptr == nullptr) + __result = false; + + if (__result) + { + _VSTD::lock_guard<_VSTD::mutex> gaurd(*__mtx_ptr); + + if (this->pptr()) + { + streamsize __size = _VSTD::distance(this->pptr(), this->epptr()); + __result &= (__wrapped->sputn(this->pptr(), __size) != -1); + this->setp(nullptr, nullptr); + } + + if (__should_flush) + __result &= (__wrapped->pubsync() != -1); + } + + __should_flush = false; + return __result; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +typename basic_syncbuf<_CharT, _Traits, _Allocator>::streambuf_type* +basic_syncbuf<_CharT, _Traits, _Allocator>::get_wrapped() const noexcept +{ + return __wrapped; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +typename basic_syncbuf<_CharT, _Traits, _Allocator>::allocator_type +basic_syncbuf<_CharT, _Traits, _Allocator>::get_allocator() const noexcept +{ + return __alloc; +} + +template +inline _LIBCPP_INLINE_VISIBILITY +void +basic_syncbuf<_CharT, _Traits, _Allocator>::set_emit_on_sync(bool __b) noexcept +{ + __emit_on_sync = __b; +} + +// [syncstream.syncbuf.virtuals], overridden virtual functions + +template +int +basic_syncbuf<_CharT, _Traits, _Allocator>::sync() +{ + __should_flush = true; + + if (__emit_on_sync) + { + if (emit() == false) return -1; + } + + return 0; +} + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP_SYNCSTREAM diff --git a/libcxx/test/std/input.output/sncstream/syncbuf/assign.pass.cpp b/libcxx/test/std/input.output/sncstream/syncbuf/assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/sncstream/syncbuf/assign.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf& operator=(basic_syncbuf&& rhs) noexcept; +// void swap(basic_syncbuf& other) noexcept; + +#include +#include + +#include "test_macros.h" + +template +class test_syncbuf + : public std::basic_syncbuf, std::allocator> { +}; + +template +void test_assign() +{ + using BuffT = std::basic_syncbuf, std::allocator>; + + test_syncbuf base; + std::allocator alloc; + BuffT buff1(&base, alloc); + BuffT buff2 = std::move(buff1); + + // TODO: test emit called + assert(buff1.get_wrapped() == nullptr); + assert(buff2.get_wrapped() == &base); + + // Also LWG issue? + // assert(buff1.rdbuf()->pbase() == buff1.rdbuf()->pptr()); + // TODO: test allocators +} + +template +void test_swap() +{ + using BuffT = std::basic_syncbuf, std::allocator>; + + test_syncbuf base1; + test_syncbuf base2; + std::allocator alloc; + BuffT buff1(&base1, alloc); + BuffT buff2(&base2, alloc); + buff1.swap(std::move(buff2)); + + assert(buff1.get_wrapped() == &base2); + assert(buff2.get_wrapped() == &base1); + // TODO: test allocator +} + +int main(int, char**) +{ + test_assign(); + test_assign(); + test_assign(); + test_assign(); + + test_swap(); + test_swap(); + test_swap(); + test_swap(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/sncstream/syncbuf/cons.pass.cpp b/libcxx/test/std/input.output/sncstream/syncbuf/cons.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/sncstream/syncbuf/cons.pass.cpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +struct test_allocator : std::allocator +{ + int id; + int moves = 0; + int copies = 0; + + test_allocator(int id = 0) : id(id) { } + test_allocator(test_allocator const& other) + : id(other.id), moves(other.moves), copies(other.copies) { } + test_allocator(test_allocator&& other) + : id(std::move(other.id)), + moves(std::move(other.moves)), + copies(std::move(other.copies)) { } +}; + +template +class test_buf + : public std::basic_streambuf { +}; + +template +void test_streambuf_const() +{ + using BuffT = std::basic_syncbuf, test_allocator>; + + test_buf base; + test_allocator alloc(42); + BuffT buff(&base, alloc); + + assert(buff.get_wrapped() == &base); + assert(buff.get_allocator().id == alloc.id); +} + +template +void test_move_const() +{ + using BuffT = std::basic_syncbuf, test_allocator>; + + test_buf base; + test_allocator alloc(42); + BuffT buff1(&base, alloc); + BuffT buff2(std::move(buff1)); + + assert(buff1.get_wrapped() == nullptr); + assert(buff2.get_wrapped() == &base); + + // LWG issue? + // assert(buff1.rdbuf()->pbase() == buff1.rdbuf()->pptr()); +} + +int main(int, char**) +{ + test_streambuf_const(); + test_streambuf_const(); + test_streambuf_const(); + test_streambuf_const(); + + test_move_const(); + test_move_const(); + test_move_const(); + test_move_const(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/sncstream/syncbuf/members.pass.cpp b/libcxx/test/std/input.output/sncstream/syncbuf/members.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/sncstream/syncbuf/members.pass.cpp @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +class test_buf : public std::basic_streambuf { +public: + int id; + + test_buf(int id = 0) : id(id) { } + + T *_pptr() { return this->pptr(); } +}; + +template> +class test_syncbuf + : public std::basic_syncbuf, Alloc> { +public: + test_syncbuf(test_buf* buf, Alloc alloc) + : std::basic_syncbuf, Alloc>(buf, alloc) { } + + void _setp(T *begin, T *end) { + return this->setp(begin, end); + } +}; + +template +struct test_allocator : std::allocator +{ + int id; + test_allocator(int id = 0) : id(id) { } +}; + +template +void test_emit() +{ + // TODO: test mutex + + CharT arr[3] = { 'a', 'b', 'c' }; + CharT *ptr = arr; + + test_buf base; + std::allocator alloc; + test_syncbuf buff(&base, alloc); + + buff._setp(ptr, ptr + 3); + assert(base._pptr() == nullptr); + buff.emit(); + CharT *pptr = base._pptr(); + while(pptr) { assert(*pptr++ == *ptr++); } +} + +template +void test_get_wrapped() +{ + test_buf base(42); + std::allocator alloc; + test_syncbuf buff(&base, alloc); + assert(static_cast*>(buff.get_wrapped())->id == 42); +} + +template +void test_get_allocator() +{ + test_buf base; + test_allocator alloc(42); + test_syncbuf> buff(&base, alloc); + assert(buff.get_allocator().id == 42); +} + +int main(int, char**) +{ + test_emit(); + test_emit(); + test_emit(); + test_emit(); + + test_get_wrapped(); + test_get_wrapped(); + test_get_wrapped(); + test_get_wrapped(); + + test_get_allocator(); + test_get_allocator(); + test_get_allocator(); + test_get_allocator(); + + // set_emit_on_sync tested in sync + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/sncstream/syncbuf/special.pass.cpp b/libcxx/test/std/input.output/sncstream/syncbuf/special.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/sncstream/syncbuf/special.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include + +#include "test_macros.h" + +template +class test_streambuf + : public std::basic_streambuf { +}; + +template +void test_swap() +{ + using BuffT = std::basic_syncbuf, std::allocator>; + + test_streambuf base1; + test_streambuf base2; + std::allocator alloc; + BuffT buff1(&base1, alloc); + BuffT buff2(&base2, alloc); + std::swap(buff1, buff2); + + assert(buff1.get_wrapped() == &base2); + assert(buff2.get_wrapped() == &base1); + // TODO: test allocator +} + +int main(int, char**) +{ + test_swap(); + test_swap(); + test_swap(); + test_swap(); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/input.output/sncstream/syncbuf/virtuals.pass.cpp b/libcxx/test/std/input.output/sncstream/syncbuf/virtuals.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/sncstream/syncbuf/virtuals.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// class basic_syncbuf; + +// basic_syncbuf(streambuf_type* obuf, const Allocator& allocator); +// basic_syncbuf(basic_syncbuf&& other); + +#include +#include +#include + +#include "test_macros.h" + +template +class test_buf : public std::basic_streambuf { + int id; + +public: + test_buf(int id = 0) : id(id) { } + + T *_pbase() { return this->pbase(); } +}; + +template +class test_syncbuf + : public std::basic_syncbuf, std::allocator> { +public: + test_syncbuf(test_buf* buf, std::allocator alloc) + : std::basic_syncbuf, std::allocator>(buf, alloc) { } + + void _setp(T *begin, T *end) { + return this->setp(begin, end); + } + + int _sync() { + return this->sync(); + } +}; + +template +void test_sync() +{ + CharT ptr[2] = { 'a', 'b' }; + std::basic_ostringstream ss; + test_buf *base = reinterpret_cast*>(ss.rdbuf()); + std::allocator alloc; + test_syncbuf buff(base, alloc); + + // emit_on_sync = true + buff.set_emit_on_sync(true); + buff._setp(ptr, ptr + 1); + assert(base->_pbase() == nullptr); + assert(buff._sync() == 0); + assert(*base->_pbase() == 'a'); + + // emit_on_sync = false + buff.set_emit_on_sync(false); + buff._setp(ptr + 1, ptr + 2); + assert(buff._sync() == 0); + assert(*(base->_pbase() + 1) == 0); + buff.emit(); + assert(*(base->_pbase() + 1) == 'b'); + + // TODO: test when emit == false +} + +int main(int, char**) +{ + test_sync(); + test_sync(); + test_sync(); + test_sync(); + + return 0; +} \ No newline at end of file