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