mirror of https://github.com/odrling/Aegisub
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
|
@ -14,10 +14,6 @@
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file path.cpp
|
|
||||||
/// @brief Platform-independent path code
|
|
||||||
/// @ingroup libaegisub
|
|
||||||
|
|
||||||
#include "libaegisub/path.h"
|
#include "libaegisub/path.h"
|
||||||
|
|
||||||
#include "libaegisub/fs.h"
|
#include "libaegisub/fs.h"
|
||||||
|
@ -26,44 +22,56 @@
|
||||||
#include <boost/range/distance.hpp>
|
#include <boost/range/distance.hpp>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template<class T, class U>
|
static const char *tokens[] = {
|
||||||
typename T::const_iterator last_less_than(T const& cont, U const& value) {
|
"?audio",
|
||||||
auto it = cont.upper_bound(value);
|
"?data",
|
||||||
if (it != cont.begin())
|
"?dictionary",
|
||||||
--it;
|
"?local",
|
||||||
return it;
|
"?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 {
|
namespace agi {
|
||||||
|
|
||||||
Path::Path() {
|
Path::Path() {
|
||||||
tokens["?user"];
|
static_assert(sizeof(paths) / sizeof(paths[0]) == sizeof(tokens) / sizeof(tokens[0]),
|
||||||
tokens["?local"];
|
"Token and path arrays need to be the same size");
|
||||||
tokens["?data"];
|
|
||||||
tokens["?temp"];
|
|
||||||
tokens["?dictionary"];
|
|
||||||
|
|
||||||
FillPlatformSpecificPaths();
|
FillPlatformSpecificPaths();
|
||||||
|
|
||||||
tokens["?audio"];
|
|
||||||
tokens["?script"];
|
|
||||||
tokens["?video"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path Path::Decode(std::string const& path) const {
|
fs::path Path::Decode(std::string const& path) const {
|
||||||
const auto it = last_less_than(tokens, path);
|
int idx = find_token(path.c_str(), path.size());
|
||||||
|
if (idx == -1 || paths[idx].empty())
|
||||||
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();
|
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 {
|
fs::path Path::MakeRelative(fs::path const& path, std::string const& token) const {
|
||||||
const auto it = tokens.find(token);
|
int idx = find_token(token.c_str(), token.size());
|
||||||
if (it == tokens.end()) throw agi::InternalError("Bad token: " + token);
|
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 {
|
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 {
|
fs::path Path::MakeAbsolute(fs::path path, std::string const& token) const {
|
||||||
if (path.empty()) return path;
|
if (path.empty()) return path;
|
||||||
const auto it = tokens.find(token);
|
int idx = find_token(token.c_str(), token.size());
|
||||||
if (it == tokens.end()) throw agi::InternalError("Bad token: " + token);
|
if (idx == -1) throw agi::InternalError("Bad token: " + token);
|
||||||
|
|
||||||
path.make_preferred();
|
path.make_preferred();
|
||||||
const auto str = path.string();
|
const auto str = path.string();
|
||||||
if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:"))
|
if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:"))
|
||||||
return path;
|
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 {
|
std::string Path::Encode(fs::path const& path) const {
|
||||||
// Find the shortest encoding of path made relative to each token
|
// Find the shortest encoding of path made relative to each token
|
||||||
std::string shortest = path.string();
|
std::string shortest = path.string();
|
||||||
size_t length = boost::distance(path);
|
size_t length = boost::distance(path);
|
||||||
for (auto const& tok : tokens) {
|
for (size_t i = 0; i < paths.size(); ++i) {
|
||||||
if (tok.second.empty()) continue;
|
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);
|
const size_t d = boost::distance(p);
|
||||||
if (d < length) {
|
if (d < length) {
|
||||||
length = d;
|
length = d;
|
||||||
shortest = (tok.first/p).string();
|
shortest = (tokens[i]/p).string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return shortest;
|
return shortest;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Path::SetToken(std::string const& token_name, fs::path const& token_value) {
|
void Path::SetToken(const char *token_name, fs::path const& token_value) {
|
||||||
const auto it = tokens.find(token_name);
|
int idx = find_token(token_name, strlen(token_name));
|
||||||
if (it == tokens.end()) throw agi::InternalError("Bad token: " + token_name);
|
if (idx == -1) throw agi::InternalError("Bad token: " + std::string(token_name));
|
||||||
|
|
||||||
if (token_value.empty())
|
if (token_value.empty())
|
||||||
it->second = token_value;
|
paths[idx] = token_value;
|
||||||
else if (!token_value.is_absolute())
|
else if (!token_value.is_absolute())
|
||||||
it->second.clear();
|
paths[idx].clear();
|
||||||
else {
|
else {
|
||||||
it->second = token_value;
|
paths[idx] = token_value;
|
||||||
it->second.make_preferred();
|
paths[idx].make_preferred();
|
||||||
if (fs::FileExists(it->second))
|
if (fs::FileExists(paths[idx]))
|
||||||
it->second = it->second.parent_path();
|
paths[idx] = paths[idx].parent_path();
|
||||||
}
|
|
||||||
|
|
||||||
paths.clear();
|
|
||||||
for (auto const& tok : tokens) {
|
|
||||||
if (!tok.second.empty())
|
|
||||||
paths[tok.second] = tok.first;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,23 +14,16 @@
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file path.h
|
|
||||||
/// @brief Common paths.
|
|
||||||
/// @ingroup libaegisub
|
|
||||||
|
|
||||||
#include <libaegisub/fs_fwd.h>
|
#include <libaegisub/fs_fwd.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
/// Class for handling everything path-related in Aegisub
|
/// Class for handling everything path-related in Aegisub
|
||||||
class Path {
|
class Path {
|
||||||
/// Token -> Path map
|
/// Token -> Path map
|
||||||
std::map<std::string, fs::path> tokens;
|
std::array<fs::path, 8> paths;
|
||||||
|
|
||||||
/// Path -> Token map
|
|
||||||
std::map<fs::path, std::string> paths;
|
|
||||||
|
|
||||||
/// Platform-specific code to fill in the default paths, called in the constructor
|
/// Platform-specific code to fill in the default paths, called in the constructor
|
||||||
void FillPlatformSpecificPaths();
|
void FillPlatformSpecificPaths();
|
||||||
|
@ -74,7 +67,7 @@ public:
|
||||||
/// @param token_name A single word token beginning with '?'
|
/// @param token_name A single word token beginning with '?'
|
||||||
/// @param token_value An absolute path to a directory or file
|
/// @param token_value An absolute path to a directory or file
|
||||||
/// @throws InternalError if `token` is not a valid token name
|
/// @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 {
|
namespace agi {
|
||||||
|
|
||||||
void Path::FillPlatformSpecificPaths() {
|
void Path::FillPlatformSpecificPaths() {
|
||||||
tokens["?temp"] = boost::filesystem::temp_directory_path();
|
SetToken("?temp", boost::filesystem::temp_directory_path());
|
||||||
|
|
||||||
SetToken("?user", WinGetFolderPath(CSIDL_APPDATA)/"Aegisub");
|
SetToken("?user", WinGetFolderPath(CSIDL_APPDATA)/"Aegisub");
|
||||||
SetToken("?local", WinGetFolderPath(CSIDL_LOCAL_APPDATA)/"Aegisub");
|
SetToken("?local", WinGetFolderPath(CSIDL_LOCAL_APPDATA)/"Aegisub");
|
||||||
|
|
Loading…
Reference in New Issue