reduce the amount of hacky path/string handling code

This commit is contained in:
arvidn 2019-03-20 09:36:37 +01:00 committed by Arvid Norberg
parent b6896cf337
commit 2990532d10
5 changed files with 118 additions and 98 deletions

View File

@ -136,9 +136,10 @@ namespace libtorrent {
TORRENT_EXTRA_EXPORT void hard_link(std::string const& file
, std::string const& link, error_code& ec);
TORRENT_EXTRA_EXPORT std::string split_path(std::string const& f
, bool only_first_part = false);
TORRENT_EXTRA_EXPORT char const* next_path_element(char const* p);
// split out a path segment from the left side or right side
TORRENT_EXTRA_EXPORT std::pair<string_view, string_view> rsplit_path(string_view p);
TORRENT_EXTRA_EXPORT std::pair<string_view, string_view> lsplit_path(string_view p);
TORRENT_EXTRA_EXPORT std::string extension(std::string const& f);
TORRENT_EXTRA_EXPORT std::string remove_extension(std::string const& f);
TORRENT_EXTRA_EXPORT bool is_root_path(std::string const& f);
@ -147,7 +148,6 @@ namespace libtorrent {
// internal used by create_torrent.hpp
TORRENT_EXTRA_EXPORT std::string parent_path(std::string const& f);
TORRENT_EXTRA_EXPORT bool has_parent_path(std::string const& f);
TORRENT_EXTRA_EXPORT char const* filename_cstr(char const* f);
// internal used by create_torrent.hpp
TORRENT_EXTRA_EXPORT std::string filename(std::string const& f);

View File

@ -597,9 +597,9 @@ namespace {
{
entry& sympath_e = info["symlink path"];
std::string const split = split_path(m_files.symlink(first));
for (char const* e = split.c_str(); e != nullptr; e = next_path_element(e))
sympath_e.list().emplace_back(e);
for (auto elems = lsplit_path(m_files.symlink(first)); !elems.first.empty();
elems = lsplit_path(elems.second))
sympath_e.list().emplace_back(elems.first);
}
if (!m_filehashes.empty())
{
@ -623,12 +623,13 @@ namespace {
{
entry& path_e = file_e["path"];
std::string const split = split_path(m_files.file_path(i));
TORRENT_ASSERT(split.c_str() == m_files.name());
for (char const* e = next_path_element(split.c_str());
e != nullptr; e = next_path_element(e))
path_e.list().emplace_back(e);
std::string const p = m_files.file_path(i);
// deliberately skip the first path element, since that's the
// "name" of the torrent already
string_view path = lsplit_path(p).second;
for (auto elems = lsplit_path(path); !elems.first.empty(); elems = lsplit_path(elems.second))
path_e.list().emplace_back(elems.first);
}
file_flags_t const flags = m_files.file_flags(i);
@ -646,9 +647,9 @@ namespace {
{
entry& sympath_e = file_e["symlink path"];
std::string const split = split_path(m_files.symlink(i));
for (char const* e = split.c_str(); e != nullptr; e = next_path_element(e))
sympath_e.list().emplace_back(e);
for (auto elems = lsplit_path(m_files.symlink(i)); !elems.first.empty();
elems = lsplit_path(elems.second))
sympath_e.list().emplace_back(elems.first);
}
if (!m_filehashes.empty() && m_filehashes[i] != sha1_hash())
{

View File

@ -114,7 +114,8 @@ namespace {
}
// path is not supposed to include the name of the torrent itself.
// path is supposed to include the name of the torrent itself.
// or an absolute path, to move a file outside of the download directory
void file_storage::update_path_index(internal_file_entry& e
, std::string const& path, bool const set_name)
{
@ -128,40 +129,31 @@ namespace {
TORRENT_ASSERT(path[0] != '/');
// sorry about this messy string handling, but I did
// profile it, and it was expensive
char const* leaf = filename_cstr(path.c_str());
// split the string into the leaf filename
// and the branch path
string_view leaf;
string_view branch_path;
if (leaf > path.c_str())
{
// split the string into the leaf filename
// and the branch path
branch_path = path;
branch_path = branch_path.substr(0
, static_cast<std::size_t>(leaf - path.c_str()));
std::tie(branch_path, leaf) = rsplit_path(path);
// trim trailing slashes
while (!branch_path.empty() && branch_path.back() == TORRENT_SEPARATOR)
{
branch_path.remove_suffix(1);
}
}
if (branch_path.empty())
{
if (set_name) e.set_name(leaf);
if (set_name) e.set_name(leaf.data());
e.path_index = -1;
return;
}
if (branch_path.size() >= m_name.size()
&& branch_path.substr(0, m_name.size()) == m_name
&& branch_path[m_name.size()] == TORRENT_SEPARATOR)
// if the path *does* contain the name of the torrent (as we expect)
// strip it before adding it to m_paths
if (lsplit_path(branch_path).first == m_name)
{
branch_path.remove_prefix(m_name.size());
while (!branch_path.empty() && branch_path.front() == TORRENT_SEPARATOR)
{
branch_path = lsplit_path(branch_path).second;
// strip duplicate separators
while (!branch_path.empty() && (branch_path.front() == TORRENT_SEPARATOR
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|| branch_path.front() == '/'
#endif
))
branch_path.remove_prefix(1);
}
e.no_root_dir = false;
}
else
@ -170,7 +162,7 @@ namespace {
}
e.path_index = get_or_add_path(branch_path);
if (set_name) e.set_name(leaf);
if (set_name) e.set_name(leaf.data());
}
int file_storage::get_or_add_path(string_view const path)
@ -607,7 +599,7 @@ namespace {
else
{
if (m_files.empty())
m_name = split_path(path, true);
m_name = lsplit_path(path).first.to_string();
}
// this is poor-man's emplace_back()
@ -616,12 +608,12 @@ namespace {
// the last argument specified whether the function should also set
// the filename. If it does, it will copy the leaf filename from path.
// if filename is nullptr, we should copy it. If it isn't, we're borrowing
// if filename is empty, we should copy it. If it isn't, we're borrowing
// it and we can save the copy by setting it after this call to
// update_path_index().
update_path_index(e, path, filename.empty());
// filename is allowed to be nullptr, in which case we just use path
// filename is allowed to be empty, in which case we just use path
if (!filename.empty())
e.set_name(filename.data(), true, int(filename.size()));

View File

@ -468,42 +468,6 @@ namespace {
rename(inf, newf, ec);
}
std::string split_path(std::string const& f, bool only_first_part)
{
if (f.empty()) return f;
std::string ret;
char const* start = f.c_str();
char const* p = start;
while (*start != 0)
{
while (*p != '/'
&& *p != '\0'
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
&& *p != '\\'
#endif
) ++p;
if (p - start > 0)
{
ret.append(start, aux::numeric_cast<std::size_t>(p - start));
if (only_first_part) return ret;
ret.append(1, '\0');
}
if (*p != 0) ++p;
start = p;
}
if (only_first_part) return ret;
ret.append(1, '\0');
return ret;
}
char const* next_path_element(char const* p)
{
p += strlen(p) + 1;
if (*p == 0) return nullptr;
return p;
}
std::string extension(std::string const& f)
{
for (int i = int(f.size()) - 1; i >= 0; --i)
@ -625,19 +589,6 @@ namespace {
return std::string(f.c_str(), std::size_t(len));
}
char const* filename_cstr(char const* f)
{
if (f == nullptr) return f;
char const* sep = std::strrchr(f, '/');
#ifdef TORRENT_WINDOWS
char const* altsep = std::strrchr(f, '\\');
if (sep == 0 || altsep > sep) sep = altsep;
#endif
if (sep == nullptr) return f;
return sep+1;
}
std::string filename(std::string const& f)
{
if (f.empty()) return "";
@ -919,6 +870,39 @@ namespace {
remove(f, ec);
}
std::pair<string_view, string_view> rsplit_path(string_view p)
{
if (p.empty()) return {{}, {}};
if (p.back() == TORRENT_SEPARATOR_CHAR) p.remove_suffix(1);
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
else if (p.back() == '/') p.remove_suffix(1);
#endif
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
auto const sep = p.find_last_of("/\\");
#else
auto const sep = p.find_last_of(TORRENT_SEPARATOR_CHAR);
#endif
if (sep == string_view::npos) return {{}, p};
return { p.substr(0, sep), p.substr(sep + 1) };
}
std::pair<string_view, string_view> lsplit_path(string_view p)
{
if (p.empty()) return {{}, {}};
// for absolute paths, skip the initial "/"
if (p.front() == TORRENT_SEPARATOR_CHAR) p.remove_prefix(1);
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
else if (p.front() == '/') p.remove_prefix(1);
#endif
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
auto const sep = p.find_first_of("/\\");
#else
auto const sep = p.find_first_of(TORRENT_SEPARATOR_CHAR);
#endif
if (sep == string_view::npos) return {p, {}};
return { p.substr(0, sep), p.substr(sep + 1) };
}
std::string complete(string_view f)
{
if (is_complete(f)) return f.to_string();

View File

@ -188,11 +188,6 @@ TORRENT_TEST(paths)
TEST_EQUAL(remove_extension("blah.foo.bar"), "blah.foo");
TEST_EQUAL(remove_extension("blah.foo."), "blah.foo");
TEST_EQUAL(filename("blah"), "blah");
TEST_EQUAL(filename("/blah/foo/bar"), "bar");
TEST_EQUAL(filename("/blah/foo/bar/"), "bar");
TEST_EQUAL(filename("blah/"), "blah");
#ifdef TORRENT_WINDOWS
TEST_EQUAL(is_root_path("c:\\blah"), false);
TEST_EQUAL(is_root_path("c:\\"), true);
@ -274,6 +269,54 @@ TORRENT_TEST(paths)
TEST_EQUAL(complete("."), current_working_directory());
}
TORRENT_TEST(filename)
{
#ifdef TORRENT_WINDOWS
TEST_EQUAL(filename("blah"), "blah");
TEST_EQUAL(filename("\\blah\\foo\\bar"), "bar");
TEST_EQUAL(filename("\\blah\\foo\\bar\\"), "bar");
TEST_EQUAL(filename("blah\\"), "blah");
#endif
TEST_EQUAL(filename("blah"), "blah");
TEST_EQUAL(filename("/blah/foo/bar"), "bar");
TEST_EQUAL(filename("/blah/foo/bar/"), "bar");
TEST_EQUAL(filename("blah/"), "blah");
}
TORRENT_TEST(split_path)
{
using r = std::pair<string_view, string_view>;
#ifdef TORRENT_WINDOWS
TEST_CHECK(lsplit_path("\\b\\c\\d") == r("b", "c\\d"));
TEST_CHECK(lsplit_path("a\\b\\c\\d") == r("a", "b\\c\\d"));
TEST_CHECK(lsplit_path("a") == r("a", ""));
TEST_CHECK(lsplit_path("") == r("", ""));
TEST_CHECK(lsplit_path("a\\b/c\\d") == r("a", "b/c\\d"));
TEST_CHECK(lsplit_path("a/b\\c\\d") == r("a", "b\\c\\d"));
TEST_CHECK(rsplit_path("a\\b\\c\\d\\") == r("a\\b\\c", "d"));
TEST_CHECK(rsplit_path("\\a\\b\\c\\d") == r("\\a\\b\\c", "d"));
TEST_CHECK(rsplit_path("\\a") == r("", "a"));
TEST_CHECK(rsplit_path("a") == r("", "a"));
TEST_CHECK(rsplit_path("") == r("", ""));
TEST_CHECK(rsplit_path("a\\b/c\\d\\") == r("a\\b/c", "d"));
TEST_CHECK(rsplit_path("a\\b\\c/d\\") == r("a\\b\\c", "d"));
#endif
TEST_CHECK(lsplit_path("/b/c/d") == r("b", "c/d"));
TEST_CHECK(lsplit_path("a/b/c/d") == r("a", "b/c/d"));
TEST_CHECK(lsplit_path("a") == r("a", ""));
TEST_CHECK(lsplit_path("") == r("", ""));
TEST_CHECK(rsplit_path("a/b/c/d/") == r("a/b/c", "d"));
TEST_CHECK(rsplit_path("/a/b/c/d") == r("/a/b/c", "d"));
TEST_CHECK(rsplit_path("/a") == r("", "a"));
TEST_CHECK(rsplit_path("a") == r("", "a"));
TEST_CHECK(rsplit_path("") == r("", ""));
}
// file class
TORRENT_TEST(file)
{