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