Merge pull request #12290 from liamwhite/deferred-path-split

Improve path splitting speed
This commit is contained in:
liamwhite 2023-12-16 11:47:29 -05:00 committed by GitHub
commit 360418f1a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 38 additions and 28 deletions

View file

@ -354,18 +354,36 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
return path; return path;
} }
std::vector<std::string> SplitPathComponents(std::string_view filename) { template <typename F>
std::string copy(filename); static void ForEachPathComponent(std::string_view filename, F&& cb) {
std::replace(copy.begin(), copy.end(), '\\', '/'); const char* component_begin = filename.data();
std::vector<std::string> out; const char* const end = component_begin + filename.size();
for (const char* it = component_begin; it != end; ++it) {
std::stringstream stream(copy); const char c = *it;
std::string item; if (c == '\\' || c == '/') {
while (std::getline(stream, item, '/')) { if (component_begin != it) {
out.push_back(std::move(item)); cb(std::string_view{component_begin, it});
} }
component_begin = it + 1;
}
}
if (component_begin != end) {
cb(std::string_view{component_begin, end});
}
}
return out; std::vector<std::string_view> SplitPathComponents(std::string_view filename) {
std::vector<std::string_view> components;
ForEachPathComponent(filename, [&](auto component) { components.emplace_back(component); });
return components;
}
std::vector<std::string> SplitPathComponentsCopy(std::string_view filename) {
std::vector<std::string> components;
ForEachPathComponent(filename, [&](auto component) { components.emplace_back(component); });
return components;
} }
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {

View file

@ -289,7 +289,11 @@ enum class DirectorySeparator {
// Splits the path on '/' or '\' and put the components into a vector // Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } // i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename); [[nodiscard]] std::vector<std::string_view> SplitPathComponents(std::string_view filename);
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' // Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows // depending if directory_separator is BackwardSlash or PlatformDefault and running on windows

View file

@ -201,8 +201,6 @@ std::string VfsFile::GetFullPath() const {
VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
auto vec = Common::FS::SplitPathComponents(path); auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) { if (vec.empty()) {
return nullptr; return nullptr;
} }
@ -237,8 +235,6 @@ VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {
VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const { VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const {
auto vec = Common::FS::SplitPathComponents(path); auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) { if (vec.empty()) {
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
// because of const-ness // because of const-ness
@ -303,8 +299,6 @@ std::size_t VfsDirectory::GetSize() const {
VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) { VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) {
auto vec = Common::FS::SplitPathComponents(path); auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) { if (vec.empty()) {
return nullptr; return nullptr;
} }
@ -334,8 +328,6 @@ VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) {
VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) { VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) {
auto vec = Common::FS::SplitPathComponents(path); auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) { if (vec.empty()) {
return nullptr; return nullptr;
} }

View file

@ -268,7 +268,7 @@ void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference)
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
const std::string& path_, Mode perms_, std::optional<u64> size_) const std::string& path_, Mode perms_, std::optional<u64> size_)
: base(base_), reference(std::move(reference_)), path(path_), : base(base_), reference(std::move(reference_)), path(path_),
parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)), parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)),
size(size_), perms(perms_) {} size(size_), perms(perms_) {}
RealVfsFile::~RealVfsFile() { RealVfsFile::~RealVfsFile() {
@ -276,7 +276,7 @@ RealVfsFile::~RealVfsFile() {
} }
std::string RealVfsFile::GetName() const { std::string RealVfsFile::GetName() const {
return path_components.back(); return path_components.empty() ? "" : std::string(path_components.back());
} }
std::size_t RealVfsFile::GetSize() const { std::size_t RealVfsFile::GetSize() const {
@ -375,7 +375,7 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
: base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
path_components(FS::SplitPathComponents(path)), perms(perms_) { path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) {
if (!FS::Exists(path) && True(perms & Mode::Write)) { if (!FS::Exists(path) && True(perms & Mode::Write)) {
void(FS::CreateDirs(path)); void(FS::CreateDirs(path));
} }
@ -464,7 +464,7 @@ bool RealVfsDirectory::IsReadable() const {
} }
std::string RealVfsDirectory::GetName() const { std::string RealVfsDirectory::GetName() const {
return path_components.back(); return path_components.empty() ? "" : std::string(path_components.back());
} }
VirtualDir RealVfsDirectory::GetParentDirectory() const { VirtualDir RealVfsDirectory::GetParentDirectory() const {

View file

@ -104,11 +104,7 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
const auto components = Common::FS::SplitPathComponents(path); const auto components = Common::FS::SplitPathComponents(path);
std::string relative_path; std::string relative_path;
for (const auto& component : components) { for (const auto& component : components) {
// Skip empty path components relative_path = Common::FS::SanitizePath(fmt::format("{}/{}", relative_path, component));
if (component.empty()) {
continue;
}
relative_path = Common::FS::SanitizePath(relative_path + '/' + component);
auto new_dir = backing->CreateSubdirectory(relative_path); auto new_dir = backing->CreateSubdirectory(relative_path);
if (new_dir == nullptr) { if (new_dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this // TODO(DarkLordZach): Find a better error code for this