From 93522e30a8796f104ab0dec51793c4fb78b730f3 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 4 Jul 2014 12:54:28 -0700 Subject: [PATCH] Use a static table of tokens for agi::Path The set of possible tokens is fixed, so using std::map is a bunch of pointless overhead (that turns out to not even really simplify the code). --- libaegisub/common/path.cpp | 102 ++++++++++++++------------- libaegisub/include/libaegisub/path.h | 13 +--- libaegisub/windows/path_win.cpp | 2 +- 3 files changed, 56 insertions(+), 61 deletions(-) diff --git a/libaegisub/common/path.cpp b/libaegisub/common/path.cpp index c7407a823..cca7302cc 100644 --- a/libaegisub/common/path.cpp +++ b/libaegisub/common/path.cpp @@ -14,10 +14,6 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file path.cpp -/// @brief Platform-independent path code -/// @ingroup libaegisub - #include "libaegisub/path.h" #include "libaegisub/fs.h" @@ -26,44 +22,56 @@ #include namespace { - template - typename T::const_iterator last_less_than(T const& cont, U const& value) { - auto it = cont.upper_bound(value); - if (it != cont.begin()) - --it; - return it; +static const char *tokens[] = { + "?audio", + "?data", + "?dictionary", + "?local", + "?script", + "?temp", + "?user", + "?video" +}; + +int find_token(const char *str, size_t len) { + if (len < 5 || str[0] != '?') return -1; + int idx; + switch (str[1] + str[4]) { + case 'a' + 'i': idx = 0; break; + case 'd' + 'a': idx = 1; break; + case 'd' + 't': idx = 2; break; + case 'l' + 'a': idx = 3; break; + case 's' + 'i': idx = 4; break; + case 't' + 'p': idx = 5; break; + case 'u' + 'r': idx = 6; break; + case 'v' + 'e': idx = 7; break; + default: return -1; } + + return strncmp(str, tokens[idx], strlen(tokens[idx])) == 0 ? idx : -1; +} } namespace agi { Path::Path() { - tokens["?user"]; - tokens["?local"]; - tokens["?data"]; - tokens["?temp"]; - tokens["?dictionary"]; - + static_assert(sizeof(paths) / sizeof(paths[0]) == sizeof(tokens) / sizeof(tokens[0]), + "Token and path arrays need to be the same size"); FillPlatformSpecificPaths(); - - tokens["?audio"]; - tokens["?script"]; - tokens["?video"]; } fs::path Path::Decode(std::string const& path) const { - const auto it = last_less_than(tokens, path); - - if (!it->second.empty() && boost::starts_with(path, it->first)) - return (it->second/path.substr(it->first.size())).make_preferred(); - return fs::path(path).make_preferred(); + int idx = find_token(path.c_str(), path.size()); + if (idx == -1 || paths[idx].empty()) + return fs::path(path).make_preferred(); + return (paths[idx]/path.substr(strlen(tokens[idx]))).make_preferred(); } fs::path Path::MakeRelative(fs::path const& path, std::string const& token) const { - const auto it = tokens.find(token); - if (it == tokens.end()) throw agi::InternalError("Bad token: " + token); + int idx = find_token(token.c_str(), token.size()); + if (idx == -1) throw agi::InternalError("Bad token: " + token); - return MakeRelative(path, it->second); + return MakeRelative(path, paths[idx]); } fs::path Path::MakeRelative(fs::path const& path, fs::path const& base) const { @@ -92,53 +100,47 @@ fs::path Path::MakeRelative(fs::path const& path, fs::path const& base) const { fs::path Path::MakeAbsolute(fs::path path, std::string const& token) const { if (path.empty()) return path; - const auto it = tokens.find(token); - if (it == tokens.end()) throw agi::InternalError("Bad token: " + token); + int idx = find_token(token.c_str(), token.size()); + if (idx == -1) throw agi::InternalError("Bad token: " + token); path.make_preferred(); const auto str = path.string(); if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:")) return path; - return (it->second.empty() || path.is_absolute()) ? path : it->second/path; + return (paths[idx].empty() || path.is_absolute()) ? path : paths[idx]/path; } std::string Path::Encode(fs::path const& path) const { // Find the shortest encoding of path made relative to each token std::string shortest = path.string(); size_t length = boost::distance(path); - for (auto const& tok : tokens) { - if (tok.second.empty()) continue; + for (size_t i = 0; i < paths.size(); ++i) { + if (paths[i].empty()) continue; - const auto p = MakeRelative(path, tok.first); + const auto p = MakeRelative(path, tokens[i]); const size_t d = boost::distance(p); if (d < length) { length = d; - shortest = (tok.first/p).string(); + shortest = (tokens[i]/p).string(); } } return shortest; } -void Path::SetToken(std::string const& token_name, fs::path const& token_value) { - const auto it = tokens.find(token_name); - if (it == tokens.end()) throw agi::InternalError("Bad token: " + token_name); +void Path::SetToken(const char *token_name, fs::path const& token_value) { + int idx = find_token(token_name, strlen(token_name)); + if (idx == -1) throw agi::InternalError("Bad token: " + std::string(token_name)); if (token_value.empty()) - it->second = token_value; + paths[idx] = token_value; else if (!token_value.is_absolute()) - it->second.clear(); + paths[idx].clear(); else { - it->second = token_value; - it->second.make_preferred(); - if (fs::FileExists(it->second)) - it->second = it->second.parent_path(); - } - - paths.clear(); - for (auto const& tok : tokens) { - if (!tok.second.empty()) - paths[tok.second] = tok.first; + paths[idx] = token_value; + paths[idx].make_preferred(); + if (fs::FileExists(paths[idx])) + paths[idx] = paths[idx].parent_path(); } } diff --git a/libaegisub/include/libaegisub/path.h b/libaegisub/include/libaegisub/path.h index 73398c5af..a1bcb7afc 100644 --- a/libaegisub/include/libaegisub/path.h +++ b/libaegisub/include/libaegisub/path.h @@ -14,23 +14,16 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file path.h -/// @brief Common paths. -/// @ingroup libaegisub - #include +#include #include -#include namespace agi { /// Class for handling everything path-related in Aegisub class Path { /// Token -> Path map - std::map tokens; - - /// Path -> Token map - std::map paths; + std::array paths; /// Platform-specific code to fill in the default paths, called in the constructor void FillPlatformSpecificPaths(); @@ -74,7 +67,7 @@ public: /// @param token_name A single word token beginning with '?' /// @param token_value An absolute path to a directory or file /// @throws InternalError if `token` is not a valid token name - void SetToken(std::string const& token_name, fs::path const& token_value); + void SetToken(const char *token_name, fs::path const& token_value); }; } diff --git a/libaegisub/windows/path_win.cpp b/libaegisub/windows/path_win.cpp index 77eb27ae6..4f164074a 100644 --- a/libaegisub/windows/path_win.cpp +++ b/libaegisub/windows/path_win.cpp @@ -40,7 +40,7 @@ agi::fs::path WinGetFolderPath(int folder) { namespace agi { void Path::FillPlatformSpecificPaths() { - tokens["?temp"] = boost::filesystem::temp_directory_path(); + SetToken("?temp", boost::filesystem::temp_directory_path()); SetToken("?user", WinGetFolderPath(CSIDL_APPDATA)/"Aegisub"); SetToken("?local", WinGetFolderPath(CSIDL_LOCAL_APPDATA)/"Aegisub");