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
10 
11 // <filesystem>
12 
13 // class path
14 
15 // path& operator/=(path const&)
16 // template <class Source>
17 //      path& operator/=(Source const&);
18 // template <class Source>
19 //      path& append(Source const&);
20 // template <class InputIterator>
21 //      path& append(InputIterator first, InputIterator last);
22 
23 
24 #include "filesystem_include.h"
25 #include <type_traits>
26 #include <string_view>
27 #include <cassert>
28 
29 #include "test_macros.h"
30 #include "test_iterators.h"
31 #include "count_new.h"
32 #include "filesystem_test_helper.h"
33 
34 
35 struct AppendOperatorTestcase {
36   MultiStringType lhs;
37   MultiStringType rhs;
38   MultiStringType expect;
39 };
40 
41 #define S(Str) MKSTR(Str)
42 const AppendOperatorTestcase Cases[] =
43     {
44         {S(""),     S(""),      S("")}
45       , {S("p1"),   S("p2"),    S("p1/p2")}
46       , {S("p1/"),  S("p2"),    S("p1/p2")}
47       , {S("p1"),   S("/p2"),   S("/p2")}
48       , {S("p1/"),  S("/p2"),   S("/p2")}
49       , {S("p1"),   S("\\p2"),  S("p1/\\p2")}
50       , {S("p1\\"), S("p2"),  S("p1\\/p2")}
51       , {S("p1\\"), S("\\p2"),  S("p1\\/\\p2")}
52       , {S(""),     S("p2"),    S("p2")}
53       , {S("/p1"),  S("p2"),    S("/p1/p2")}
54       , {S("/p1"),  S("/p2"),    S("/p2")}
55       , {S("/p1/p3"),  S("p2"),    S("/p1/p3/p2")}
56       , {S("/p1/p3/"),  S("p2"),    S("/p1/p3/p2")}
57       , {S("/p1/"),  S("p2"),    S("/p1/p2")}
58       , {S("/p1/p3/"),  S("/p2/p4"),    S("/p2/p4")}
59       , {S("/"),    S(""),      S("/")}
60       , {S("/p1"), S("/p2/"), S("/p2/")}
61       , {S("p1"),   S(""),      S("p1/")}
62       , {S("p1/"),  S(""),      S("p1/")}
63     };
64 
65 
66 const AppendOperatorTestcase LongLHSCases[] =
67     {
68         {S("p1"),   S("p2"),    S("p1/p2")}
69       , {S("p1/"),  S("p2"),    S("p1/p2")}
70       , {S("p1"),   S("/p2"),   S("/p2")}
71       , {S("/p1"),  S("p2"),    S("/p1/p2")}
72     };
73 #undef S
74 
75 
76 // The append operator may need to allocate a temporary buffer before a code_cvt
77 // conversion. Test if this allocation occurs by:
78 //   1. Create a path, `LHS`, and reserve enough space to append `RHS`.
79 //      This prevents `LHS` from allocating during the actual appending.
80 //   2. Create a `Source` object `RHS`, which represents a "large" string.
81 //      (The string must not trigger the SSO)
82 //   3. Append `RHS` to `LHS` and check for the expected allocation behavior.
83 template <class CharT>
84 void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
85 {
86   using namespace fs;
87   using Ptr = CharT const*;
88   using Str = std::basic_string<CharT>;
89   using StrView = std::basic_string_view<CharT>;
90   using InputIter = input_iterator<Ptr>;
91 
92   const Ptr L = TC.lhs;
93   Str RShort = (Ptr)TC.rhs;
94   Str EShort = (Ptr)TC.expect;
95   assert(RShort.size() >= 2);
96   CharT c = RShort.back();
97   RShort.append(100, c);
98   EShort.append(100, c);
99   const Ptr R = RShort.data();
100   const Str& E = EShort;
101   std::size_t ReserveSize = E.size() + 3;
102   // basic_string
103   {
104     path LHS(L); PathReserve(LHS, ReserveSize);
105     Str  RHS(R);
106     {
107       DisableAllocationGuard g;
108       LHS /= RHS;
109     }
110     assert(PathEq(LHS, E));
111   }
112   // basic_string_view
113   {
114     path LHS(L); PathReserve(LHS, ReserveSize);
115     StrView  RHS(R);
116     {
117       DisableAllocationGuard g;
118       LHS /= RHS;
119     }
120     assert(PathEq(LHS, E));
121   }
122   // CharT*
123   {
124     path LHS(L); PathReserve(LHS, ReserveSize);
125     Ptr RHS(R);
126     {
127       DisableAllocationGuard g;
128       LHS /= RHS;
129     }
130     assert(PathEq(LHS, E));
131   }
132   {
133     path LHS(L); PathReserve(LHS, ReserveSize);
134     Ptr RHS(R);
135     {
136       DisableAllocationGuard g;
137       LHS.append(RHS, StrEnd(RHS));
138     }
139     assert(PathEq(LHS, E));
140   }
141   // input iterator - For non-native char types, appends needs to copy the
142   // iterator range into a contiguous block of memory before it can perform the
143   // code_cvt conversions.
144   // For "char" no allocations will be performed because no conversion is
145   // required.
146   bool DisableAllocations = std::is_same<CharT, char>::value;
147   {
148     path LHS(L); PathReserve(LHS, ReserveSize);
149     InputIter RHS(R);
150     {
151       RequireAllocationGuard  g; // requires 1 or more allocations occur by default
152       if (DisableAllocations) g.requireExactly(0);
153       LHS /= RHS;
154     }
155     assert(PathEq(LHS, E));
156   }
157   {
158     path LHS(L); PathReserve(LHS, ReserveSize);
159     InputIter RHS(R);
160     InputIter REnd(StrEnd(R));
161     {
162       RequireAllocationGuard g;
163       if (DisableAllocations) g.requireExactly(0);
164       LHS.append(RHS, REnd);
165     }
166     assert(PathEq(LHS, E));
167   }
168 }
169 
170 template <class CharT>
171 void doAppendSourceTest(AppendOperatorTestcase const& TC)
172 {
173   using namespace fs;
174   using Ptr = CharT const*;
175   using Str = std::basic_string<CharT>;
176   using StrView = std::basic_string_view<CharT>;
177   using InputIter = input_iterator<Ptr>;
178   const Ptr L = TC.lhs;
179   const Ptr R = TC.rhs;
180   const Ptr E = TC.expect;
181   // basic_string
182   {
183     path Result(L);
184     Str RHS(R);
185     path& Ref = (Result /= RHS);
186     assert(Result == E);
187     assert(&Ref == &Result);
188   }
189   {
190     path LHS(L);
191     Str RHS(R);
192     path& Ref = LHS.append(RHS);
193     assert(PathEq(LHS, E));
194     assert(&Ref == &LHS);
195   }
196   // basic_string_view
197   {
198     path LHS(L);
199     StrView RHS(R);
200     path& Ref = (LHS /= RHS);
201     assert(PathEq(LHS, E));
202     assert(&Ref == &LHS);
203   }
204   {
205     path LHS(L);
206     StrView RHS(R);
207     path& Ref = LHS.append(RHS);
208     assert(PathEq(LHS, E));
209     assert(&Ref == &LHS);
210   }
211   // Char*
212   {
213     path LHS(L);
214     Str RHS(R);
215     path& Ref = (LHS /= RHS);
216     assert(PathEq(LHS, E));
217     assert(&Ref == &LHS);
218   }
219   {
220     path LHS(L);
221     Ptr RHS(R);
222     path& Ref = LHS.append(RHS);
223     assert(PathEq(LHS, E));
224     assert(&Ref == &LHS);
225   }
226   {
227     path LHS(L);
228     Ptr RHS(R);
229     path& Ref = LHS.append(RHS, StrEnd(RHS));
230     assert(PathEq(LHS, E));
231     assert(&Ref == &LHS);
232   }
233   // iterators
234   {
235     path LHS(L);
236     InputIter RHS(R);
237     path& Ref = (LHS /= RHS);
238     assert(PathEq(LHS, E));
239     assert(&Ref == &LHS);
240   }
241   {
242     path LHS(L); InputIter RHS(R);
243     path& Ref = LHS.append(RHS);
244     assert(PathEq(LHS, E));
245     assert(&Ref == &LHS);
246   }
247   {
248     path LHS(L);
249     InputIter RHS(R);
250     InputIter REnd(StrEnd(R));
251     path& Ref = LHS.append(RHS, REnd);
252     assert(PathEq(LHS, E));
253     assert(&Ref == &LHS);
254   }
255 }
256 
257 
258 
259 template <class It, class = decltype(fs::path{}.append(std::declval<It>()))>
260 constexpr bool has_append(int) { return true; }
261 template <class It>
262 constexpr bool has_append(long) { return false; }
263 
264 template <class It, class = decltype(fs::path{}.operator/=(std::declval<It>()))>
265 constexpr bool has_append_op(int) { return true; }
266 template <class It>
267 constexpr bool has_append_op(long) { return false; }
268 
269 template <class It>
270 constexpr bool has_append() {
271   static_assert(has_append<It>(0) == has_append_op<It>(0), "must be same");
272   return has_append<It>(0) && has_append_op<It>(0);
273 }
274 
275 void test_sfinae()
276 {
277   using namespace fs;
278   {
279     using It = const char* const;
280     static_assert(has_append<It>(), "");
281   }
282   {
283     using It = input_iterator<const char*>;
284     static_assert(has_append<It>(), "");
285   }
286   {
287     struct Traits {
288       using iterator_category = std::input_iterator_tag;
289       using value_type = const char;
290       using pointer = const char*;
291       using reference = const char&;
292       using difference_type = std::ptrdiff_t;
293     };
294     using It = input_iterator<const char*, Traits>;
295     static_assert(has_append<It>(), "");
296   }
297   {
298     using It = output_iterator<const char*>;
299     static_assert(!has_append<It>(), "");
300 
301   }
302   {
303     static_assert(!has_append<int*>(), "");
304   }
305   {
306     static_assert(!has_append<char>(), "");
307     static_assert(!has_append<const char>(), "");
308   }
309 }
310 
311 int main(int, char**)
312 {
313   using namespace fs;
314   for (auto const & TC : Cases) {
315     {
316       const char* LHS_In = TC.lhs;
317       const char* RHS_In = TC.rhs;
318       path LHS(LHS_In);
319       path RHS(RHS_In);
320       path& Res = (LHS /= RHS);
321       assert(PathEq(Res, (const char*)TC.expect));
322       assert(&Res == &LHS);
323     }
324     doAppendSourceTest<char>    (TC);
325     doAppendSourceTest<wchar_t> (TC);
326     doAppendSourceTest<char16_t>(TC);
327     doAppendSourceTest<char32_t>(TC);
328   }
329   for (auto const & TC : LongLHSCases) {
330     LIBCPP_ONLY(doAppendSourceAllocTest<char>(TC));
331     LIBCPP_ONLY(doAppendSourceAllocTest<wchar_t>(TC));
332   }
333   test_sfinae();
334 
335   return 0;
336 }
337