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