Improve path splitting speed

And create a range closure so the components can be iterated without allocating a container.
This commit is contained in:
Marshall Mohror 2023-06-09 21:28:34 -05:00
parent b3e2c9f9f1
commit 8626140b73
6 changed files with 45 additions and 27 deletions

View file

@ -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)

View file

@ -350,17 +350,11 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
}
std::vector<std::string> SplitPathComponents(std::string_view filename) {
std::string copy(filename);
std::replace(copy.begin(), copy.end(), '\\', '/');
std::vector<std::string> out;
std::stringstream stream(copy);
std::string item;
while (std::getline(stream, item, '/')) {
out.push_back(std::move(item));
std::vector<std::string> 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) {

View file

@ -6,6 +6,10 @@
#include <filesystem>
#include <vector>
#include <range/v3/view/remove_if.hpp>
#include <range/v3/view/split_when.hpp>
#include <range/v3/view/transform.hpp>
#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<std::size_t>(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<std::string> SplitPathComponents(std::string_view filename);

View file

@ -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()

View file

@ -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 {

View file

@ -24,6 +24,7 @@
"fmt",
"lz4",
"nlohmann-json",
"range-v3",
"zlib",
"zstd"
],