diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 4b2ad4ce9b8..a08e435d4cc 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -1377,6 +1377,35 @@ namespace filesystem { #endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD_FRIEND path operator/(const path& _Left, const path& _Right) { // append a pair of paths together + const auto _Right_size = _Right._Text.size(); + const auto _Right_first = _Right._Text.data(); + const auto _Right_last = _Right_first + _Right_size; + + // Handle the most common case: !has_root_name(_Right) && !has_root_directory(_Right) + if (_Right_size != 0 && !_Has_drive_letter_prefix(_Right_first, _Right_last) && !_Is_slash(*_Right_first)) { + const auto _Left_size = _Left._Text.size(); + const auto _Left_first = _Left._Text.data(); + const auto _Left_last = _Left_first + _Left_size; + + // Appending a slash to "X:" would make it an absolute path + const bool _Left_is_just_drive = _Left_size == 2 && _Is_drive_prefix(_Left_first); + const bool _Is_slash_needed = _Left_size != 0 && !_Left_is_just_drive && !_Is_slash(_Left_last[-1]); + + const auto _Total_size = _Left_size + static_cast(_Is_slash_needed) + _Right_size; + + path _Tmp; + _Tmp._Text._Resize_and_overwrite(_Total_size, [=](wchar_t* _Ptr, const size_t _Size) { + _CSTD memcpy(_Ptr, _Left_first, _Left_size * sizeof(wchar_t)); + _Ptr += _Left_size; + if (_Is_slash_needed) { + *_Ptr++ = preferred_separator; + } + _CSTD memcpy(_Ptr, _Right_first, _Right_size * sizeof(wchar_t)); + return _Size; + }); + return _Tmp; + } + path _Tmp = _Left; _Tmp /= _Right; return _Tmp; diff --git a/tests/std/tests/P0218R1_filesystem/test.cpp b/tests/std/tests/P0218R1_filesystem/test.cpp index d3ea57bbbc8..e2d66ba01b2 100644 --- a/tests/std/tests/P0218R1_filesystem/test.cpp +++ b/tests/std/tests/P0218R1_filesystem/test.cpp @@ -544,6 +544,7 @@ struct slash_test_case { }; constexpr slash_test_case slashTestCases[] = { + {L""sv, L""sv, L""sv}, {L"relative"sv, L"other"sv, LR"(relative\other)"sv}, {L"//server"sv, L"share"sv, LR"(//server\share)"sv}, {L"//server/"sv, L"share"sv, LR"(//server/share)"sv}, @@ -555,6 +556,7 @@ constexpr slash_test_case slashTestCases[] = { {L""sv, L"cat"sv, L"cat"sv}, {L"./"sv, L"cat"sv, L"./cat"sv}, // original test case catching a bug in the above {L"c:"sv, L""sv, L"c:"sv}, + {L"c:"sv, L"dog"sv, L"c:dog"sv}, {L"c:cat"sv, L"/dog"sv, L"c:/dog"sv}, {L"c:/cat"sv, L"/dog"sv, L"c:/dog"sv}, {L"c:cat"sv, L"c:dog"sv, LR"(c:cat\dog)"sv}, @@ -568,13 +570,23 @@ constexpr slash_test_case slashTestCases[] = { bool run_slash_test_case(const slash_test_case& testCase) { path p(testCase.a); p /= testCase.b; - if (p.native() == testCase.expected) { - return true; + + if (p.native() != testCase.expected) { + wcerr << L"With operator/=, expected " << testCase.a << L" / " << testCase.b << L" to be " << testCase.expected + << L" but it was " << p.native() << L"\n"; + return false; } - wcerr << L"Expected " << testCase.a << L" / " << testCase.b << L" to be " << testCase.expected << L" but it was " - << p.native() << L"\n"; - return false; + // Also test operator/, which was optimized by GH-4136. + p = path{testCase.a} / path{testCase.b}; + + if (p.native() != testCase.expected) { + wcerr << L"With operator/, expected " << testCase.a << L" / " << testCase.b << L" to be " << testCase.expected + << L" but it was " << p.native() << L"\n"; + return false; + } + + return true; } void test_iterators() {