From 8626140b7393014801215f7673420e0019f7bac9 Mon Sep 17 00:00:00 2001 From: Marshall Mohror Date: Fri, 9 Jun 2023 21:28:34 -0500 Subject: [PATCH] Improve path splitting speed And create a range closure so the components can be iterated without allocating a container. --- CMakeLists.txt | 1 + src/common/fs/path_util.cpp | 14 ++++---------- src/common/fs/path_util.h | 21 +++++++++++++++++++++ src/core/CMakeLists.txt | 2 +- src/core/file_sys/vfs.cpp | 33 +++++++++++++++++---------------- vcpkg.json | 1 + 6 files changed, 45 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d03bbf94e..9e74e033ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -279,6 +279,7 @@ find_package(LLVM MODULE COMPONENTS Demangle) find_package(lz4 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED) find_package(Opus 1.3 MODULE) +find_package(range-v3 REQUIRED) find_package(ZLIB 1.2 REQUIRED) find_package(zstd 1.5 REQUIRED) diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index d71cfacc6a..2729961c9f 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -350,17 +350,11 @@ std::string_view RemoveTrailingSlash(std::string_view path) { } std::vector SplitPathComponents(std::string_view filename) { - std::string copy(filename); - std::replace(copy.begin(), copy.end(), '\\', '/'); - std::vector out; - - std::stringstream stream(copy); - std::string item; - while (std::getline(stream, item, '/')) { - out.push_back(std::move(item)); + std::vector copied_components; + for (std::string_view component : filename | split_path_components_view) { + copied_components.emplace_back(component); } - - return out; + return copied_components; } std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index ba28964d0b..753f1f3a2b 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h @@ -6,6 +6,10 @@ #include #include +#include +#include +#include + #include "common/fs/fs_util.h" namespace Common::FS { @@ -284,6 +288,23 @@ enum class DirectorySeparator { PlatformDefault, }; +// A range adaptor closure which splits the path on '/' or '\' +// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } +constexpr auto split_path_components_view = + // Somehow split_when never made it into the standard + ranges::views::split_when([](auto cur, auto end) { + const char c = *cur; + bool do_split = c == '\\' || c == '/'; + return std::make_pair(do_split, cur + (do_split ? 1 : 0)); + }) | + // Convert from opaque range to std::string_view + ranges::views::transform([](auto&& chunk) { + return std::string_view{&*ranges::begin(chunk), + static_cast(ranges::distance(chunk))}; + }) | + // Skip any empty segments like at the beginning and end of "/root/directory/" + ranges::views::remove_if(&std::string_view::empty); + // 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 SplitPathComponents(std::string_view filename); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 99602699a2..1650d2338b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -837,7 +837,7 @@ endif() create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) -target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus) +target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus range-v3) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) endif() diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 6398424015..05faf51631 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -200,31 +200,32 @@ std::string VfsFile::GetFullPath() const { } VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { - 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()) { + auto&& component_view = path | Common::FS::split_path_components_view; + auto next_component = component_view.begin(); + if (next_component == component_view.end()) { return nullptr; } - if (vec.size() == 1) { - return GetFile(vec[0]); - } - - auto dir = GetSubdirectory(vec[0]); - for (std::size_t component = 1; component < vec.size() - 1; ++component) { - if (dir == nullptr) { - return nullptr; - } - - dir = dir->GetSubdirectory(vec[component]); + std::string_view component = *next_component; + if (++next_component == component_view.end()) { + return GetFile(component); } + auto dir = GetSubdirectory(component); if (dir == nullptr) { return nullptr; } + component = *next_component; - return dir->GetFile(vec.back()); + while (++next_component != component_view.end()) { + dir = dir->GetSubdirectory(component); + if (dir == nullptr) { + return nullptr; + } + component = *next_component; + } + + return dir->GetFile(component); } VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const { diff --git a/vcpkg.json b/vcpkg.json index 26f545c6c2..551e9533d0 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -24,6 +24,7 @@ "fmt", "lz4", "nlohmann-json", + "range-v3", "zlib", "zstd" ],