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,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -10,214 +10,290 @@
|
||||
#ifndef XENIA_BASE_STRING_UTIL_H_
|
||||
#define XENIA_BASE_STRING_UTIL_H_
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <charconv>
|
||||
#include <string>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/base/string.h"
|
||||
#include "xenia/base/vec128.h"
|
||||
|
||||
// TODO(gibbed): Clang and GCC don't have std::from_chars for floating point(!)
|
||||
// despite it being part of the C++17 standard. Check this in the future to see
|
||||
// if it's been resolved.
|
||||
|
||||
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
namespace xe {
|
||||
namespace string_util {
|
||||
|
||||
// TODO(gibbed): Figure out why clang doesn't line forward declarations of
|
||||
// inline functions.
|
||||
inline std::string to_hex_string(uint32_t value) {
|
||||
return fmt::format("{:08X}", value);
|
||||
}
|
||||
|
||||
std::string to_hex_string(uint32_t value);
|
||||
std::string to_hex_string(uint64_t value);
|
||||
inline std::string to_hex_string(uint64_t value) {
|
||||
return fmt::format("{:016X}", value);
|
||||
}
|
||||
|
||||
inline std::string to_hex_string(float value) {
|
||||
union {
|
||||
uint32_t ui;
|
||||
float flt;
|
||||
} v;
|
||||
v.flt = value;
|
||||
return to_hex_string(v.ui);
|
||||
static_assert(sizeof(uint32_t) == sizeof(value));
|
||||
uint32_t pun;
|
||||
std::memcpy(&pun, &value, sizeof(value));
|
||||
return to_hex_string(pun);
|
||||
}
|
||||
|
||||
inline std::string to_hex_string(double value) {
|
||||
union {
|
||||
uint64_t ui;
|
||||
double dbl;
|
||||
} v;
|
||||
v.dbl = value;
|
||||
return to_hex_string(v.ui);
|
||||
static_assert(sizeof(uint64_t) == sizeof(value));
|
||||
uint64_t pun;
|
||||
std::memcpy(&pun, &value, sizeof(value));
|
||||
return to_hex_string(pun);
|
||||
}
|
||||
|
||||
std::string to_hex_string(const vec128_t& value);
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
|
||||
// TODO(DrChat): This should not exist. Force the caller to use vec128.
|
||||
std::string to_hex_string(const __m128& value);
|
||||
std::string to_string(const __m128& value);
|
||||
|
||||
#endif
|
||||
inline std::string to_hex_string(const vec128_t& value) {
|
||||
return fmt::format("[{:08X} {:08X} {:08X} {:08X} {:08X}]", value.u32[0],
|
||||
value.u32[1], value.u32[2], value.u32[3]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T from_string(const char* value, bool force_hex = false) {
|
||||
// Missing implementation for converting type T to string
|
||||
inline T from_string(const std::string_view value, bool force_hex = false) {
|
||||
// Missing implementation for converting type T from string
|
||||
throw;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool from_string<bool>(const char* value, bool force_hex) {
|
||||
return std::strcmp(value, "true") == 0 || value[0] == '1';
|
||||
}
|
||||
namespace internal {
|
||||
|
||||
template <>
|
||||
inline int32_t from_string<int32_t>(const char* value, bool force_hex) {
|
||||
if (force_hex || std::strchr(value, 'h') != nullptr) {
|
||||
return std::strtol(value, nullptr, 16);
|
||||
template <typename T, typename V = std::make_signed_t<T>>
|
||||
inline T make_negative(T value) {
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
value = static_cast<T>(-static_cast<V>(value));
|
||||
} else {
|
||||
return std::strtol(value, nullptr, 0);
|
||||
value = -value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t from_string<uint32_t>(const char* value, bool force_hex) {
|
||||
if (force_hex || std::strchr(value, 'h') != nullptr) {
|
||||
return std::strtoul(value, nullptr, 16);
|
||||
} else {
|
||||
return std::strtoul(value, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int64_t from_string<int64_t>(const char* value, bool force_hex) {
|
||||
if (force_hex || std::strchr(value, 'h') != nullptr) {
|
||||
return std::strtoll(value, nullptr, 16);
|
||||
} else {
|
||||
return std::strtoll(value, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint64_t from_string<uint64_t>(const char* value, bool force_hex) {
|
||||
if (force_hex || std::strchr(value, 'h') != nullptr) {
|
||||
return std::strtoull(value, nullptr, 16);
|
||||
} else {
|
||||
return std::strtoull(value, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float from_string<float>(const char* value, bool force_hex) {
|
||||
if (force_hex || std::strstr(value, "0x") == value ||
|
||||
std::strchr(value, 'h') != nullptr) {
|
||||
union {
|
||||
uint32_t ui;
|
||||
float flt;
|
||||
} v;
|
||||
v.ui = from_string<uint32_t>(value, force_hex);
|
||||
return v.flt;
|
||||
}
|
||||
return std::strtof(value, nullptr);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline double from_string<double>(const char* value, bool force_hex) {
|
||||
if (force_hex || std::strstr(value, "0x") == value ||
|
||||
std::strchr(value, 'h') != nullptr) {
|
||||
union {
|
||||
uint64_t ui;
|
||||
double dbl;
|
||||
} v;
|
||||
v.ui = from_string<uint64_t>(value, force_hex);
|
||||
return v.dbl;
|
||||
}
|
||||
return std::strtod(value, nullptr);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline vec128_t from_string<vec128_t>(const char* value, bool force_hex) {
|
||||
vec128_t v;
|
||||
char* p = const_cast<char*>(value);
|
||||
bool hex_mode = force_hex;
|
||||
if (*p == '[') {
|
||||
hex_mode = true;
|
||||
++p;
|
||||
} else if (*p == '(') {
|
||||
hex_mode = false;
|
||||
++p;
|
||||
} else {
|
||||
// Assume hex?
|
||||
hex_mode = true;
|
||||
++p;
|
||||
}
|
||||
if (hex_mode) {
|
||||
v.i32[0] = std::strtoul(p, &p, 16);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.i32[1] = std::strtoul(p, &p, 16);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.i32[2] = std::strtoul(p, &p, 16);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.i32[3] = std::strtoul(p, &p, 16);
|
||||
} else {
|
||||
v.f32[0] = std::strtof(p, &p);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.f32[1] = std::strtof(p, &p);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.f32[2] = std::strtof(p, &p);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
v.f32[3] = std::strtof(p, &p);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
|
||||
// TODO(DrChat): ?? Why is this here? Force the caller to use vec128.
|
||||
template <>
|
||||
inline __m128 from_string<__m128>(const char* value, bool force_hex) {
|
||||
__m128 v;
|
||||
float f[4];
|
||||
uint32_t u;
|
||||
char* p = const_cast<char*>(value);
|
||||
bool hex_mode = force_hex;
|
||||
if (*p == '[') {
|
||||
hex_mode = true;
|
||||
++p;
|
||||
} else if (*p == '(') {
|
||||
hex_mode = false;
|
||||
++p;
|
||||
} else {
|
||||
// Assume hex?
|
||||
hex_mode = true;
|
||||
++p;
|
||||
}
|
||||
if (hex_mode) {
|
||||
u = std::strtoul(p, &p, 16);
|
||||
f[0] = *reinterpret_cast<float*>(&u);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
u = std::strtoul(p, &p, 16);
|
||||
f[1] = *reinterpret_cast<float*>(&u);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
u = std::strtoul(p, &p, 16);
|
||||
f[2] = *reinterpret_cast<float*>(&u);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
u = std::strtoul(p, &p, 16);
|
||||
f[3] = *reinterpret_cast<float*>(&u);
|
||||
} else {
|
||||
f[0] = std::strtof(p, &p);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
f[1] = std::strtof(p, &p);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
f[2] = std::strtof(p, &p);
|
||||
while (*p == ' ' || *p == ',') ++p;
|
||||
f[3] = std::strtof(p, &p);
|
||||
}
|
||||
v = _mm_loadu_ps(f);
|
||||
return v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// integral_from_string
|
||||
template <typename T>
|
||||
inline T from_string(const std::string& value, bool force_hex = false) {
|
||||
return from_string<T>(value.c_str(), force_hex);
|
||||
inline T ifs(const std::string_view value, bool force_hex) {
|
||||
int base = 10;
|
||||
std::string_view range = value;
|
||||
bool is_hex = force_hex;
|
||||
bool is_negative = false;
|
||||
if (utf8::starts_with(range, "-")) {
|
||||
is_negative = true;
|
||||
range = range.substr(1);
|
||||
}
|
||||
if (utf8::starts_with(range, "0x")) {
|
||||
is_hex = true;
|
||||
range = range.substr(2);
|
||||
}
|
||||
if (utf8::ends_with(range, "h")) {
|
||||
is_hex = true;
|
||||
range = range.substr(0, range.length() - 1);
|
||||
}
|
||||
T result;
|
||||
if (is_hex) {
|
||||
base = 16;
|
||||
}
|
||||
// TODO(gibbed): do something more with errors?
|
||||
auto [p, error] =
|
||||
std::from_chars(range.data(), range.data() + range.size(), result, base);
|
||||
if (error != std::errc()) {
|
||||
assert_always();
|
||||
return T();
|
||||
}
|
||||
if (is_negative) {
|
||||
result = make_negative(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// floating_point_from_string
|
||||
template <typename T, typename PUN>
|
||||
inline T fpfs(const std::string_view value, bool force_hex) {
|
||||
static_assert(sizeof(T) == sizeof(PUN));
|
||||
std::string_view range = value;
|
||||
bool is_hex = force_hex;
|
||||
bool is_negative = false;
|
||||
if (utf8::starts_with(range, "-")) {
|
||||
is_negative = true;
|
||||
range = range.substr(1);
|
||||
}
|
||||
if (utf8::starts_with(range, "0x")) {
|
||||
is_hex = true;
|
||||
range = range.substr(2);
|
||||
}
|
||||
if (utf8::ends_with(range, "h")) {
|
||||
is_hex = true;
|
||||
range = range.substr(0, range.length() - 1);
|
||||
}
|
||||
T result;
|
||||
if (is_hex) {
|
||||
PUN pun = from_string<PUN>(range, true);
|
||||
if (is_negative) {
|
||||
pun = make_negative(pun);
|
||||
}
|
||||
std::memcpy(&result, &pun, sizeof(PUN));
|
||||
} else {
|
||||
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
|
||||
auto temp = std::string(range);
|
||||
result = std::strtof(temp.c_str(), nullptr);
|
||||
#else
|
||||
auto [p, error] = std::from_chars(range.data(), range.data() + range.size(),
|
||||
result, std::chars_format::general);
|
||||
// TODO(gibbed): do something more with errors?
|
||||
if (error != std::errc()) {
|
||||
assert_always();
|
||||
return T();
|
||||
}
|
||||
#endif
|
||||
if (is_negative) {
|
||||
result = -result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <>
|
||||
inline bool from_string<bool>(const std::string_view value, bool force_hex) {
|
||||
return value == "true" || value == "1";
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int8_t from_string<int8_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::ifs<int8_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint8_t from_string<uint8_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::ifs<uint8_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int16_t from_string<int16_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::ifs<int16_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint16_t from_string<uint16_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::ifs<uint16_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int32_t from_string<int32_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::ifs<int32_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t from_string<uint32_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::ifs<uint32_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int64_t from_string<int64_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::ifs<int64_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint64_t from_string<uint64_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::ifs<uint64_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float from_string<float>(const std::string_view value, bool force_hex) {
|
||||
return internal::fpfs<float, uint32_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline double from_string<double>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
return internal::fpfs<double, uint64_t>(value, force_hex);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline vec128_t from_string<vec128_t>(const std::string_view value,
|
||||
bool force_hex) {
|
||||
if (!value.size()) {
|
||||
return vec128_t();
|
||||
}
|
||||
vec128_t v;
|
||||
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
|
||||
auto temp = std::string(value);
|
||||
auto p = temp.c_str();
|
||||
auto end = temp.c_str() + temp.size();
|
||||
#else
|
||||
auto p = value.data();
|
||||
auto end = value.data() + value.size();
|
||||
#endif
|
||||
bool is_hex = force_hex;
|
||||
if (p != end && *p == '[') {
|
||||
is_hex = true;
|
||||
++p;
|
||||
} else if (p != end && *p == '(') {
|
||||
is_hex = false;
|
||||
++p;
|
||||
} else {
|
||||
// Assume hex?
|
||||
is_hex = true;
|
||||
}
|
||||
if (p == end) {
|
||||
assert_always();
|
||||
return vec128_t();
|
||||
}
|
||||
if (is_hex) {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
while (p != end && (*p == ' ' || *p == ',')) {
|
||||
++p;
|
||||
}
|
||||
if (p == end) {
|
||||
assert_always();
|
||||
return vec128_t();
|
||||
}
|
||||
auto result = std::from_chars(p, end, v.u32[i], 16);
|
||||
if (result.ec != std::errc()) {
|
||||
assert_always();
|
||||
return vec128_t();
|
||||
}
|
||||
p = result.ptr;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
while (p != end && (*p == ' ' || *p == ',')) {
|
||||
++p;
|
||||
}
|
||||
if (p == end) {
|
||||
assert_always();
|
||||
return vec128_t();
|
||||
}
|
||||
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
|
||||
char* next_p;
|
||||
v.f32[i] = std::strtof(p, &next_p);
|
||||
p = next_p;
|
||||
#else
|
||||
auto result =
|
||||
std::from_chars(p, end, v.f32[i], std::chars_format::general);
|
||||
if (result.ec != std::errc()) {
|
||||
assert_always();
|
||||
return vec128_t();
|
||||
}
|
||||
p = result.ptr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace string_util
|
||||
|
||||
Reference in New Issue
Block a user