Skip to content

Commit

Permalink
<filesystem>: Preallocate memory in path::operator/ (#4136)
Browse files Browse the repository at this point in the history
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
Co-authored-by: Casey Carter <cacarter@microsoft.com>
  • Loading branch information
3 people authored Jan 30, 2024
1 parent 46402aa commit 9c10c1a
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 5 deletions.
29 changes: 29 additions & 0 deletions stl/inc/filesystem
Original file line number Diff line number Diff line change
Expand Up @@ -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<size_t>(_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;
Expand Down
22 changes: 17 additions & 5 deletions tests/std/tests/P0218R1_filesystem/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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},
Expand All @@ -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() {
Expand Down

0 comments on commit 9c10c1a

Please sign in to comment.