|
|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
#include "types.h"
|
|
|
|
|
#include <charconv>
|
|
|
|
|
#include <cstdarg>
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
@ -12,13 +13,16 @@
|
|
|
|
|
#include <string_view>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#if defined(__has_include) && __has_include(<charconv>)
|
|
|
|
|
#include <charconv>
|
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
|
#include "fast_float/fast_float.h"
|
|
|
|
|
|
|
|
|
|
// Older versions of libstdc++ are missing support for from_chars() with floats, and was only recently
|
|
|
|
|
// merged in libc++. So, just fall back to stringstream (yuck!) on everywhere except MSVC.
|
|
|
|
|
#if !defined(_MSC_VER)
|
|
|
|
|
#include <locale>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
|
#include <Availability.h>
|
|
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace StringUtil {
|
|
|
|
|
@ -56,23 +60,34 @@ static inline int Strncasecmp(const char* s1, const char* s2, std::size_t n)
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Wrapper arond std::from_chars
|
|
|
|
|
/// Wrapper around std::from_chars
|
|
|
|
|
template<typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
|
|
|
|
|
inline std::optional<T> FromChars(const std::string_view& str, int base = 10)
|
|
|
|
|
{
|
|
|
|
|
T value;
|
|
|
|
|
|
|
|
|
|
#if defined(__has_include) && __has_include(<charconv>)
|
|
|
|
|
const std::from_chars_result result = std::from_chars(str.data(), str.data() + str.length(), value, base);
|
|
|
|
|
if (result.ec != std::errc())
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
#else
|
|
|
|
|
std::string temp(str);
|
|
|
|
|
std::istringstream ss(temp);
|
|
|
|
|
ss >> std::setbase(base) >> value;
|
|
|
|
|
if (ss.fail())
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
template<typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
|
|
|
|
|
inline std::optional<T> FromChars(const std::string_view& str, int base, std::string_view* endptr)
|
|
|
|
|
{
|
|
|
|
|
T value;
|
|
|
|
|
|
|
|
|
|
const char* ptr = str.data();
|
|
|
|
|
const char* end = ptr + str.length();
|
|
|
|
|
const std::from_chars_result result = std::from_chars(ptr, end, value, base);
|
|
|
|
|
if (result.ec != std::errc())
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (endptr)
|
|
|
|
|
{
|
|
|
|
|
const size_t remaining_len = end - ptr - 1;
|
|
|
|
|
*endptr = (remaining_len > 0) ? std::string_view(result.ptr, remaining_len) : std::string_view();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
@ -82,22 +97,75 @@ inline std::optional<T> FromChars(const std::string_view& str)
|
|
|
|
|
{
|
|
|
|
|
T value;
|
|
|
|
|
|
|
|
|
|
#if defined(__has_include) && __has_include(<charconv>) && defined(_MSC_VER)
|
|
|
|
|
const std::from_chars_result result = std::from_chars(str.data(), str.data() + str.length(), value);
|
|
|
|
|
const fast_float::from_chars_result result = fast_float::from_chars(str.data(), str.data() + str.length(), value);
|
|
|
|
|
if (result.ec != std::errc())
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
#else
|
|
|
|
|
/// libstdc++ does not support from_chars with floats yet
|
|
|
|
|
std::string temp(str);
|
|
|
|
|
std::istringstream ss(temp);
|
|
|
|
|
ss >> value;
|
|
|
|
|
if (ss.fail())
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
template<typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
|
|
|
|
|
inline std::optional<T> FromChars(const std::string_view& str, std::string_view* endptr)
|
|
|
|
|
{
|
|
|
|
|
T value;
|
|
|
|
|
|
|
|
|
|
const char* ptr = str.data();
|
|
|
|
|
const char* end = ptr + str.length();
|
|
|
|
|
const fast_float::from_chars_result result = fast_float::from_chars(ptr, end, value);
|
|
|
|
|
if (result.ec != std::errc())
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (endptr)
|
|
|
|
|
{
|
|
|
|
|
const size_t remaining_len = end - ptr - 1;
|
|
|
|
|
*endptr = (remaining_len > 0) ? std::string_view(result.ptr, remaining_len) : std::string_view();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Wrapper around std::to_chars
|
|
|
|
|
template<typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
|
|
|
|
|
inline std::string ToChars(T value, int base = 10)
|
|
|
|
|
{
|
|
|
|
|
// to_chars() requires macOS 10.15+.
|
|
|
|
|
#if !defined(__APPLE__) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
|
|
|
|
|
constexpr size_t MAX_SIZE = 32;
|
|
|
|
|
char buf[MAX_SIZE];
|
|
|
|
|
std::string ret;
|
|
|
|
|
|
|
|
|
|
const std::to_chars_result result = std::to_chars(buf, buf + MAX_SIZE, value, base);
|
|
|
|
|
if (result.ec == std::errc())
|
|
|
|
|
ret.append(buf, result.ptr - buf);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
#else
|
|
|
|
|
std::ostringstream ss;
|
|
|
|
|
ss.imbue(std::locale::classic());
|
|
|
|
|
ss << std::setbase(base) << value;
|
|
|
|
|
return ss.str();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
|
|
|
|
|
inline std::string ToChars(T value)
|
|
|
|
|
{
|
|
|
|
|
// No to_chars() in older versions of libstdc++/libc++.
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
constexpr size_t MAX_SIZE = 64;
|
|
|
|
|
char buf[MAX_SIZE];
|
|
|
|
|
std::string ret;
|
|
|
|
|
const std::to_chars_result result = std::to_chars(buf, buf + MAX_SIZE, value);
|
|
|
|
|
if (result.ec == std::errc())
|
|
|
|
|
ret.append(buf, result.ptr - buf);
|
|
|
|
|
return ret;
|
|
|
|
|
#else
|
|
|
|
|
std::ostringstream ss;
|
|
|
|
|
ss.imbue(std::locale::classic());
|
|
|
|
|
ss << value;
|
|
|
|
|
return ss.str();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Explicit override for booleans
|
|
|
|
|
template<>
|
|
|
|
|
inline std::optional<bool> FromChars(const std::string_view& str, int base)
|
|
|
|
|
@ -119,6 +187,12 @@ inline std::optional<bool> FromChars(const std::string_view& str, int base)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
inline std::string ToChars(bool value, int base)
|
|
|
|
|
{
|
|
|
|
|
return std::string(value ? "true" : "false");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Encode/decode hexadecimal byte buffers
|
|
|
|
|
std::optional<std::vector<u8>> DecodeHex(const std::string_view& str);
|
|
|
|
|
std::string EncodeHex(const u8* data, int length);
|
|
|
|
|
|