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).
This commit is contained in:
parent
6fab17d860
commit
93522e30a8
3 changed files with 56 additions and 61 deletions
|
@ -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 <boost/range/distance.hpp>
|
||||
|
||||
namespace {
|
||||
template<class T, class U>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,23 +14,16 @@
|
|||
//
|
||||
// Aegisub Project http://www.aegisub.org/
|
||||
|
||||
/// @file path.h
|
||||
/// @brief Common paths.
|
||||
/// @ingroup libaegisub
|
||||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
#include <array>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <map>
|
||||
|
||||
namespace agi {
|
||||
/// Class for handling everything path-related in Aegisub
|
||||
class Path {
|
||||
/// Token -> Path map
|
||||
std::map<std::string, fs::path> tokens;
|
||||
|
||||
/// Path -> Token map
|
||||
std::map<fs::path, std::string> paths;
|
||||
std::array<fs::path, 8> 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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue