rpcs3/Utilities/StrFmt.h

306 lines
7.3 KiB
C
Raw Normal View History

#pragma once // No BOM and only basic ASCII in this header, or a neko will die
2020-12-12 12:01:29 +00:00
#include "util/types.hpp"
2016-08-14 00:22:19 +00:00
2016-05-13 14:01:48 +00:00
#include <string>
2016-02-01 21:55:43 +00:00
namespace fmt
{
2020-12-18 07:39:54 +00:00
template <typename CharT, usz N, typename... Args>
2020-02-04 18:37:00 +00:00
static std::string format(const CharT(&)[N], const Args&...);
}
template <typename T, typename = void>
struct fmt_unveil
{
static_assert(sizeof(T) > 0, "fmt_unveil<> error: incomplete type");
using type = T;
static inline u64 get(const T& arg)
{
2020-12-12 13:29:55 +00:00
return reinterpret_cast<uptr>(&arg);
}
2016-08-07 13:59:46 +00:00
// Temporary value container (can possibly be created by other fmt_unveil<> specializations)
struct u64_wrapper
{
T arg;
// Allow implicit conversion
operator u64() const
{
2020-12-12 13:29:55 +00:00
return reinterpret_cast<uptr>(&arg);
2016-08-07 13:59:46 +00:00
}
};
// This overload resolution takes the precedence
static inline u64_wrapper get(T&& arg)
{
return u64_wrapper{std::move(arg)};
2016-08-07 13:59:46 +00:00
}
};
2016-08-13 13:36:04 +00:00
template <typename T>
struct fmt_unveil<T, std::enable_if_t<std::is_integral<T>::value && sizeof(T) <= 8 && alignof(T) <= 8>>
{
using type = T;
static inline u64 get(T arg)
{
return static_cast<T>(arg);
}
};
2016-08-13 13:36:04 +00:00
template <typename T>
struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof(T) <= 8 && alignof(T) <= 8>>
{
using type = T;
// Convert FP to f64 and reinterpret as u64
static inline u64 get(const f64& arg)
{
return std::bit_cast<u64>(arg);
}
};
2016-08-13 13:36:04 +00:00
template <typename T>
struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
{
using type = T;
static inline u64 get(T arg)
{
return static_cast<std::underlying_type_t<T>>(arg);
}
};
2016-08-13 13:36:04 +00:00
template <typename T>
struct fmt_unveil<T*, void>
{
2018-04-21 20:55:01 +00:00
using type = std::add_const_t<T>*;
2018-04-21 20:55:01 +00:00
static inline u64 get(type arg)
{
2020-12-12 13:29:55 +00:00
return reinterpret_cast<uptr>(arg);
}
};
2020-12-18 07:39:54 +00:00
template <typename T, usz N>
struct fmt_unveil<T[N], void>
{
2018-04-21 20:55:01 +00:00
using type = std::add_const_t<T>*;
2018-04-21 20:55:01 +00:00
static inline u64 get(type arg)
{
2020-12-12 13:29:55 +00:00
return reinterpret_cast<uptr>(arg);
}
};
2020-12-18 07:39:54 +00:00
template <typename T, bool Se, usz Align>
struct fmt_unveil<se_t<T, Se, Align>, void>
{
using type = typename fmt_unveil<T>::type;
static inline auto get(const se_t<T, Se, Align>& arg)
{
return fmt_unveil<T>::get(arg);
}
};
// String type format provider, also type classifier (format() called if an argument is formatted as "%s")
2016-08-13 13:36:04 +00:00
template <typename T, typename = void>
struct fmt_class_string
{
// Formatting function (must be explicitly specialized)
static void format(std::string& out, u64 arg);
// Helper typedef (visible in format())
using type = T;
// Helper function (converts arg to object reference)
static SAFE_BUFFERS FORCE_INLINE const T& get_object(u64 arg)
{
2020-12-12 13:29:55 +00:00
return *reinterpret_cast<const T*>(static_cast<uptr>(arg));
}
// Enum -> string function type
using convert_t = const char*(*)(T value);
// Helper function (safely converts arg to enum value)
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, convert_t convert)
{
const auto value = static_cast<std::underlying_type_t<T>>(arg);
// Check narrowing
if (static_cast<u64>(value) == arg)
{
if (const char* str = convert(static_cast<T>(value)))
{
out += str;
return;
}
}
// Fallback to underlying type formatting
fmt_class_string<std::underlying_type_t<T>>::format(out, static_cast<u64>(value));
}
2016-08-07 19:01:27 +00:00
// Helper function (bitset formatting)
2016-08-13 13:36:04 +00:00
static SAFE_BUFFERS FORCE_INLINE void format_bitset(std::string& out, u64 arg, const char* prefix, const char* delim, const char* suffix, void (*fmt)(std::string&, u64))
2016-08-07 19:01:27 +00:00
{
// Start from raw value
fmt_class_string<u64>::format(out, arg);
out += prefix;
2017-02-15 15:07:42 +00:00
for (u64 i = 0; i < 63; i++)
2016-08-07 19:01:27 +00:00
{
const u64 mask = 1ull << i;
if (arg & mask)
{
fmt(out, i);
2017-02-15 15:07:42 +00:00
if (arg >> (i + 1))
2016-08-07 19:01:27 +00:00
{
out += delim;
}
}
}
2017-02-15 15:07:42 +00:00
if (arg & (1ull << 63))
{
fmt(out, 63);
}
2016-08-07 19:01:27 +00:00
out += suffix;
}
// Helper constant (may be used in format_enum as lambda return value)
static constexpr const char* unknown = nullptr;
};
2016-08-13 13:36:04 +00:00
template <>
struct fmt_class_string<const void*, void>
{
static void format(std::string& out, u64 arg);
};
2016-08-13 13:36:04 +00:00
template <typename T>
struct fmt_class_string<T*, void> : fmt_class_string<const void*, void>
{
// Classify all pointers as const void*
};
2016-08-13 13:36:04 +00:00
template <>
struct fmt_class_string<const char*, void>
{
static void format(std::string& out, u64 arg);
};
2016-08-13 13:36:04 +00:00
template <>
struct fmt_class_string<char*, void> : fmt_class_string<const char*>
{
// Classify char* as const char*
};
2016-08-05 16:49:45 +00:00
struct fmt_type_info
{
2016-08-05 16:49:45 +00:00
decltype(&fmt_class_string<int>::format) fmt_string;
2016-08-13 13:36:04 +00:00
template <typename T>
2016-08-05 16:49:45 +00:00
static constexpr fmt_type_info make()
{
2016-08-05 16:49:45 +00:00
return fmt_type_info
{
2016-08-05 16:49:45 +00:00
&fmt_class_string<T>::format,
};
}
};
2018-08-25 09:06:57 +00:00
// Argument array type (each element generated via fmt_unveil<>)
template <typename... Args>
using fmt_args_t = const u64(&&)[sizeof...(Args) + 1];
template <typename Arg>
using fmt_unveil_t = typename fmt_unveil<Arg>::type;
2016-08-05 16:49:45 +00:00
namespace fmt
{
2018-03-22 19:48:38 +00:00
// Base-57 format helper
struct base57
{
const uchar* data;
2020-12-18 07:39:54 +00:00
usz size;
2018-03-22 19:48:38 +00:00
template <typename T>
base57(const T& arg)
: data(reinterpret_cast<const uchar*>(&arg))
, size(sizeof(T))
{
}
2020-12-18 07:39:54 +00:00
base57(const uchar* data, usz size)
2018-03-22 19:48:38 +00:00
: data(data)
, size(size)
{
}
};
2016-08-13 13:36:04 +00:00
template <typename... Args>
SAFE_BUFFERS FORCE_INLINE const fmt_type_info* get_type_info()
{
// Constantly initialized null-terminated list of type-specific information
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
return result;
}
template <typename... Args>
constexpr const fmt_type_info type_info_v[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
// Internal formatting function
2016-08-05 16:49:45 +00:00
void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept;
// Formatting function
2020-12-18 07:39:54 +00:00
template <typename CharT, usz N, typename... Args>
2020-02-04 18:37:00 +00:00
SAFE_BUFFERS FORCE_INLINE void append(std::string& out, const CharT(&fmt)[N], const Args&... args)
{
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
2020-02-04 18:37:00 +00:00
raw_append(out, reinterpret_cast<const char*>(fmt), type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
}
// Formatting function
2020-12-18 07:39:54 +00:00
template <typename CharT, usz N, typename... Args>
2020-02-04 18:37:00 +00:00
SAFE_BUFFERS FORCE_INLINE std::string format(const CharT(&fmt)[N], const Args&... args)
2016-02-01 21:55:43 +00:00
{
std::string result;
2020-02-04 18:37:00 +00:00
append(result, fmt, args...);
2016-02-01 21:55:43 +00:00
return result;
}
// Internal exception message formatting template, must be explicitly specialized or instantiated in cpp to minimize code bloat
[[noreturn]] void raw_throw_exception(const src_loc&, const char*, const fmt_type_info*, const u64*);
// Throw exception with formatting
2020-12-18 07:39:54 +00:00
template <typename CharT, usz N, typename... Args>
struct throw_exception
{
[[noreturn]] SAFE_BUFFERS FORCE_INLINE throw_exception(const CharT(&fmt)[N], const Args&... args,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION())
{
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
raw_throw_exception({line, col, file, func}, reinterpret_cast<const char*>(fmt), type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
}
[[noreturn]] ~throw_exception();
};
2020-12-18 07:39:54 +00:00
template <typename CharT, usz N, typename... Args>
throw_exception(const CharT(&)[N], const Args&...) -> throw_exception<CharT, N, Args...>;
}