400 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710// UNSUPPORTED: no-localization11// UNSUPPORTED: libcpp-has-no-experimental-syncstream12 13// <syncstream>14 15// template <class charT, class traits, class Allocator>16// class basic_syncbuf;17 18// basic_syncbuf& operator=(basic_syncbuf&& rhs);19 20#include <syncstream>21#include <sstream>22#include <cassert>23#include <concepts>24 25#include "test_macros.h"26 27template <class T, class propagate>28struct test_allocator : std::allocator<T> {29 using propagate_on_container_move_assignment = propagate;30 31 int id{-1};32 33 test_allocator(int _id = -1) : id(_id) {}34 test_allocator(test_allocator const& other) = default;35 test_allocator(test_allocator&& other) = default;36 test_allocator& operator=(const test_allocator& other) = default;37 38 test_allocator& operator=(test_allocator&& other) {39 if constexpr (propagate_on_container_move_assignment::value)40 id = other.id;41 else42 id = -1;43 return *this;44 }45};46 47template <class T>48class test_buf : public std::basic_streambuf<T> {49public:50 int id;51 52 test_buf(int _id = 0) : id(_id) {}53 54 T* _pptr() { return this->pptr(); }55};56 57template <class T, class Alloc = std::allocator<T>>58class test_syncbuf : public std::basic_syncbuf<T, std::char_traits<T>, Alloc> {59 using Base = std::basic_syncbuf<T, std::char_traits<T>, Alloc>;60 61public:62 test_syncbuf() = default;63 64 test_syncbuf(test_buf<T>* buf, Alloc alloc) : Base(buf, alloc) {}65 66 test_syncbuf(typename Base::streambuf_type* buf, Alloc alloc) : Base(buf, alloc) {}67 68 void _setp(T* begin, T* end) { return this->setp(begin, end); }69};70 71// Helper wrapper to inspect the internal state of the basic_syncbuf72//73// This is used to validate some standard requirements and libc++74// implementation details.75template <class CharT, class Traits, class Allocator>76class syncbuf_inspector : public std::basic_syncbuf<CharT, Traits, Allocator> {77public:78 syncbuf_inspector() = default;79 explicit syncbuf_inspector(std::basic_syncbuf<CharT, Traits, Allocator>&& base)80 : std::basic_syncbuf<CharT, Traits, Allocator>(std::move(base)) {}81 82 void operator=(std::basic_syncbuf<CharT, Traits, Allocator>&& base) { *this = std::move(base); }83 84 using std::basic_syncbuf<CharT, Traits, Allocator>::pbase;85 using std::basic_syncbuf<CharT, Traits, Allocator>::pptr;86 using std::basic_syncbuf<CharT, Traits, Allocator>::epptr;87};88 89template <class CharT>90static void test_assign() {91 test_buf<CharT> base;92 93 { // Test using the real class, propagating allocator.94 using BuffT = std::basic_syncbuf<CharT, std::char_traits<CharT>, test_allocator<CharT, std::true_type>>;95 96 BuffT buff1(&base, test_allocator<CharT, std::true_type>{42});97 buff1.sputc(CharT('A'));98 99 assert(buff1.get_wrapped() != nullptr);100 101 BuffT buff2;102 assert(buff2.get_allocator().id == -1);103 buff2 = std::move(buff1);104 assert(buff1.get_wrapped() == nullptr);105 assert(buff2.get_wrapped() == &base);106 107 assert(buff2.get_wrapped() == &base);108 assert(buff2.get_allocator().id == 42);109 }110 111 { // Test using the real class, non-propagating allocator.112 using BuffT = std::basic_syncbuf<CharT, std::char_traits<CharT>, test_allocator<CharT, std::false_type>>;113 114 BuffT buff1(&base, test_allocator<CharT, std::false_type>{42});115 buff1.sputc(CharT('A'));116 117 assert(buff1.get_wrapped() != nullptr);118 119 BuffT buff2;120 assert(buff2.get_allocator().id == -1);121 buff2 = std::move(buff1);122 assert(buff1.get_wrapped() == nullptr);123 assert(buff2.get_wrapped() == &base);124 125 assert(buff2.get_wrapped() == &base);126 assert(buff2.get_allocator().id == -1);127 }128 129 { // Move assignment propagating allocator130 // Test using the inspection wrapper.131 // Not all these requirements are explicitly in the Standard,132 // however the asserts are based on secondary requirements. The133 // LIBCPP_ASSERTs are implementation specific.134 135 using BuffT = std::basic_syncbuf<CharT, std::char_traits<CharT>, std::allocator<CharT>>;136 137 using Inspector = syncbuf_inspector<CharT, std::char_traits<CharT>, std::allocator<CharT>>;138 Inspector inspector1{BuffT(&base)};139 inspector1.sputc(CharT('A'));140 141 assert(inspector1.get_wrapped() != nullptr);142 assert(inspector1.pbase() != nullptr);143 assert(inspector1.pptr() != nullptr);144 assert(inspector1.epptr() != nullptr);145 assert(inspector1.pbase() != inspector1.pptr());146 assert(inspector1.pptr() - inspector1.pbase() == 1);147 [[maybe_unused]] std::streamsize size = inspector1.epptr() - inspector1.pbase();148 149 Inspector inspector2;150 inspector2 = std::move(inspector1);151 152 assert(inspector1.get_wrapped() == nullptr);153 LIBCPP_ASSERT(inspector1.pbase() == nullptr);154 LIBCPP_ASSERT(inspector1.pptr() == nullptr);155 LIBCPP_ASSERT(inspector1.epptr() == nullptr);156 assert(inspector1.pbase() == inspector1.pptr());157 158 assert(inspector2.get_wrapped() == &base);159 LIBCPP_ASSERT(inspector2.pbase() != nullptr);160 LIBCPP_ASSERT(inspector2.pptr() != nullptr);161 LIBCPP_ASSERT(inspector2.epptr() != nullptr);162 assert(inspector2.pptr() - inspector2.pbase() == 1);163 LIBCPP_ASSERT(inspector2.epptr() - inspector2.pbase() == size);164 }165}166 167template <class CharT>168static void test_basic() {169 { // Test properties170 std::basic_syncbuf<CharT> sync_buf1(nullptr);171 std::basic_syncbuf<CharT> sync_buf2(nullptr);172 [[maybe_unused]] std::same_as<std::basic_syncbuf<CharT>&> decltype(auto) ret =173 sync_buf1.operator=(std::move(sync_buf2));174 }175 176 std::basic_stringbuf<CharT> sstr1;177 std::basic_stringbuf<CharT> sstr2;178 std::basic_string<CharT> expected(42, CharT('*')); // a long string179 180 {181 std::basic_syncbuf<CharT> sync_buf1(&sstr1);182 sync_buf1.sputc(CharT('A')); // a short string183 184 std::basic_syncbuf<CharT> sync_buf2(&sstr2);185 sync_buf2.sputn(expected.data(), expected.size());186 187#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS)188 assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr1) == 1);189 assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr2) == 1);190#endif191 192 sync_buf2 = std::move(sync_buf1);193 assert(sync_buf2.get_wrapped() == &sstr1);194 195 assert(sstr1.str().empty());196 assert(sstr2.str() == expected);197 198#if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_THREADS)199 assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr1) == 1);200 assert(std::__wrapped_streambuf_mutex::__instance().__get_count(&sstr2) == 0);201#endif202 }203 204 assert(sstr1.str().size() == 1);205 assert(sstr1.str()[0] == CharT('A'));206 assert(sstr2.str() == expected);207}208 209template <class CharT>210static void test_short_write_after_assign() {211 std::basic_stringbuf<CharT> sstr1;212 std::basic_stringbuf<CharT> sstr2;213 std::basic_string<CharT> expected(42, CharT('*')); // a long string214 215 {216 std::basic_syncbuf<CharT> sync_buf1(&sstr1);217 sync_buf1.sputc(CharT('A')); // a short string218 219 std::basic_syncbuf<CharT> sync_buf2(&sstr2);220 sync_buf2.sputn(expected.data(), expected.size());221 222 sync_buf2 = std::move(sync_buf1);223 sync_buf2.sputc(CharT('Z'));224 225 assert(sstr1.str().empty());226 assert(sstr2.str() == expected);227 }228 229 assert(sstr1.str().size() == 2);230 assert(sstr1.str()[0] == CharT('A'));231 assert(sstr1.str()[1] == CharT('Z'));232 assert(sstr2.str() == expected);233}234 235template <class CharT>236static void test_long_write_after_assign() {237 std::basic_stringbuf<CharT> sstr1;238 std::basic_stringbuf<CharT> sstr2;239 std::basic_string<CharT> expected(42, CharT('*')); // a long string240 241 {242 std::basic_syncbuf<CharT> sync_buf1(&sstr1);243 sync_buf1.sputc(CharT('A')); // a short string244 245 std::basic_syncbuf<CharT> sync_buf2(&sstr2);246 sync_buf2.sputn(expected.data(), expected.size());247 248 sync_buf2 = std::move(sync_buf1);249 sync_buf2.sputn(expected.data(), expected.size());250 251 assert(sstr1.str().empty());252 assert(sstr2.str() == expected);253 }254 255 assert(sstr1.str().size() == 1 + expected.size());256 assert(sstr1.str()[0] == CharT('A'));257 assert(sstr1.str().substr(1) == expected);258 assert(sstr2.str() == expected);259}260 261template <class CharT>262static void test_emit_on_assign() {263 { // don't emit / don't emit264 265 std::basic_stringbuf<CharT> sstr1;266 std::basic_stringbuf<CharT> sstr2;267 std::basic_string<CharT> expected(42, CharT('*')); // a long string268 269 {270 std::basic_syncbuf<CharT> sync_buf1(&sstr1);271 sync_buf1.set_emit_on_sync(false);272 sync_buf1.sputc(CharT('A')); // a short string273 274 std::basic_syncbuf<CharT> sync_buf2(&sstr2);275 sync_buf2.set_emit_on_sync(false);276 sync_buf2.sputn(expected.data(), expected.size());277 278 sync_buf2 = std::move(sync_buf1);279 assert(sstr1.str().empty());280 assert(sstr2.str() == expected);281 282 sync_buf2.pubsync();283 assert(sstr1.str().empty());284 assert(sstr2.str() == expected);285 }286 287 assert(sstr1.str().size() == 1);288 assert(sstr1.str()[0] == CharT('A'));289 assert(sstr2.str() == expected);290 }291 292 { // don't emit / do emit293 294 std::basic_stringbuf<CharT> sstr1;295 std::basic_stringbuf<CharT> sstr2;296 std::basic_string<CharT> expected(42, CharT('*')); // a long string297 298 {299 std::basic_syncbuf<CharT> sync_buf1(&sstr1);300 sync_buf1.set_emit_on_sync(true);301 sync_buf1.sputc(CharT('A')); // a short string302 303 std::basic_syncbuf<CharT> sync_buf2(&sstr2);304 sync_buf2.set_emit_on_sync(false);305 sync_buf2.sputn(expected.data(), expected.size());306 307 sync_buf2 = std::move(sync_buf1);308 assert(sstr1.str().empty());309 assert(sstr2.str() == expected);310 311 sync_buf2.pubsync();312 assert(sstr1.str().size() == 1);313 assert(sstr1.str()[0] == CharT('A'));314 assert(sstr2.str() == expected);315 }316 317 assert(sstr1.str().size() == 1);318 assert(sstr1.str()[0] == CharT('A'));319 assert(sstr2.str() == expected);320 }321 322 { // do emit / don't emit323 324 std::basic_stringbuf<CharT> sstr1;325 std::basic_stringbuf<CharT> sstr2;326 std::basic_string<CharT> expected(42, CharT('*')); // a long string327 328 {329 std::basic_syncbuf<CharT> sync_buf1(&sstr1);330 sync_buf1.set_emit_on_sync(false);331 sync_buf1.sputc(CharT('A')); // a short string332 333 std::basic_syncbuf<CharT> sync_buf2(&sstr2);334 sync_buf2.set_emit_on_sync(true);335 sync_buf2.sputn(expected.data(), expected.size());336 337 sync_buf2 = std::move(sync_buf1);338 assert(sstr1.str().empty());339 assert(sstr2.str() == expected);340 341 sync_buf2.pubsync();342 assert(sstr1.str().empty());343 assert(sstr2.str() == expected);344 }345 346 assert(sstr1.str().size() == 1);347 assert(sstr1.str()[0] == CharT('A'));348 assert(sstr2.str() == expected);349 }350 351 { // do emit / do emit352 353 std::basic_stringbuf<CharT> sstr1;354 std::basic_stringbuf<CharT> sstr2;355 std::basic_string<CharT> expected(42, CharT('*')); // a long string356 357 {358 std::basic_syncbuf<CharT> sync_buf1(&sstr1);359 sync_buf1.set_emit_on_sync(true);360 sync_buf1.sputc(CharT('A')); // a short string361 362 std::basic_syncbuf<CharT> sync_buf2(&sstr2);363 sync_buf2.set_emit_on_sync(true);364 sync_buf2.sputn(expected.data(), expected.size());365 366 sync_buf2 = std::move(sync_buf1);367 assert(sstr1.str().empty());368 assert(sstr2.str() == expected);369 370 sync_buf2.pubsync();371 assert(sstr1.str().size() == 1);372 assert(sstr1.str()[0] == CharT('A'));373 assert(sstr2.str() == expected);374 }375 376 assert(sstr1.str().size() == 1);377 assert(sstr1.str()[0] == CharT('A'));378 assert(sstr2.str() == expected);379 }380}381 382template <class CharT>383static void test() {384 test_assign<CharT>();385 test_basic<CharT>();386 test_short_write_after_assign<CharT>();387 test_long_write_after_assign<CharT>();388 test_emit_on_assign<CharT>();389}390 391int main(int, char**) {392 test<char>();393 394#ifndef TEST_HAS_NO_WIDE_CHARACTERS395 test<wchar_t>();396#endif397 398 return 0;399}400