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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 // UNSUPPORTED: no-localization
11 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
12
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
27 template <class T, class propagate>
28 struct test_allocator : std::allocator<T> {
29 using propagate_on_container_move_assignment = propagate;
30
31 int id{-1};
32
test_allocatortest_allocator33 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
operator =test_allocator38 test_allocator& operator=(test_allocator&& other) {
39 if constexpr (propagate_on_container_move_assignment::value)
40 id = other.id;
41 else
42 id = -1;
43 return *this;
44 }
45 };
46
47 template <class T>
48 class test_buf : public std::basic_streambuf<T> {
49 public:
50 int id;
51
test_buf(int _id=0)52 test_buf(int _id = 0) : id(_id) {}
53
_pptr()54 T* _pptr() { return this->pptr(); }
55 };
56
57 template <class T, class Alloc = std::allocator<T>>
58 class 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
61 public:
62 test_syncbuf() = default;
63
test_syncbuf(test_buf<T> * buf,Alloc alloc)64 test_syncbuf(test_buf<T>* buf, Alloc alloc) : Base(buf, alloc) {}
65
test_syncbuf(typename Base::streambuf_type * buf,Alloc alloc)66 test_syncbuf(typename Base::streambuf_type* buf, Alloc alloc) : Base(buf, alloc) {}
67
_setp(T * begin,T * end)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_syncbuf
72 //
73 // This is used to validate some standard requirements and libc++
74 // implementation details.
75 template <class CharT, class Traits, class Allocator>
76 class syncbuf_inspector : public std::basic_syncbuf<CharT, Traits, Allocator> {
77 public:
78 syncbuf_inspector() = default;
syncbuf_inspector(std::basic_syncbuf<CharT,Traits,Allocator> && base)79 explicit syncbuf_inspector(std::basic_syncbuf<CharT, Traits, Allocator>&& base)
80 : std::basic_syncbuf<CharT, Traits, Allocator>(std::move(base)) {}
81
operator =(std::basic_syncbuf<CharT,Traits,Allocator> && base)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
89 template <class CharT>
test_assign()90 static 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 allocator
130 // Test using the inspection wrapper.
131 // Not all these requirements are explicitly in the Standard,
132 // however the asserts are based on secondary requirements. The
133 // 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
167 template <class CharT>
test_basic()168 static void test_basic() {
169 { // Test properties
170 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 string
179
180 {
181 std::basic_syncbuf<CharT> sync_buf1(&sstr1);
182 sync_buf1.sputc(CharT('A')); // a short string
183
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 #endif
191
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 #endif
202 }
203
204 assert(sstr1.str().size() == 1);
205 assert(sstr1.str()[0] == CharT('A'));
206 assert(sstr2.str() == expected);
207 }
208
209 template <class CharT>
test_short_write_after_assign()210 static 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 string
214
215 {
216 std::basic_syncbuf<CharT> sync_buf1(&sstr1);
217 sync_buf1.sputc(CharT('A')); // a short string
218
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
235 template <class CharT>
test_long_write_after_assign()236 static 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 string
240
241 {
242 std::basic_syncbuf<CharT> sync_buf1(&sstr1);
243 sync_buf1.sputc(CharT('A')); // a short string
244
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
261 template <class CharT>
test_emit_on_assign()262 static void test_emit_on_assign() {
263 { // don't emit / don't emit
264
265 std::basic_stringbuf<CharT> sstr1;
266 std::basic_stringbuf<CharT> sstr2;
267 std::basic_string<CharT> expected(42, CharT('*')); // a long string
268
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 string
273
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 emit
293
294 std::basic_stringbuf<CharT> sstr1;
295 std::basic_stringbuf<CharT> sstr2;
296 std::basic_string<CharT> expected(42, CharT('*')); // a long string
297
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 string
302
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 emit
323
324 std::basic_stringbuf<CharT> sstr1;
325 std::basic_stringbuf<CharT> sstr2;
326 std::basic_string<CharT> expected(42, CharT('*')); // a long string
327
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 string
332
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 emit
352
353 std::basic_stringbuf<CharT> sstr1;
354 std::basic_stringbuf<CharT> sstr2;
355 std::basic_string<CharT> expected(42, CharT('*')); // a long string
356
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 string
361
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
382 template <class CharT>
test()383 static 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
main(int,char **)391 int main(int, char**) {
392 test<char>();
393
394 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
395 test<wchar_t>();
396 #endif
397
398 return 0;
399 }
400