Formatting system improved

`unveil<>` renamed to `fmt_unveil<>`, now packs args to u64 imitating va_args
`bijective...` removed, `cfg::enum_entry` now uses formatting system
`fmt_class_string<>` added, providing type-specific "%s" handler function
Added `fmt::append`, removed `fmt::narrow` (too obscure)
Utilities/cfmt.h: C-style format template function (WIP)
Minor formatting fixes and cleanup
This commit is contained in:
Nekotekina 2016-08-03 23:51:05 +03:00
parent 662fce38bd
commit 5a36c57c57
63 changed files with 1305 additions and 469 deletions

View file

@ -298,10 +298,6 @@ union alignas(16) v128
_u64[0] = 0;
_u64[1] = 0;
}
std::string to_hex() const;
std::string to_xyzw() const;
};
inline v128 operator |(const v128& left, const v128& right)
@ -950,11 +946,13 @@ template<typename T> using atomic_le_t = atomic_t<le_t<T>>;
// Formatting for BE/LE data
template<typename T, bool Se, std::size_t Align>
struct unveil<se_t<T, Se, Align>, void>
struct fmt_unveil<se_t<T, Se, Align>, void>
{
static inline auto get(const se_t<T, Se, Align>& arg)
using type = typename fmt_unveil<T>::type;
static inline u64 get(const se_t<T, Se, Align>& arg)
{
return unveil<T>::get(arg);
return fmt_unveil<T>::get(arg);
}
};

View file

@ -242,3 +242,36 @@ struct ff_t : bf_base<T, N>
return V;
}
};
template<typename T, uint I, uint N>
struct fmt_unveil<bf_t<T, I, N>, void>
{
using type = typename fmt_unveil<simple_t<T>>::type;
static inline u64 get(const bf_t<T, I, N>& bf)
{
return fmt_unveil<type>::get(bf);
}
};
template<typename F, typename... Fields>
struct fmt_unveil<cf_t<F, Fields...>, void>
{
using type = typename fmt_unveil<simple_t<typename F::type>>::type;
static inline u64 get(const cf_t<F, Fields...>& cf)
{
return fmt_unveil<type>::get(cf);
}
};
template<typename T, T V, uint N>
struct fmt_unveil<ff_t<T, V, N>, void>
{
using type = typename fmt_unveil<simple_t<T>>::type;
static inline u64 get(const ff_t<T, V, N>& ff)
{
return fmt_unveil<type>::get(ff);
}
};

View file

@ -256,3 +256,14 @@ struct atomic_test_and_complement<bitset_t<T>, T, std::enable_if_t<std::is_enum<
static constexpr auto atomic_op = &_op;
};
template<typename T, std::size_t BitSize>
struct fmt_unveil<bitset_t<T, BitSize>, void>
{
using type = typename bitset_t<T, BitSize>::raw_type;
static inline u64 get(const bitset_t<T, BitSize>& value)
{
return fmt_unveil<type>::get(static_cast<type>(value._value()));
}
};

View file

@ -85,6 +85,62 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
return true;
}
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string& value)
{
for (u64 i = 0;; i++)
{
std::string var;
func(var, i);
if (var == value)
{
if (out) *out = i;
return true;
}
std::string hex;
fmt_class_string<u64>::format(hex, i);
if (var == hex)
{
break;
}
}
try
{
const auto val = std::stoull(value, nullptr, 0);
if (out) *out = val;
return true;
}
catch (...)
{
return false;
}
}
std::vector<std::string> cfg::try_to_enum_list(decltype(&fmt_class_string<int>::format) func)
{
std::vector<std::string> result;
for (u64 i = 0;; i++)
{
std::string var;
func(var, i);
std::string hex;
fmt_class_string<u64>::format(hex, i);
if (var == hex)
{
break;
}
result.emplace_back(std::move(var));
}
return result;
}
void cfg::encode(YAML::Emitter& out, const cfg::entry_base& rhs)
{
switch (rhs.get_type())

View file

@ -2,6 +2,7 @@
#include "Utilities/types.h"
#include "Utilities/Atomic.h"
#include "Utilities/StrFmt.h"
#include <initializer_list>
#include <exception>
@ -18,6 +19,12 @@ namespace cfg
// Convert string to signed integer
bool try_to_int64(s64* out, const std::string& value, s64 min, s64 max);
// Internal hack
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string&);
// Internal hack
std::vector<std::string> try_to_enum_list(decltype(&fmt_class_string<int>::format) func);
// Config tree entry type.
enum class type : uint
{
@ -296,26 +303,26 @@ namespace cfg
std::string to_string() const override
{
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
{
if (bijective<T, const char*>::map[i].v1 == m_value)
{
return bijective<T, const char*>::map[i].v2;
}
}
return{}; // TODO: ???
std::string result;
fmt_class_string<T>::format(result, fmt_unveil<T>::get(m_value));
return result; // TODO: ???
}
bool from_string(const std::string& value) override
{
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
u64 result;
if (try_to_enum_value(&result, &fmt_class_string<T>::format, value))
{
if (bijective<T, const char*>::map[i].v2 == value)
const auto val = static_cast<std::underlying_type_t<T>>(result);
if (static_cast<u64>(val) != result)
{
m_value = bijective<T, const char*>::map[i].v1;
return true;
return false;
}
m_value = static_cast<T>(val);
return true;
}
return false;
@ -323,14 +330,7 @@ namespace cfg
std::vector<std::string> to_list() const override
{
std::vector<std::string> result;
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
{
result.emplace_back(bijective<T, const char*>::map[i].v2);
}
return result;
return try_to_enum_list(&fmt_class_string<T>::format);
}
};

View file

@ -1333,3 +1333,37 @@ u64 fs::get_dir_size(const std::string& path)
return result;
}
template<>
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto arg)
{
switch (arg)
{
STR_CASE(fs::seek_mode::seek_set);
STR_CASE(fs::seek_mode::seek_cur);
STR_CASE(fs::seek_mode::seek_end);
}
return unknown;
});
}
template<>
void fmt_class_string<fs::error>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto arg)
{
switch (arg)
{
case fs::error::ok: return "OK";
case fs::error::inval: return "Invalid arguments";
case fs::error::noent: return "Not found";
case fs::error::exist: return "Already exists";
}
return unknown;
});
}

View file

@ -463,21 +463,3 @@ namespace fs
// Error code returned
extern thread_local error g_tls_error;
}
template<>
struct unveil<fs::error>
{
static inline const char* get(fs::error error)
{
switch (error)
{
case fs::error::ok: return "OK";
case fs::error::inval: return "Invalid arguments";
case fs::error::noent: return "Not found";
case fs::error::exist: return "Already exists";
default: throw error;
}
}
};

View file

@ -3,15 +3,31 @@
#include "StrFmt.h"
#include "rpcs3_version.h"
#include <cstdarg>
#include <string>
// Thread-specific log prefix provider
thread_local std::string(*g_tls_log_prefix)() = nullptr;
#ifndef _MSC_VER
constexpr DECLARE(bijective<logs::level, const char*>::map);
#endif
template<>
void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto lev)
{
switch (lev)
{
case logs::level::always: return "Nothing";
case logs::level::fatal: return "Fatal";
case logs::level::error: return "Error";
case logs::level::todo: return "TODO";
case logs::level::success: return "Success";
case logs::level::warning: return "Warning";
case logs::level::notice: return "Notice";
case logs::level::trace: return "Trace";
}
return unknown;
});
}
namespace logs
{
@ -72,13 +88,10 @@ void logs::listener::add(logs::listener* _new)
}
}
void logs::channel::broadcast(const logs::channel& ch, logs::level sev, const char* fmt...)
void logs::channel::broadcast(const logs::channel& ch, logs::level sev, const char* fmt, const fmt::supplementary_info* sup, const u64* args)
{
va_list args;
va_start(args, fmt);
std::string&& text = fmt::unsafe_vformat(fmt, args);
std::string&& prefix = g_tls_log_prefix ? g_tls_log_prefix() : "";
va_end(args);
std::string text; fmt::raw_append(text, fmt, sup, args);
std::string prefix(g_tls_log_prefix ? g_tls_log_prefix() : "");
// Prepare message information
message msg;

View file

@ -1,7 +1,9 @@
#pragma once
#include "types.h"
#include "Platform.h"
#include "Atomic.h"
#include "StrFmt.h"
namespace logs
{
@ -67,19 +69,17 @@ namespace logs
// Formatting function
template<typename... Args>
void format(level sev, const char* fmt, const Args&... args) const
SAFE_BUFFERS void format(level sev, const char* fmt, const Args&... args) const
{
#ifdef _MSC_VER
if (sev <= enabled)
#else
if (__builtin_expect(sev <= enabled, 0))
#endif
broadcast(*this, sev, fmt, ::unveil<Args>::get(args)...);
if (UNLIKELY(sev <= enabled))
{
broadcast(*this, sev, fmt, fmt::arg_type_info::get<typename fmt_unveil<Args>::type...>(), fmt::args_t<Args...>{::fmt_unveil<Args>::get(args)...});
}
}
#define GEN_LOG_METHOD(_sev)\
template<typename... Args>\
void _sev(const char* fmt, const Args&... args) const\
SAFE_BUFFERS void _sev(const char* fmt, const Args&... args) const\
{\
return format<Args...>(level::_sev, fmt, args...);\
}
@ -95,7 +95,7 @@ namespace logs
#undef GEN_LOG_METHOD
private:
// Send log message to global logger instance
static void broadcast(const channel& ch, level sev, const char* fmt...);
static void broadcast(const channel& ch, level sev, const char*, const fmt::supplementary_info*, const u64*);
};
/* Small set of predefined channels */
@ -110,22 +110,6 @@ namespace logs
extern channel ARMv7;
}
template<>
struct bijective<logs::level, const char*>
{
static constexpr bijective_pair<logs::level, const char*> map[]
{
{ logs::level::always, "Nothing" },
{ logs::level::fatal, "Fatal" },
{ logs::level::error, "Error" },
{ logs::level::todo, "TODO" },
{ logs::level::success, "Success" },
{ logs::level::warning, "Warning" },
{ logs::level::notice, "Notice" },
{ logs::level::trace, "Trace" },
};
};
// Legacy:
#define LOG_SUCCESS(ch, fmt, ...) logs::ch.success(fmt, ##__VA_ARGS__)

View file

@ -61,7 +61,7 @@ public:
Show();
}
force_inline void Update(const u8 thread_id, const u64 value, const wxString& msg)
void Update(const u8 thread_id, const u64 value, const wxString& msg)
{
if(thread_id > m_cores) return;

View file

@ -29,15 +29,21 @@
#endif
#ifdef _MSC_VER
#define never_inline __declspec(noinline)
#define SAFE_BUFFERS __declspec(safebuffers)
#else
#define never_inline __attribute__((noinline))
#define SAFE_BUFFERS
#endif
#ifdef _MSC_VER
#define force_inline __forceinline
#define NEVER_INLINE __declspec(noinline)
#else
#define force_inline __attribute__((always_inline)) inline
#define NEVER_INLINE __attribute__((noinline))
#endif
#ifdef _MSC_VER
#define FORCE_INLINE __forceinline
#else
#define FORCE_INLINE __attribute__((always_inline)) inline
#endif
#if defined(__GNUG__)

View file

@ -1,73 +1,178 @@
#include "StrFmt.h"
#include "BEType.h"
#include "StrUtil.h"
#include "cfmt.h"
#include <cassert>
#include <array>
#include <memory>
#include <algorithm>
std::string v128::to_hex() const
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
{
return fmt::format("%016llx%016llx", _u64[1], _u64[0]);
fmt::append(out, "%p", reinterpret_cast<const void*>(static_cast<std::uintptr_t>(arg)));
}
std::string v128::to_xyzw() const
void fmt_class_string<const char*>::format(std::string& out, u64 arg)
{
return fmt::format("x: %g y: %g z: %g w: %g", _f[3], _f[2], _f[1], _f[0]);
out += reinterpret_cast<const char*>(static_cast<std::uintptr_t>(arg));
}
std::string fmt::unsafe_vformat(const char* fmt, va_list _args) noexcept
template<>
void fmt_class_string<std::string>::format(std::string& out, u64 arg)
{
// Fixed stack buffer for the first attempt
std::array<char, 4096> fixed_buf;
out += get_object(arg).c_str(); // TODO?
}
// Possibly dynamically allocated buffer for the second attempt
std::unique_ptr<char[]> buf;
template<>
void fmt_class_string<std::vector<char>>::format(std::string& out, u64 arg)
{
const std::vector<char>& obj = get_object(arg);
out.append(obj.cbegin(), obj.cend());
}
// Pointer to the current buffer
char* buf_addr = fixed_buf.data();
template<>
void fmt_class_string<char>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%hhx", static_cast<char>(arg));
}
for (std::size_t buf_size = fixed_buf.size();;)
template<>
void fmt_class_string<uchar>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%hhx", static_cast<uchar>(arg));
}
template<>
void fmt_class_string<schar>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%hhx", static_cast<schar>(arg));
}
template<>
void fmt_class_string<short>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%hx", static_cast<short>(arg));
}
template<>
void fmt_class_string<ushort>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%hx", static_cast<ushort>(arg));
}
template<>
void fmt_class_string<int>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%x", static_cast<int>(arg));
}
template<>
void fmt_class_string<uint>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%x", static_cast<uint>(arg));
}
template<>
void fmt_class_string<long>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%lx", static_cast<long>(arg));
}
template<>
void fmt_class_string<ulong>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%lx", static_cast<ulong>(arg));
}
template<>
void fmt_class_string<llong>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%llx", static_cast<llong>(arg));
}
template<>
void fmt_class_string<ullong>::format(std::string& out, u64 arg)
{
fmt::append(out, "0x%llx", static_cast<ullong>(arg));
}
template<>
void fmt_class_string<float>::format(std::string& out, u64 arg)
{
fmt::append(out, "%f", static_cast<float>(reinterpret_cast<f64&>(arg)));
}
template<>
void fmt_class_string<double>::format(std::string& out, u64 arg)
{
fmt::append(out, "%f", reinterpret_cast<f64&>(arg));
}
template<>
void fmt_class_string<bool>::format(std::string& out, u64 arg)
{
out += arg ? "true" : "false"; // TODO?
}
template<>
void fmt_class_string<v128>::format(std::string& out, u64 arg)
{
const v128& vec = get_object(arg);
fmt::append(out, "0x%016llx%016llx", vec._u64[1], vec._u64[0]);
}
namespace fmt
{
struct cfmt_src;
}
// Temporary implementation
struct fmt::cfmt_src
{
const fmt::supplementary_info* sup;
const u64* args;
bool test(std::size_t index = 0)
{
va_list args;
va_copy(args, _args);
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
#endif
const std::size_t len = std::vsnprintf(buf_addr, buf_size, fmt, args);
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
va_end(args);
assert(len <= INT_MAX);
if (len < buf_size)
for (std::size_t i = 0; i <= index; i++)
{
return{ buf_addr, len };
if (!sup[i].fmt_string)
{
return false;
}
}
buf.reset(buf_addr = new char[buf_size = len + 1]);
return true;
}
template<typename T>
T get(std::size_t index = 0)
{
return reinterpret_cast<const T&>(args[index]);
}
void skip(std::size_t extra)
{
++sup += extra;
++args += extra;
}
std::size_t fmt_string(std::string& out)
{
const std::size_t start = out.size();
sup->fmt_string(out, args[0]);
return out.size() - start;
}
};
void fmt::raw_append(std::string& out, const char* fmt, const fmt::supplementary_info* sup, const u64* args) noexcept
{
cfmt_append(out, fmt, cfmt_src{sup, args});
}
std::string fmt::unsafe_format(const char* fmt...) noexcept
char* fmt::alloc_format(const char* fmt, const fmt::supplementary_info* sup, const u64* args) noexcept
{
va_list args;
va_start(args, fmt);
auto result = unsafe_vformat(fmt, args);
va_end(args);
return result;
}
fmt::exception_base::exception_base(const char* fmt...)
: std::runtime_error((va_start(m_args, fmt), unsafe_vformat(fmt, m_args)))
{
va_end(m_args);
std::string str;
raw_append(str, fmt, sup, args);
return static_cast<char*>(std::memcpy(std::malloc(str.size() + 1), str.data(), str.size() + 1));
}
std::string fmt::replace_first(const std::string& src, const std::string& from, const std::string& to)

View file

@ -1,6 +1,5 @@
#pragma once
#include <cstdarg>
#include <exception>
#include <string>
@ -9,44 +8,224 @@
namespace fmt
{
std::string unsafe_format(const char* fmt...) noexcept;
std::string unsafe_vformat(const char*, va_list) noexcept;
// Formatting function
template<typename... Args>
inline std::string format(const char* fmt, const Args&... args)
static std::string format(const char*, const Args&...);
}
template<typename T, typename>
struct fmt_unveil
{
static_assert(sizeof(T) > 0, "fmt_unveil<>: cannot pass forward-declared object");
using type = T;
static inline u64 get(const T& arg)
{
return unsafe_format(fmt, ::unveil<Args>::get(args)...);
return reinterpret_cast<std::uintptr_t>(&arg);
}
};
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);
}
};
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 (TODO?)
static inline u64 get(f64 arg)
{
return reinterpret_cast<u64&>(arg);
}
};
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);
}
};
template<typename T>
struct fmt_unveil<T*, void>
{
using type = const T*;
static inline u64 get(const T* arg)
{
return reinterpret_cast<std::uintptr_t>(arg);
}
};
template<typename T, std::size_t N>
struct fmt_unveil<T[N], void>
{
using type = const T*;
static inline u64 get(const T* arg)
{
return reinterpret_cast<std::uintptr_t>(arg);
}
};
template<>
struct fmt_unveil<b8, void>
{
using type = bool;
static inline u64 get(const b8& value)
{
return fmt_unveil<bool>::get(value);
}
};
// String type format provider, also type classifier (format() called if an argument is formatted as "%s")
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)
{
return *reinterpret_cast<const T*>(static_cast<std::uintptr_t>(arg));
}
// Helper class
class exception_base : public std::runtime_error
// Helper function (safely converts arg to enum value)
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, const char*(*get)(T value))
{
// Helper (there is no other room)
va_list m_args;
const auto value = static_cast<std::underlying_type_t<T>>(arg);
protected:
// Internal formatting constructor
exception_base(const char* fmt...);
};
// Exception type derived from std::runtime_error with formatting constructor
class exception : public exception_base
{
public:
template<typename... Args>
exception(const char* fmt, const Args&... args)
: exception_base(fmt, ::unveil<Args>::get(args)...)
// Check narrowing
if (static_cast<u64>(value) == arg)
{
if (const char* str = get(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));
}
// Helper constant (may be used in format_enum as lambda return value)
static constexpr const char* unknown = nullptr;
};
template<>
struct fmt_class_string<const void*, void>
{
static void format(std::string& out, u64 arg);
};
template<typename T>
struct fmt_class_string<T*, void> : fmt_class_string<const void*, void>
{
// Classify all pointers as const void*
};
template<>
struct fmt_class_string<const char*, void>
{
static void format(std::string& out, u64 arg);
};
template<>
struct fmt_class_string<char*, void> : fmt_class_string<const char*>
{
// Classify char* as const char*
};
namespace fmt
{
// Argument array type (each element generated via fmt_unveil<>)
template<typename... Args>
using args_t = const u64(&&)[sizeof...(Args) + 1];
using supplementary_info = const struct arg_type_info;
struct arg_type_info
{
decltype(&fmt_class_string<int>::format) fmt_string;
template<typename T>
static constexpr arg_type_info make()
{
return arg_type_info
{
&fmt_class_string<T>::format,
};
}
template<typename... Args>
static inline const supplementary_info* get()
{
// Constantly initialized null-terminated list of type-specific information
static constexpr arg_type_info result[sizeof...(Args) + 1]
{
make<Args>()...
};
return result;
}
};
// Narrow cast (similar to gsl::narrow) with exception message formatting
template<typename To, typename From, typename... Args>
inline auto narrow(const char* format_str, const From& value, const Args&... args) -> decltype(static_cast<To>(static_cast<From>(std::declval<To>())))
// Internal formatting function
void raw_append(std::string& out, const char*, const supplementary_info*, const u64*) noexcept;
// Formatting function
template<typename... Args>
static SAFE_BUFFERS void append(std::string& out, const char* fmt, const Args&... args)
{
const auto result = static_cast<To>(value);
if (static_cast<From>(result) != value) throw fmt::exception(format_str, value, args...);
raw_append(out, fmt, arg_type_info::get<typename fmt_unveil<Args>::type...>(), args_t<Args...>{::fmt_unveil<Args>::get(args)...});
}
// Formatting function
template<typename... Args>
static SAFE_BUFFERS std::string format(const char* fmt, const Args&... args)
{
std::string result;
append<Args...>(result, fmt, args...);
return result;
}
// Internal helper function
char* alloc_format(const char*, const supplementary_info*, const u64*) noexcept;
// Exception type with formatting constructor
template<typename Base>
class exception_t : public Base
{
using base = Base;
public:
template<typename... Args>
SAFE_BUFFERS exception_t(const char* fmt, const Args&... args)
: base((fmt = alloc_format(fmt, arg_type_info::get<typename fmt_unveil<Args>::type...>(), args_t<Args...>{::fmt_unveil<Args>::get(args)...})))
{
std::free(const_cast<char*>(fmt));
}
};
// Exception type derived from std::runtime_error with formatting constructor
using exception = exception_t<std::runtime_error>;
}

View file

@ -863,7 +863,7 @@ bool get_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, size_
return true;
}
LOG_ERROR(MEMORY, "get_x64_reg_value(): invalid arguments (reg=%d, d_size=%lld, i_size=%lld)", reg, d_size, i_size);
LOG_ERROR(MEMORY, "get_x64_reg_value(): invalid arguments (reg=%d, d_size=%lld, i_size=%lld)", (u32)reg, d_size, i_size);
return false;
}
@ -882,7 +882,7 @@ bool put_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, u64 v
}
}
LOG_ERROR(MEMORY, "put_x64_reg_value(): invalid destination (reg=%d, d_size=%lld, value=0x%llx)", reg, d_size, value);
LOG_ERROR(MEMORY, "put_x64_reg_value(): invalid destination (reg=%d, d_size=%lld, value=0x%llx)", (u32)reg, d_size, value);
return false;
}
@ -1062,7 +1062,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
if (a_size != 4 || !d_size || !i_size)
{
LOG_ERROR(MEMORY, "Invalid or unsupported instruction (op=%d, reg=%d, d_size=%lld, a_size=0x%llx, i_size=%lld)", op, reg, d_size, a_size, i_size);
LOG_ERROR(MEMORY, "Invalid or unsupported instruction (op=%d, reg=%d, d_size=%lld, a_size=0x%llx, i_size=%lld)", (u32)op, (u32)reg, d_size, a_size, i_size);
report_opcode();
return false;
}
@ -1134,7 +1134,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
case X64OP_STOS:
default:
{
LOG_ERROR(MEMORY, "Invalid or unsupported operation (op=%d, reg=%d, d_size=%lld, i_size=%lld)", op, reg, d_size, i_size);
LOG_ERROR(MEMORY, "Invalid or unsupported operation (op=%d, reg=%d, d_size=%lld, i_size=%lld)", (u32)op, (u32)reg, d_size, i_size);
report_opcode();
return false;
}
@ -1151,7 +1151,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
// write memory using "privileged" access to avoid breaking reservation
if (!d_size || !i_size)
{
LOG_ERROR(MEMORY, "Invalid or unsupported instruction (op=%d, reg=%d, d_size=%lld, a_size=0x%llx, i_size=%lld)", op, reg, d_size, a_size, i_size);
LOG_ERROR(MEMORY, "Invalid or unsupported instruction (op=%d, reg=%d, d_size=%lld, a_size=0x%llx, i_size=%lld)", (u32)op, (u32)reg, d_size, a_size, i_size);
report_opcode();
return false;
}
@ -1165,7 +1165,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
{
if (reg - X64R_XMM0 >= 16)
{
LOG_ERROR(MEMORY, "X64OP_STORE: d_size=16, reg=%d", reg);
LOG_ERROR(MEMORY, "X64OP_STORE: d_size=16, reg=%d", (u32)reg);
return false;
}
@ -1577,7 +1577,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
}
default:
{
LOG_ERROR(MEMORY, "Invalid or unsupported operation (op=%d, reg=%d, d_size=%lld, a_size=0x%llx, i_size=%lld)", op, reg, d_size, a_size, i_size);
LOG_ERROR(MEMORY, "Invalid or unsupported operation (op=%d, reg=%d, d_size=%lld, a_size=0x%llx, i_size=%lld)", (u32)op, (u32)reg, d_size, a_size, i_size);
report_opcode();
return false;
}

431
Utilities/cfmt.h Normal file
View file

@ -0,0 +1,431 @@
#pragma once
#include "types.h"
#include <string>
#include <vector>
/*
C-style format parser. Appends formatted string to `out`, returns number of characters written.
Arguments are provided via `src`. TODO
*/
template<typename Src, typename Size = std::size_t>
Size cfmt_append(std::string& out, const char* fmt, Src&& src)
{
const std::size_t old_size = out.size();
out.reserve(old_size + 992);
struct cfmt_context
{
std::size_t size; // Size of current format sequence
u8 args; // Number of extra args used
u8 type; // Integral type bytesize
bool dot; // Precision enabled
bool left;
bool sign;
bool space;
bool alter;
bool zeros;
uint width;
uint prec;
};
cfmt_context ctx{0};
// Error handling: print untouched sequence, stop further formatting
const auto drop_sequence = [&]
{
out.append(fmt - ctx.size, ctx.size);
ctx.size = -1;
};
// TODO: check overflow
const auto read_decimal = [&](uint result) -> uint
{
while (fmt[0] >= '0' && fmt[0] <= '9')
{
result = result * 10 + (fmt[0] - '0');
fmt++, ctx.size++;
}
return result;
};
// TODO: remove this
const auto fallback = [&]()
{
const std::string _fmt(fmt - ctx.size, fmt);
const u64 arg0 = src.template get<u64>();
const int arg1 = ctx.args >= 1 ? src.template get<int>(1) : 0;
const int arg2 = ctx.args >= 2 ? src.template get<int>(2) : 0;
if (const std::size_t _size = std::snprintf(0, 0, _fmt.c_str(), arg0, arg1, arg2))
{
out.resize(out.size() + _size);
std::snprintf(&out.front() + out.size() - _size, _size + 1, _fmt.c_str(), arg0, arg1, arg2);
}
};
// Single pass over fmt string (null-terminated), TODO: check correct order
while (const char ch = *fmt++) if (ctx.size == 0)
{
if (ch == '%')
{
ctx.size = 1;
}
else
{
out += ch;
}
}
else if (ctx.size == 1 && ch == '%')
{
ctx = {0};
out += ch;
}
else if (ctx.size == -1)
{
out += ch;
}
else switch (ctx.size++, ch)
{
case '-': ctx.left = true; break;
case '+': ctx.sign = true; break;
case ' ': ctx.space = true; break;
case '#': ctx.alter = true; break;
case '0': ctx.zeros = true; break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
ctx.width = read_decimal(ch - '0');
break;
}
case '*':
{
if (!src.test(++ctx.args))
{
drop_sequence();
break;
}
const int warg = src.template get<int>(ctx.args);
ctx.width = std::abs(warg);
ctx.left |= warg < 0;
break;
}
case '.':
{
if (*fmt >= '0' && *fmt <= '9') // TODO: does it allow '0'?
{
ctx.prec = read_decimal(0);
ctx.dot = true;
}
else if (*fmt == '*')
{
if (!src.test(++ctx.args))
{
drop_sequence();
break;
}
fmt++, ctx.size++;
const int parg = src.template get<int>(ctx.args);
ctx.prec = parg;
ctx.dot = parg >= 0;
}
else
{
ctx.prec = 0;
ctx.dot = true;
}
break;
}
case 'h':
{
if (ctx.type)
{
drop_sequence();
}
else if (fmt[0] == 'h')
{
fmt++, ctx.size++;
ctx.type = sizeof(char);
}
else
{
ctx.type = sizeof(short);
}
break;
}
case 'l':
{
if (ctx.type)
{
drop_sequence();
}
else if (fmt[0] == 'l')
{
fmt++, ctx.size++;
ctx.type = sizeof(llong);
}
else
{
ctx.type = sizeof(long);
}
break;
}
case 'z':
{
if (ctx.type)
{
drop_sequence();
}
else
{
ctx.type = sizeof(std::size_t);
}
break;
}
case 'j':
{
if (ctx.type)
{
drop_sequence();
}
else
{
ctx.type = sizeof(std::intmax_t);
}
break;
}
case 't':
{
if (ctx.type)
{
drop_sequence();
}
else
{
ctx.type = sizeof(std::ptrdiff_t);
}
break;
}
case 'c':
{
if (ctx.type || !src.test())
{
drop_sequence();
break;
}
const std::size_t start = out.size();
out += src.template get<char>(0);
if (1 < ctx.width)
{
// Add spaces if necessary
out.insert(start + ctx.left, ctx.width - 1, ' ');
}
src.skip(ctx.args);
ctx = {0};
break;
}
case 's':
{
if (ctx.type || !src.test())
{
drop_sequence();
break;
}
const std::size_t start = out.size();
const std::size_t size1 = src.fmt_string(out);
if (ctx.dot && size1 > ctx.prec)
{
// Shrink if necessary
out.resize(start + ctx.prec);
}
// TODO: how it works if precision and width specified simultaneously?
const std::size_t size2 = out.size() - start;
if (size2 < ctx.width)
{
// Add spaces if necessary
out.insert(ctx.left ? out.size() : start, ctx.width - size2, ' ');
}
src.skip(ctx.args);
ctx = {0};
break;
}
case 'd':
case 'i':
{
if (!src.test())
{
drop_sequence();
break;
}
if (!ctx.type)
{
ctx.type = sizeof(int);
}
fallback(); // TODO
src.skip(ctx.args);
ctx = {0};
break;
}
case 'o':
{
if (!src.test())
{
drop_sequence();
break;
}
if (!ctx.type)
{
ctx.type = sizeof(int);
}
fallback(); // TODO
src.skip(ctx.args);
ctx = {0};
break;
}
case 'x':
case 'X':
{
if (!src.test())
{
drop_sequence();
break;
}
if (!ctx.type)
{
ctx.type = sizeof(int);
}
fallback(); // TODO
src.skip(ctx.args);
ctx = {0};
break;
}
case 'u':
{
if (!src.test())
{
drop_sequence();
break;
}
if (!ctx.type)
{
ctx.type = sizeof(int);
}
fallback(); // TODO
src.skip(ctx.args);
ctx = {0};
break;
}
case 'p':
{
if (!src.test())
{
drop_sequence();
break;
}
if (ctx.type)
{
drop_sequence();
break;
}
fallback(); // TODO
src.skip(ctx.args);
ctx = {0};
break;
}
case 'f':
case 'F':
case 'e':
case 'E':
case 'a':
case 'A':
case 'g':
case 'G':
{
if (!src.test())
{
drop_sequence();
break;
}
if (ctx.type)
{
drop_sequence();
break;
}
fallback(); // TODO
src.skip(ctx.args);
ctx = {0};
break;
}
case 'L': // long double, not supported
case 'n': // writeback, not supported
default:
{
drop_sequence();
}
}
// Handle unfinished sequence
if (ctx.size && ctx.size != -1)
{
fmt--, drop_sequence();
}
return static_cast<Size>(out.size() - old_size);
}

View file

@ -27,12 +27,13 @@ namespace gsl
enum class byte : u8;
}
template<typename T1, typename T2>
struct bijective_pair
namespace fmt
{
T1 v1;
T2 v2;
};
}
// Formatting helper, type-specific preprocessing for improving safety and functionality
template<typename T, typename = void>
struct fmt_unveil;
template<typename T, std::size_t Align = alignof(T), std::size_t Size = sizeof(T)>
struct se_storage;
@ -40,10 +41,6 @@ struct se_storage;
template<typename T, bool Se = true, std::size_t Align = alignof(T)>
class se_t;
// Specialization with static constexpr bijective_pair<T1, T2> map[] member expected
template<typename T1, typename T2>
struct bijective;
template<typename T, std::size_t Size = sizeof(T)>
struct atomic_storage;
@ -501,40 +498,6 @@ struct multicast<void>
}
};
template<typename T1, typename T2 = const char*, typename T = T1, typename DT = T2>
T2 bijective_find(const T& left, const DT& def = {})
{
for (std::size_t i = 0; i < sizeof(bijective<T1, T2>::map) / sizeof(bijective_pair<T1, T2>); i++)
{
if (bijective<T1, T2>::map[i].v1 == left)
{
return bijective<T1, T2>::map[i].v2;
}
}
return def;
}
// Formatting helper, type-specific preprocessing for improving safety and functionality
template<typename T, typename = void>
struct unveil
{
// TODO
static inline const T& get(const T& arg)
{
return arg;
}
};
template<typename T>
struct unveil<T, void_t<decltype(std::declval<T>().c_str())>>
{
static inline const char* get(const T& arg)
{
return arg.c_str();
}
};
// Tagged ID type
template<typename T = void, typename ID = u32>
class id_value
@ -560,3 +523,14 @@ public:
return m_value;
}
};
template<typename T, typename ID>
struct fmt_unveil<id_value<T, ID>>
{
using type = typename fmt_unveil<ID>::type;
static inline u64 get(const id_value<T, ID>& value)
{
return fmt_unveil<ID>::get(value);
}
};