C++17ification.
C++17ification! - Filesystem interaction now uses std::filesystem::path. - Usage of const char*, std::string have been changed to std::string_view where appropriate. - Usage of printf-style functions changed to use fmt.
This commit is contained in:
@@ -2,359 +2,29 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/string.h"
|
||||
|
||||
// codecvt existence check
|
||||
#ifdef __clang__
|
||||
// using clang
|
||||
#if (__clang_major__ < 4) // 3.3 has it but I think we need at least 4 anyway
|
||||
// insufficient clang version
|
||||
#define NO_CODECVT 1
|
||||
#else
|
||||
#include <codecvt>
|
||||
#endif
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
// using gcc
|
||||
#if (__GNUC__ < 5)
|
||||
// insufficient clang version
|
||||
#define NO_CODECVT 1
|
||||
#else
|
||||
#include <codecvt>
|
||||
#endif
|
||||
// since the windows 10 sdk is required, this shouldn't be an issue
|
||||
#elif defined(_MSC_VER)
|
||||
#include <codecvt>
|
||||
#endif
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
|
||||
#define UTF_CPP_CPLUSPLUS 201703L
|
||||
#include "third_party/utfcpp/source/utf8.h"
|
||||
|
||||
namespace utfcpp = utf8;
|
||||
|
||||
namespace xe {
|
||||
|
||||
std::string to_string(const std::wstring& source) {
|
||||
#if NO_CODECVT
|
||||
return std::string(source.begin(), source.end());
|
||||
#else
|
||||
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.to_bytes(source);
|
||||
#endif // XE_PLATFORM_LINUX
|
||||
std::string to_utf8(const std::u16string_view source) {
|
||||
return utfcpp::utf16to8(source);
|
||||
}
|
||||
|
||||
std::wstring to_wstring(const std::string& source) {
|
||||
#if NO_CODECVT
|
||||
return std::wstring(source.begin(), source.end());
|
||||
#else
|
||||
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.from_bytes(source);
|
||||
#endif // XE_PLATFORM_LINUX
|
||||
}
|
||||
|
||||
std::string format_string(const char* format, va_list args) {
|
||||
if (!format) {
|
||||
return "";
|
||||
}
|
||||
size_t max_len = 64;
|
||||
std::string new_s;
|
||||
while (true) {
|
||||
new_s.resize(max_len);
|
||||
int ret =
|
||||
std::vsnprintf(const_cast<char*>(new_s.data()), max_len, format, args);
|
||||
if (ret > max_len) {
|
||||
// Needed size is known (+2 for termination and avoid ambiguity).
|
||||
max_len = ret + 2;
|
||||
} else if (ret == -1 || ret >= max_len - 1) {
|
||||
// Handle some buggy vsnprintf implementations.
|
||||
max_len *= 2;
|
||||
} else {
|
||||
// Everything fit for sure.
|
||||
new_s.resize(ret);
|
||||
return new_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring format_string(const wchar_t* format, va_list args) {
|
||||
if (!format) {
|
||||
return L"";
|
||||
}
|
||||
size_t max_len = 64;
|
||||
std::wstring new_s;
|
||||
while (true) {
|
||||
new_s.resize(max_len);
|
||||
int ret = std::vswprintf(const_cast<wchar_t*>(new_s.data()), max_len,
|
||||
format, args);
|
||||
if (ret > max_len) {
|
||||
// Needed size is known (+2 for termination and avoid ambiguity).
|
||||
max_len = ret + 2;
|
||||
} else if (ret == -1 || ret >= max_len - 1) {
|
||||
// Handle some buggy vsnprintf implementations.
|
||||
max_len *= 2;
|
||||
} else {
|
||||
// Everything fit for sure.
|
||||
new_s.resize(ret);
|
||||
return new_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> split_string(const std::string& path,
|
||||
const std::string& delimiters) {
|
||||
std::vector<std::string> parts;
|
||||
size_t n = 0;
|
||||
size_t last = 0;
|
||||
while ((n = path.find_first_of(delimiters, last)) != path.npos) {
|
||||
if (last != n) {
|
||||
parts.push_back(path.substr(last, n - last));
|
||||
}
|
||||
last = n + 1;
|
||||
}
|
||||
if (last != path.size()) {
|
||||
parts.push_back(path.substr(last));
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> split_string(const std::wstring& path,
|
||||
const std::wstring& delimiters) {
|
||||
std::vector<std::wstring> parts;
|
||||
size_t n = 0;
|
||||
size_t last = 0;
|
||||
while ((n = path.find_first_of(delimiters, last)) != path.npos) {
|
||||
if (last != n) {
|
||||
parts.push_back(path.substr(last, n - last));
|
||||
}
|
||||
last = n + 1;
|
||||
}
|
||||
if (last != path.size()) {
|
||||
parts.push_back(path.substr(last));
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
std::string::size_type find_first_of_case(const std::string& target,
|
||||
const std::string& search) {
|
||||
const char* str = target.c_str();
|
||||
while (*str) {
|
||||
if (!strncasecmp(str, search.c_str(), search.size())) {
|
||||
break;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
if (*str) {
|
||||
return str - target.c_str();
|
||||
} else {
|
||||
return std::string::npos;
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring to_absolute_path(const std::wstring& path) {
|
||||
#if XE_PLATFORM_WIN32
|
||||
std::wstring result;
|
||||
wchar_t* buffer = _wfullpath(nullptr, path.c_str(), 0);
|
||||
if (buffer != nullptr) {
|
||||
result.assign(buffer);
|
||||
free(buffer);
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
char buffer[kMaxPath];
|
||||
realpath(xe::to_string(path).c_str(), buffer);
|
||||
return xe::to_wstring(buffer);
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
}
|
||||
|
||||
std::vector<std::string> split_path(const std::string& path) {
|
||||
return split_string(path, "\\/");
|
||||
}
|
||||
|
||||
std::vector<std::wstring> split_path(const std::wstring& path) {
|
||||
return split_string(path, L"\\/");
|
||||
}
|
||||
|
||||
std::string join_paths(const std::string& left, const std::string& right,
|
||||
char sep) {
|
||||
if (!left.size()) {
|
||||
return right;
|
||||
} else if (!right.size()) {
|
||||
return left;
|
||||
}
|
||||
if (left[left.size() - 1] == sep) {
|
||||
return left + right;
|
||||
} else {
|
||||
return left + sep + right;
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring join_paths(const std::wstring& left, const std::wstring& right,
|
||||
wchar_t sep) {
|
||||
if (!left.size()) {
|
||||
return right;
|
||||
} else if (!right.size()) {
|
||||
return left;
|
||||
}
|
||||
if (left[left.size() - 1] == sep) {
|
||||
return left + right;
|
||||
} else {
|
||||
return left + sep + right;
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring fix_path_separators(const std::wstring& source, wchar_t new_sep) {
|
||||
// Swap all separators to new_sep.
|
||||
wchar_t old_sep = new_sep == '\\' ? '/' : '\\';
|
||||
std::wstring::size_type pos = 0;
|
||||
std::wstring dest = source;
|
||||
while ((pos = source.find_first_of(old_sep, pos)) != std::wstring::npos) {
|
||||
dest[pos] = new_sep;
|
||||
++pos;
|
||||
}
|
||||
// Replace redundant separators.
|
||||
pos = 0;
|
||||
while ((pos = dest.find_first_of(new_sep, pos)) != std::wstring::npos) {
|
||||
if (pos < dest.size() - 1) {
|
||||
if (dest[pos + 1] == new_sep) {
|
||||
dest.erase(pos + 1, 1);
|
||||
}
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
std::string fix_path_separators(const std::string& source, char new_sep) {
|
||||
// Swap all separators to new_sep.
|
||||
char old_sep = new_sep == '\\' ? '/' : '\\';
|
||||
std::string::size_type pos = 0;
|
||||
std::string dest = source;
|
||||
while ((pos = source.find_first_of(old_sep, pos)) != std::string::npos) {
|
||||
dest[pos] = new_sep;
|
||||
++pos;
|
||||
}
|
||||
// Replace redundant separators.
|
||||
pos = 0;
|
||||
while ((pos = dest.find_first_of(new_sep, pos)) != std::string::npos) {
|
||||
if (pos < dest.size() - 1) {
|
||||
if (dest[pos + 1] == new_sep) {
|
||||
dest.erase(pos + 1, 1);
|
||||
}
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
std::string find_name_from_path(const std::string& path, char sep) {
|
||||
std::string name(path);
|
||||
if (!path.empty()) {
|
||||
std::string::size_type from(std::string::npos);
|
||||
if (path.back() == sep) {
|
||||
from = path.size() - 2;
|
||||
}
|
||||
auto pos(path.find_last_of(sep, from));
|
||||
if (pos != std::string::npos) {
|
||||
if (from == std::string::npos) {
|
||||
name = path.substr(pos + 1);
|
||||
} else {
|
||||
auto len(from - pos);
|
||||
name = path.substr(pos + 1, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::wstring find_name_from_path(const std::wstring& path, wchar_t sep) {
|
||||
std::wstring name(path);
|
||||
if (!path.empty()) {
|
||||
std::wstring::size_type from(std::wstring::npos);
|
||||
if (path.back() == sep) {
|
||||
from = path.size() - 2;
|
||||
}
|
||||
auto pos(path.find_last_of(sep, from));
|
||||
if (pos != std::wstring::npos) {
|
||||
if (from == std::wstring::npos) {
|
||||
name = path.substr(pos + 1);
|
||||
} else {
|
||||
auto len(from - pos);
|
||||
name = path.substr(pos + 1, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string find_base_path(const std::string& path, char sep) {
|
||||
auto last_slash = path.find_last_of(sep);
|
||||
if (last_slash == std::string::npos) {
|
||||
return "";
|
||||
} else if (last_slash == path.length() - 1) {
|
||||
auto prev_slash = path.find_last_of(sep, last_slash - 1);
|
||||
if (prev_slash == std::string::npos) {
|
||||
return "";
|
||||
} else {
|
||||
return path.substr(0, prev_slash + 1);
|
||||
}
|
||||
} else {
|
||||
return path.substr(0, last_slash + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring find_base_path(const std::wstring& path, wchar_t sep) {
|
||||
auto last_slash = path.find_last_of(sep);
|
||||
if (last_slash == std::wstring::npos) {
|
||||
return L"";
|
||||
} else if (last_slash == path.length() - 1) {
|
||||
auto prev_slash = path.find_last_of(sep, last_slash - 1);
|
||||
if (prev_slash == std::wstring::npos) {
|
||||
return L"";
|
||||
} else {
|
||||
return path.substr(0, prev_slash + 1);
|
||||
}
|
||||
} else {
|
||||
return path.substr(0, last_slash + 1);
|
||||
}
|
||||
}
|
||||
|
||||
int fuzzy_match(const std::string& pattern, const char* value) {
|
||||
// https://github.com/mattyork/fuzzy/blob/master/lib/fuzzy.js
|
||||
// TODO(benvanik): look at https://github.com/atom/fuzzaldrin/tree/master/src
|
||||
// This does not weight complete substrings or prefixes right, which
|
||||
// kind of sucks.
|
||||
size_t pattern_index = 0;
|
||||
size_t value_length = std::strlen(value);
|
||||
int total_score = 0;
|
||||
int local_score = 0;
|
||||
for (size_t i = 0; i < value_length; ++i) {
|
||||
if (std::tolower(value[i]) == std::tolower(pattern[pattern_index])) {
|
||||
++pattern_index;
|
||||
local_score += 1 + local_score;
|
||||
} else {
|
||||
local_score = 0;
|
||||
}
|
||||
total_score += local_score;
|
||||
}
|
||||
return total_score;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, int>> fuzzy_filter(const std::string& pattern,
|
||||
const void* const* entries,
|
||||
size_t entry_count,
|
||||
size_t string_offset) {
|
||||
std::vector<std::pair<size_t, int>> results;
|
||||
results.reserve(entry_count);
|
||||
for (size_t i = 0; i < entry_count; ++i) {
|
||||
auto entry_value =
|
||||
reinterpret_cast<const char*>(entries[i]) + string_offset;
|
||||
int score = fuzzy_match(pattern, entry_value);
|
||||
results.emplace_back(i, score);
|
||||
}
|
||||
return results;
|
||||
std::u16string to_utf16(const std::string_view source) {
|
||||
return utfcpp::utf8to16(source);
|
||||
}
|
||||
|
||||
} // namespace xe
|
||||
|
||||
Reference in New Issue
Block a user