perf: tr_sys_path_basename() returns a std::string_view (#4176)

This commit is contained in:
Charles Kerr 2022-11-15 08:29:00 -06:00 committed by GitHub
parent 134363d83d
commit d0639b5f0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 61 deletions

View File

@ -240,17 +240,32 @@ std::string tr_sys_path_resolve(std::string_view path, tr_error** error)
return {};
}
std::string tr_sys_path_basename(std::string_view path, tr_error** error)
std::string_view tr_sys_path_basename(std::string_view path, tr_error** /*error*/)
{
auto tmp = tr_pathbuf{ path };
if (char const* const ret = basename(std::data(tmp)); ret != nullptr)
// As per the basename() manpage:
// If path [is] an empty string, then basename() return[s] the string "."
if (std::empty(path))
{
return ret;
return "."sv;
}
set_system_error(error, errno);
return {};
// Remove all trailing slashes.
// If nothing is left, return "/"
if (auto pos = path.find_last_not_of('/'); pos != std::string_view::npos)
{
path = path.substr(0, pos + 1);
}
else // all slashes
{
return "/"sv;
}
if (auto pos = path.find_last_of('/'); pos != std::string_view::npos)
{
path.remove_prefix(pos + 1);
}
return std::empty(path) ? "/"sv : path;
}
// This function is adapted from Node.js's path.posix.dirname() function,

View File

@ -109,9 +109,11 @@ static constexpr auto stat_to_sys_path_info(DWORD attributes, DWORD size_low, DW
return info;
}
static auto constexpr Slashes = "\\/"sv;
static constexpr bool is_slash(char c)
{
return c == '\\' || c == '/';
return tr_strvContains(Slashes, c);
}
static constexpr bool is_unc_path(std::string_view path)
@ -547,11 +549,11 @@ std::string tr_sys_path_resolve(std::string_view path, tr_error** error)
return {};
}
std::string tr_sys_path_basename(std::string_view path, tr_error** error)
std::string_view tr_sys_path_basename(std::string_view path, tr_error** error)
{
if (std::empty(path))
{
return ".";
return "."sv;
}
if (!is_valid_path(path))
@ -560,32 +562,23 @@ std::string tr_sys_path_basename(std::string_view path, tr_error** error)
return {};
}
char const* const begin = std::data(path);
char const* end = begin + std::size(path);
while (end > begin && is_slash(*(end - 1)))
// Remove all trailing slashes.
// If nothing is left, return "/"
if (auto const pos = path.find_last_not_of(Slashes); pos != std::string_view::npos)
{
--end;
path = path.substr(0, pos + 1);
}
else // all slashes
{
return "/"sv;
}
if (end == begin)
if (auto pos = path.find_last_of("\\/:"); pos != std::string_view::npos)
{
return "/";
path.remove_prefix(pos + 1);
}
char const* name = end;
while (name > begin && *(name - 1) != ':' && !is_slash(*(name - 1)))
{
--name;
}
if (name == end)
{
return "/";
}
return { name, size_t(end - name) };
return !std::empty(path) ? path : "/"sv;
}
[[nodiscard]] static bool isWindowsDeviceRoot(char ch) noexcept

View File

@ -237,7 +237,7 @@ std::string tr_sys_path_resolve(std::string_view path, struct tr_error** error =
* @return base name (last path component; parent path removed) on success,
* or empty string otherwise (with `error` set accordingly).
*/
std::string tr_sys_path_basename(std::string_view path, struct tr_error** error = nullptr);
std::string_view tr_sys_path_basename(std::string_view path, struct tr_error** error = nullptr);
/**
* @brief Portability wrapper for `dirname()`.

View File

@ -140,26 +140,30 @@ protected:
struct XnameTestData
{
char const* input;
char const* output;
std::string_view input;
std::string_view output;
};
static void testPathXname(XnameTestData const* data, size_t data_size, std::string (*func)(std::string_view, tr_error**))
static void testPathXname(
XnameTestData const* data,
size_t data_size,
std::string_view (*func)(std::string_view, tr_error**))
{
for (size_t i = 0; i < data_size; ++i)
{
tr_error* err = nullptr;
auto const name = func(data[i].input, &err);
auto const& [input, output] = data[i];
auto const name = func(input, &err);
if (data[i].output != nullptr)
if (!std::empty(data[i].output))
{
EXPECT_NE(""sv, name);
EXPECT_EQ(nullptr, err) << *err;
EXPECT_EQ(data[i].output, name);
EXPECT_EQ(output, name) << " in [" << input << ']';
}
else
{
EXPECT_EQ(""sv, name);
EXPECT_EQ(""sv, name) << " in [" << input << ']';
EXPECT_NE(nullptr, err);
tr_error_clear(&err);
}
@ -720,30 +724,30 @@ TEST_F(FileTest, pathBasename)
#ifdef _WIN32
{ "\\", "/" },
/* Invalid paths */
{ "\\\\\\", nullptr },
{ "123:", nullptr },
{ "\\\\\\", "" },
{ "123:", "" },
/* Reserved characters */
{ "<", nullptr },
{ ">", nullptr },
{ ":", nullptr },
{ "\"", nullptr },
{ "|", nullptr },
{ "?", nullptr },
{ "*", nullptr },
{ "a\\<", nullptr },
{ "a\\>", nullptr },
{ "a\\:", nullptr },
{ "a\\\"", nullptr },
{ "a\\|", nullptr },
{ "a\\?", nullptr },
{ "a\\*", nullptr },
{ "c:\\a\\b<c\\d", nullptr },
{ "c:\\a\\b>c\\d", nullptr },
{ "c:\\a\\b:c\\d", nullptr },
{ "c:\\a\\b\"c\\d", nullptr },
{ "c:\\a\\b|c\\d", nullptr },
{ "c:\\a\\b?c\\d", nullptr },
{ "c:\\a\\b*c\\d", nullptr },
{ "<", "" },
{ ">", "" },
{ ":", "" },
{ "\"", "" },
{ "|", "" },
{ "?", "" },
{ "*", "" },
{ "a\\<", "" },
{ "a\\>", "" },
{ "a\\:", "" },
{ "a\\\"", "" },
{ "a\\|", "" },
{ "a\\?", "" },
{ "a\\*", "" },
{ "c:\\a\\b<c\\d", "" },
{ "c:\\a\\b>c\\d", "" },
{ "c:\\a\\b:c\\d", "" },
{ "c:\\a\\b\"c\\d", "" },
{ "c:\\a\\b|c\\d", "" },
{ "c:\\a\\b?c\\d", "" },
{ "c:\\a\\b*c\\d", "" },
#else
{ "////", "/" },
#endif