mirror of https://github.com/AxioDL/logvisor.git
Update fmt to 8.0.0; drop support for wchar
Adds some boost-nowide headers to assist in UTF-8/16 conversion for Windows API.
This commit is contained in:
parent
274ad5ef07
commit
df3c283186
|
@ -10,11 +10,11 @@ include (CMakePackageConfigHelpers)
|
|||
if (NOT TARGET fmt)
|
||||
add_subdirectory(fmt)
|
||||
target_compile_definitions(fmt PUBLIC
|
||||
FMT_STRING_ALIAS=1
|
||||
FMT_ARM_ABI_COMPATIBILITY=1
|
||||
FMT_EXCEPTIONS=0)
|
||||
target_compile_definitions(fmt INTERFACE
|
||||
FMT_ENFORCE_COMPILE_STRING=1)
|
||||
FMT_ENFORCE_COMPILE_STRING=1
|
||||
FMT_UNICODE=1)
|
||||
set(FMT_LIB fmt)
|
||||
endif ()
|
||||
|
||||
|
|
2
fmt
2
fmt
|
@ -1 +1 @@
|
|||
Subproject commit 95da4847274c42c110fd5bf1bf61d3f94be4a08e
|
||||
Subproject commit 9e8b86fd2d9806672cc73133d21780dd182bfd24
|
|
@ -21,10 +21,6 @@ namespace logvisor {
|
|||
|
||||
[[noreturn]] void logvisorAbort();
|
||||
|
||||
#if _WIN32 && UNICODE
|
||||
#define LOG_UCS2 1
|
||||
#endif
|
||||
|
||||
/* True if ANSI color available */
|
||||
extern bool XtermColor;
|
||||
|
||||
|
@ -48,11 +44,8 @@ public:
|
|||
ILogger(uint64_t typeHash) : m_typeHash(typeHash) {}
|
||||
virtual ~ILogger() = default;
|
||||
virtual void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) = 0;
|
||||
virtual void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) = 0;
|
||||
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
|
||||
fmt::string_view format, fmt::format_args args) = 0;
|
||||
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
|
||||
fmt::wstring_view format, fmt::wformat_args args) = 0;
|
||||
|
||||
[[nodiscard]] uint64_t getTypeId() const { return m_typeHash; }
|
||||
};
|
||||
|
@ -106,7 +99,7 @@ struct LogMutex {
|
|||
if (enabled)
|
||||
return std::unique_lock<std::recursive_mutex>(mutex);
|
||||
else
|
||||
return std::unique_lock<std::recursive_mutex>();
|
||||
return {};
|
||||
}
|
||||
};
|
||||
extern LogMutex _LogMutex;
|
||||
|
@ -168,18 +161,6 @@ void RegisterSentry(const char* appName, const char* appVersion, const char* cac
|
|||
void CreateWin32Console();
|
||||
#endif
|
||||
|
||||
#if LOG_UCS2
|
||||
|
||||
/**
|
||||
* @brief Construct and register a file logger (wchar_t version)
|
||||
* @param filepath Path to write the file
|
||||
*
|
||||
* If there's already a file logger registered to the same file, this is a no-op.
|
||||
*/
|
||||
void RegisterFileLogger(const wchar_t* filepath);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief This is constructed per-subsystem in a locally centralized fashion
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
//
|
||||
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_ARGS_HPP_INCLUDED
|
||||
#define NOWIDE_ARGS_HPP_INCLUDED
|
||||
|
||||
#include <nowide/config.hpp>
|
||||
#ifdef NOWIDE_WINDOWS
|
||||
#include <nowide/stackstring.hpp>
|
||||
#include <nowide/windows.hpp>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
namespace nowide {
|
||||
#if !defined(NOWIDE_WINDOWS) && !defined(NOWIDE_DOXYGEN)
|
||||
class args
|
||||
{
|
||||
public:
|
||||
args(int&, char**&)
|
||||
{}
|
||||
args(int&, char**&, char**&)
|
||||
{}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
///
|
||||
/// \brief \c args is a class that temporarily replaces standard main() function arguments with their
|
||||
/// equal, but UTF-8 encoded values under Microsoft Windows for the lifetime of the instance.
|
||||
///
|
||||
/// The class uses \c GetCommandLineW(), \c CommandLineToArgvW() and \c GetEnvironmentStringsW()
|
||||
/// in order to obtain Unicode-encoded values.
|
||||
/// It does not relate to actual values of argc, argv and env under Windows.
|
||||
///
|
||||
/// It restores the original values in its destructor (usually at the end of the \c main function).
|
||||
///
|
||||
/// If any of the system calls fails, an exception of type std::runtime_error will be thrown
|
||||
/// and argc, argv, env remain unchanged.
|
||||
///
|
||||
/// \note The class owns the memory of the newly allocated strings.
|
||||
/// So you need to keep it alive as long as you use the values.
|
||||
///
|
||||
/// Usage:
|
||||
/// \code
|
||||
/// int main(int argc, char** argv, char** env) {
|
||||
/// nowide::args _(argc, argv, env); // Note the _ as a "don't care" name for the instance
|
||||
/// // Use argv and env as usual, they are now UTF-8 encoded on Windows
|
||||
/// return 0; // Memory held by args is released
|
||||
/// }
|
||||
/// \endcode
|
||||
class args
|
||||
{
|
||||
public:
|
||||
///
|
||||
/// Fix command line arguments
|
||||
///
|
||||
args(int& argc, char**& argv) :
|
||||
old_argc_(argc), old_argv_(argv), old_env_(0), old_argc_ptr_(&argc), old_argv_ptr_(&argv), old_env_ptr_(0)
|
||||
{
|
||||
fix_args(argc, argv);
|
||||
}
|
||||
///
|
||||
/// Fix command line arguments and environment
|
||||
///
|
||||
args(int& argc, char**& argv, char**& env) :
|
||||
old_argc_(argc), old_argv_(argv), old_env_(env), old_argc_ptr_(&argc), old_argv_ptr_(&argv),
|
||||
old_env_ptr_(&env)
|
||||
{
|
||||
fix_args(argc, argv);
|
||||
fix_env(env);
|
||||
}
|
||||
///
|
||||
/// Restore original argc, argv, env values, if changed
|
||||
///
|
||||
~args()
|
||||
{
|
||||
if(old_argc_ptr_)
|
||||
*old_argc_ptr_ = old_argc_;
|
||||
if(old_argv_ptr_)
|
||||
*old_argv_ptr_ = old_argv_;
|
||||
if(old_env_ptr_)
|
||||
*old_env_ptr_ = old_env_;
|
||||
}
|
||||
|
||||
private:
|
||||
class wargv_ptr
|
||||
{
|
||||
wchar_t** p;
|
||||
int argc;
|
||||
|
||||
public:
|
||||
wargv_ptr()
|
||||
{
|
||||
p = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
}
|
||||
~wargv_ptr()
|
||||
{
|
||||
if(p)
|
||||
LocalFree(p);
|
||||
}
|
||||
wargv_ptr(const wargv_ptr&) = delete;
|
||||
wargv_ptr& operator=(const wargv_ptr&) = delete;
|
||||
|
||||
int size() const
|
||||
{
|
||||
return argc;
|
||||
}
|
||||
operator bool() const
|
||||
{
|
||||
return p != NULL;
|
||||
}
|
||||
const wchar_t* operator[](size_t i) const
|
||||
{
|
||||
return p[i];
|
||||
}
|
||||
};
|
||||
class wenv_ptr
|
||||
{
|
||||
wchar_t* p;
|
||||
|
||||
public:
|
||||
wenv_ptr() : p(GetEnvironmentStringsW())
|
||||
{}
|
||||
~wenv_ptr()
|
||||
{
|
||||
if(p)
|
||||
FreeEnvironmentStringsW(p);
|
||||
}
|
||||
wenv_ptr(const wenv_ptr&) = delete;
|
||||
wenv_ptr& operator=(const wenv_ptr&) = delete;
|
||||
|
||||
operator const wchar_t*() const
|
||||
{
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
void fix_args(int& argc, char**& argv)
|
||||
{
|
||||
const wargv_ptr wargv;
|
||||
if(!wargv)
|
||||
throw std::runtime_error("Could not get command line!");
|
||||
args_.resize(wargv.size() + 1, 0);
|
||||
arg_values_.resize(wargv.size());
|
||||
for(int i = 0; i < wargv.size(); i++)
|
||||
args_[i] = arg_values_[i].convert(wargv[i]);
|
||||
argc = wargv.size();
|
||||
argv = &args_[0];
|
||||
}
|
||||
void fix_env(char**& env)
|
||||
{
|
||||
const wenv_ptr wstrings;
|
||||
if(!wstrings)
|
||||
throw std::runtime_error("Could not get environment strings!");
|
||||
const wchar_t* wstrings_end = 0;
|
||||
int count = 0;
|
||||
for(wstrings_end = wstrings; *wstrings_end; wstrings_end += wcslen(wstrings_end) + 1)
|
||||
count++;
|
||||
env_.convert(wstrings, wstrings_end);
|
||||
envp_.resize(count + 1, 0);
|
||||
char* p = env_.get();
|
||||
int pos = 0;
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
if(*p != '=')
|
||||
envp_[pos++] = p;
|
||||
p += strlen(p) + 1;
|
||||
}
|
||||
env = &envp_[0];
|
||||
}
|
||||
|
||||
std::vector<char*> args_;
|
||||
std::vector<short_stackstring> arg_values_;
|
||||
stackstring env_;
|
||||
std::vector<char*> envp_;
|
||||
|
||||
int old_argc_;
|
||||
char** old_argv_;
|
||||
char** old_env_;
|
||||
|
||||
int* old_argc_ptr_;
|
||||
char*** old_argv_ptr_;
|
||||
char*** old_env_ptr_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace nowide
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
|
||||
// Copyright (c) 2020 Alexander Grund
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_CONFIG_HPP_INCLUDED
|
||||
#define NOWIDE_CONFIG_HPP_INCLUDED
|
||||
|
||||
#include <nowide/replacement.hpp>
|
||||
|
||||
#if(defined(__WIN32) || defined(_WIN32) || defined(WIN32)) && !defined(__CYGWIN__)
|
||||
#define NOWIDE_WINDOWS
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define NOWIDE_MSVC _MSC_VER
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define NOWIDE_SYMBOL_VISIBLE __attribute__((__visibility__("default")))
|
||||
#endif
|
||||
|
||||
#ifndef NOWIDE_SYMBOL_VISIBLE
|
||||
#define NOWIDE_SYMBOL_VISIBLE
|
||||
#endif
|
||||
|
||||
#ifdef NOWIDE_WINDOWS
|
||||
#define NOWIDE_SYMBOL_EXPORT __declspec(dllexport)
|
||||
#define NOWIDE_SYMBOL_IMPORT __declspec(dllimport)
|
||||
#else
|
||||
#define NOWIDE_SYMBOL_EXPORT NOWIDE_SYMBOL_VISIBLE
|
||||
#define NOWIDE_SYMBOL_IMPORT
|
||||
#endif
|
||||
|
||||
#if defined(NOWIDE_DYN_LINK)
|
||||
#ifdef NOWIDE_SOURCE
|
||||
#define NOWIDE_DECL NOWIDE_SYMBOL_EXPORT
|
||||
#else
|
||||
#define NOWIDE_DECL NOWIDE_SYMBOL_IMPORT
|
||||
#endif // NOWIDE_SOURCE
|
||||
#else
|
||||
#define NOWIDE_DECL
|
||||
#endif // NOWIDE_DYN_LINK
|
||||
|
||||
#ifndef NOWIDE_DECL
|
||||
#define NOWIDE_DECL
|
||||
#endif
|
||||
|
||||
#if defined(NOWIDE_WINDOWS)
|
||||
#ifdef NOWIDE_USE_FILEBUF_REPLACEMENT
|
||||
#undef NOWIDE_USE_FILEBUF_REPLACEMENT
|
||||
#endif
|
||||
#define NOWIDE_USE_FILEBUF_REPLACEMENT 1
|
||||
#elif !defined(NOWIDE_USE_FILEBUF_REPLACEMENT)
|
||||
#define NOWIDE_USE_FILEBUF_REPLACEMENT 0
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 7
|
||||
#define NOWIDE_FALLTHROUGH __attribute__((fallthrough))
|
||||
#else
|
||||
#define NOWIDE_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#if defined __GNUC__
|
||||
#define NOWIDE_LIKELY(x) __builtin_expect(x, 1)
|
||||
#define NOWIDE_UNLIKELY(x) __builtin_expect(x, 0)
|
||||
#else
|
||||
#if !defined(NOWIDE_LIKELY)
|
||||
#define NOWIDE_LIKELY(x) x
|
||||
#endif
|
||||
#if !defined(NOWIDE_UNLIKELY)
|
||||
#define NOWIDE_UNLIKELY(x) x
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
|
||||
// Copyright (c) 2020 Alexander Grund
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_CONVERT_HPP_INCLUDED
|
||||
#define NOWIDE_CONVERT_HPP_INCLUDED
|
||||
|
||||
#include <nowide/detail/is_string_container.hpp>
|
||||
#include <nowide/utf/convert.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace nowide {
|
||||
|
||||
///
|
||||
/// Convert wide string (UTF-16/32) in range [begin,end) to NULL terminated narrow string (UTF-8)
|
||||
/// stored in \a output of size \a output_size (including NULL)
|
||||
///
|
||||
/// If there is not enough room NULL is returned, else output is returned.
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
inline char* narrow(char* output, size_t output_size, const wchar_t* begin, const wchar_t* end)
|
||||
{
|
||||
return utf::convert_buffer(output, output_size, begin, end);
|
||||
}
|
||||
///
|
||||
/// Convert NULL terminated wide string (UTF-16/32) to NULL terminated narrow string (UTF-8)
|
||||
/// stored in \a output of size \a output_size (including NULL)
|
||||
///
|
||||
/// If there is not enough room NULL is returned, else output is returned.
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
inline char* narrow(char* output, size_t output_size, const wchar_t* source)
|
||||
{
|
||||
return narrow(output, output_size, source, source + utf::strlen(source));
|
||||
}
|
||||
|
||||
///
|
||||
/// Convert narrow string (UTF-8) in range [begin,end) to NULL terminated wide string (UTF-16/32)
|
||||
/// stored in \a output of size \a output_size (including NULL)
|
||||
///
|
||||
/// If there is not enough room NULL is returned, else output is returned.
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
inline wchar_t* widen(wchar_t* output, size_t output_size, const char* begin, const char* end)
|
||||
{
|
||||
return utf::convert_buffer(output, output_size, begin, end);
|
||||
}
|
||||
///
|
||||
/// Convert NULL terminated narrow string (UTF-8) to NULL terminated wide string (UTF-16/32)
|
||||
/// most output_size (including NULL)
|
||||
///
|
||||
/// If there is not enough room NULL is returned, else output is returned.
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
inline wchar_t* widen(wchar_t* output, size_t output_size, const char* source)
|
||||
{
|
||||
return widen(output, output_size, source, source + utf::strlen(source));
|
||||
}
|
||||
|
||||
///
|
||||
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
|
||||
///
|
||||
/// \param s Input string
|
||||
/// \param count Number of characters to convert
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
template<typename T_Char, typename = detail::requires_wide_char<T_Char>>
|
||||
inline std::string narrow(const T_Char* s, size_t count)
|
||||
{
|
||||
return utf::convert_string<char>(s, s + count);
|
||||
}
|
||||
///
|
||||
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
|
||||
///
|
||||
/// \param s NULL terminated input string
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
template<typename T_Char, typename = detail::requires_wide_char<T_Char>>
|
||||
inline std::string narrow(const T_Char* s)
|
||||
{
|
||||
return narrow(s, utf::strlen(s));
|
||||
}
|
||||
///
|
||||
/// Convert wide string (UTF-16/32) to narrow string (UTF-8).
|
||||
///
|
||||
/// \param s Input string
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
template<typename StringOrStringView, typename = detail::requires_wide_string_container<StringOrStringView>>
|
||||
inline std::string narrow(const StringOrStringView& s)
|
||||
{
|
||||
return utf::convert_string<char>(s.data(), s.data() + s.size());
|
||||
}
|
||||
|
||||
///
|
||||
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
|
||||
///
|
||||
/// \param s Input string
|
||||
/// \param count Number of characters to convert
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
template<typename T_Char, typename = detail::requires_narrow_char<T_Char>>
|
||||
inline std::wstring widen(const T_Char* s, size_t count)
|
||||
{
|
||||
return utf::convert_string<wchar_t>(s, s + count);
|
||||
}
|
||||
///
|
||||
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
|
||||
///
|
||||
/// \param s NULL terminated input string
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
template<typename T_Char, typename = detail::requires_narrow_char<T_Char>>
|
||||
inline std::wstring widen(const T_Char* s)
|
||||
{
|
||||
return widen(s, utf::strlen(s));
|
||||
}
|
||||
///
|
||||
/// Convert narrow string (UTF-8) to wide string (UTF-16/32).
|
||||
///
|
||||
/// \param s Input string
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
template<typename StringOrStringView, typename = detail::requires_narrow_string_container<StringOrStringView>>
|
||||
inline std::wstring widen(const StringOrStringView& s)
|
||||
{
|
||||
return utf::convert_string<wchar_t>(s.data(), s.data() + s.size());
|
||||
}
|
||||
} // namespace nowide
|
||||
|
||||
#endif
|
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// Copyright (c) 2020 Alexander Grund
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_DETAIL_IS_STRING_CONTAINER_HPP_INCLUDED
|
||||
#define NOWIDE_DETAIL_IS_STRING_CONTAINER_HPP_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace nowide {
|
||||
namespace detail {
|
||||
template<class...>
|
||||
struct make_void
|
||||
{
|
||||
typedef void type;
|
||||
};
|
||||
|
||||
template<class... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template<typename T>
|
||||
struct is_char_type : std::false_type
|
||||
{};
|
||||
template<>
|
||||
struct is_char_type<char> : std::true_type
|
||||
{};
|
||||
template<>
|
||||
struct is_char_type<wchar_t> : std::true_type
|
||||
{};
|
||||
template<>
|
||||
struct is_char_type<char16_t> : std::true_type
|
||||
{};
|
||||
template<>
|
||||
struct is_char_type<char32_t> : std::true_type
|
||||
{};
|
||||
#ifdef __cpp_char8_t
|
||||
template<>
|
||||
struct is_char_type<char8_t> : std::true_type
|
||||
{};
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
struct is_c_string : std::false_type
|
||||
{};
|
||||
template<typename T>
|
||||
struct is_c_string<const T*> : is_char_type<T>
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
using const_data_result = decltype(std::declval<const T>().data());
|
||||
/// Return the size of the char type returned by the data() member function
|
||||
template<typename T>
|
||||
using get_data_width =
|
||||
std::integral_constant<std::size_t, sizeof(typename std::remove_pointer<const_data_result<T>>::type)>;
|
||||
template<typename T>
|
||||
using size_result = decltype(std::declval<T>().size());
|
||||
/// Return true if the data() member function returns a pointer to a type of size 1
|
||||
template<typename T>
|
||||
using has_narrow_data = std::integral_constant<bool, (get_data_width<T>::value == 1)>;
|
||||
|
||||
/// Return true if T is a string container, e.g. std::basic_string, std::basic_string_view
|
||||
/// Requires a static value `npos`, a member function `size()` returning an integral,
|
||||
/// and a member function `data()` returning a C string
|
||||
template<typename T, bool isNarrow, typename = void>
|
||||
struct is_string_container : std::false_type
|
||||
{};
|
||||
// clang-format off
|
||||
template<typename T, bool isNarrow>
|
||||
struct is_string_container<T, isNarrow, void_t<decltype(T::npos), size_result<T>, const_data_result<T>>>
|
||||
: std::integral_constant<bool,
|
||||
std::is_integral<decltype(T::npos)>::value
|
||||
&& std::is_integral<size_result<T>>::value
|
||||
&& is_c_string<const_data_result<T>>::value
|
||||
&& isNarrow == has_narrow_data<T>::value>
|
||||
{};
|
||||
// clang-format on
|
||||
template<typename T>
|
||||
using requires_narrow_string_container = typename std::enable_if<is_string_container<T, true>::value>::type;
|
||||
template<typename T>
|
||||
using requires_wide_string_container = typename std::enable_if<is_string_container<T, false>::value>::type;
|
||||
|
||||
template<typename T>
|
||||
using requires_narrow_char = typename std::enable_if<sizeof(T) == 1 && is_char_type<T>::value>::type;
|
||||
template<typename T>
|
||||
using requires_wide_char = typename std::enable_if<(sizeof(T) > 1) && is_char_type<T>::value>::type;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace nowide
|
||||
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Copyright (c) 2018 Artyom Beilis (Tonkikh)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_REPLACEMENT_HPP_INCLUDED
|
||||
#define NOWIDE_REPLACEMENT_HPP_INCLUDED
|
||||
|
||||
/// @file
|
||||
|
||||
/// \def NOWIDE_REPLACEMENT_CHARACTER
|
||||
/// Unicode character to be used to replace invalid UTF-8 sequences
|
||||
#ifndef NOWIDE_REPLACEMENT_CHARACTER
|
||||
#define NOWIDE_REPLACEMENT_CHARACTER 0xFFFD
|
||||
#endif
|
||||
|
||||
#endif // boost/nowide/replacement.hpp
|
|
@ -0,0 +1,213 @@
|
|||
//
|
||||
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_STACKSTRING_HPP_INCLUDED
|
||||
#define NOWIDE_STACKSTRING_HPP_INCLUDED
|
||||
|
||||
#include <nowide/convert.hpp>
|
||||
#include <nowide/utf/utf.hpp>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
namespace nowide {
|
||||
|
||||
///
|
||||
/// \brief A class that allows to create a temporary wide or narrow UTF strings from
|
||||
/// wide or narrow UTF source.
|
||||
///
|
||||
/// It uses a stack buffer if the string is short enough
|
||||
/// otherwise allocates a buffer on the heap.
|
||||
///
|
||||
/// Invalid UTF characters are replaced by the substitution character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
///
|
||||
/// If a NULL pointer is passed to the constructor or convert method, NULL will be returned by c_str.
|
||||
/// Similarily a default constructed stackstring will return NULL on calling c_str.
|
||||
///
|
||||
template<typename CharOut = wchar_t, typename CharIn = char, size_t BufferSize = 256>
|
||||
class basic_stackstring
|
||||
{
|
||||
public:
|
||||
/// Size of the stack buffer
|
||||
static const size_t buffer_size = BufferSize;
|
||||
/// Type of the output character (converted to)
|
||||
using output_char = CharOut;
|
||||
/// Type of the input character (converted from)
|
||||
using input_char = CharIn;
|
||||
|
||||
/// Creates a NULL stackstring
|
||||
basic_stackstring() : data_(NULL)
|
||||
{
|
||||
buffer_[0] = 0;
|
||||
}
|
||||
/// Convert the NULL terminated string input and store in internal buffer
|
||||
/// If input is NULL, nothing will be stored
|
||||
explicit basic_stackstring(const input_char* input) : data_(NULL)
|
||||
{
|
||||
convert(input);
|
||||
}
|
||||
explicit basic_stackstring(std::string_view input) : data_(NULL) {
|
||||
convert(input.data(), input.data() + input.size());
|
||||
}
|
||||
/// Convert the sequence [begin, end) and store in internal buffer
|
||||
/// If begin is NULL, nothing will be stored
|
||||
basic_stackstring(const input_char* begin, const input_char* end) : data_(NULL)
|
||||
{
|
||||
convert(begin, end);
|
||||
}
|
||||
/// Copy construct from other
|
||||
basic_stackstring(const basic_stackstring& other) : data_(NULL)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
/// Copy assign from other
|
||||
basic_stackstring& operator=(const basic_stackstring& other)
|
||||
{
|
||||
if(this != &other)
|
||||
{
|
||||
clear();
|
||||
const size_t len = other.length();
|
||||
if(other.uses_stack_memory())
|
||||
data_ = buffer_;
|
||||
else if(other.data_)
|
||||
data_ = new output_char[len + 1];
|
||||
else
|
||||
{
|
||||
data_ = NULL;
|
||||
return *this;
|
||||
}
|
||||
std::memcpy(data_, other.data_, sizeof(output_char) * (len + 1));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~basic_stackstring()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
/// Convert the NULL terminated string input and store in internal buffer
|
||||
/// If input is NULL, the current buffer will be reset to NULL
|
||||
output_char* convert(const input_char* input)
|
||||
{
|
||||
if(input)
|
||||
return convert(input, input + utf::strlen(input));
|
||||
clear();
|
||||
return get();
|
||||
}
|
||||
/// Convert the sequence [begin, end) and store in internal buffer
|
||||
/// If begin is NULL, the current buffer will be reset to NULL
|
||||
output_char* convert(const input_char* begin, const input_char* end)
|
||||
{
|
||||
clear();
|
||||
|
||||
if(begin)
|
||||
{
|
||||
const size_t input_len = end - begin;
|
||||
// Minimum size required: 1 output char per input char + trailing NULL
|
||||
const size_t min_output_size = input_len + 1;
|
||||
// If there is a chance the converted string fits on stack, try it
|
||||
if(min_output_size <= buffer_size && utf::convert_buffer(buffer_, buffer_size, begin, end))
|
||||
data_ = buffer_;
|
||||
else
|
||||
{
|
||||
// Fallback: Allocate a buffer that is surely large enough on heap
|
||||
// Max size: Every input char is transcoded to the output char with maximum with + trailing NULL
|
||||
const size_t max_output_size = input_len * utf::utf_traits<output_char>::max_width + 1;
|
||||
data_ = new output_char[max_output_size];
|
||||
const bool success = utf::convert_buffer(data_, max_output_size, begin, end) == data_;
|
||||
assert(success);
|
||||
(void)success;
|
||||
}
|
||||
}
|
||||
return get();
|
||||
}
|
||||
/// Return the converted, NULL-terminated string or NULL if no string was converted
|
||||
output_char* get()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
/// Return the converted, NULL-terminated string or NULL if no string was converted
|
||||
const output_char* get() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
/// Reset the internal buffer to NULL
|
||||
void clear()
|
||||
{
|
||||
if(!uses_stack_memory())
|
||||
delete[] data_;
|
||||
data_ = NULL;
|
||||
}
|
||||
/// Swap lhs with rhs
|
||||
friend void swap(basic_stackstring& lhs, basic_stackstring& rhs)
|
||||
{
|
||||
if(lhs.uses_stack_memory())
|
||||
{
|
||||
if(rhs.uses_stack_memory())
|
||||
{
|
||||
for(size_t i = 0; i < buffer_size; i++)
|
||||
std::swap(lhs.buffer_[i], rhs.buffer_[i]);
|
||||
} else
|
||||
{
|
||||
lhs.data_ = rhs.data_;
|
||||
rhs.data_ = rhs.buffer_;
|
||||
for(size_t i = 0; i < buffer_size; i++)
|
||||
rhs.buffer_[i] = lhs.buffer_[i];
|
||||
}
|
||||
} else if(rhs.uses_stack_memory())
|
||||
{
|
||||
rhs.data_ = lhs.data_;
|
||||
lhs.data_ = lhs.buffer_;
|
||||
for(size_t i = 0; i < buffer_size; i++)
|
||||
lhs.buffer_[i] = rhs.buffer_[i];
|
||||
} else
|
||||
std::swap(lhs.data_, rhs.data_);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// True if the stack memory is used
|
||||
bool uses_stack_memory() const
|
||||
{
|
||||
return data_ == buffer_;
|
||||
}
|
||||
/// Return the current length of the string excluding the NULL terminator
|
||||
/// If NULL is stored returns NULL
|
||||
size_t length() const
|
||||
{
|
||||
if(!data_)
|
||||
return 0;
|
||||
size_t len = 0;
|
||||
while(data_[len])
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
private:
|
||||
output_char buffer_[buffer_size];
|
||||
output_char* data_;
|
||||
}; // basic_stackstring
|
||||
|
||||
///
|
||||
/// Convenience typedef
|
||||
///
|
||||
using wstackstring = basic_stackstring<wchar_t, char, 256>;
|
||||
///
|
||||
/// Convenience typedef
|
||||
///
|
||||
using stackstring = basic_stackstring<char, wchar_t, 256>;
|
||||
///
|
||||
/// Convenience typedef
|
||||
///
|
||||
using wshort_stackstring = basic_stackstring<wchar_t, char, 16>;
|
||||
///
|
||||
/// Convenience typedef
|
||||
///
|
||||
using short_stackstring = basic_stackstring<char, wchar_t, 16>;
|
||||
|
||||
} // namespace nowide
|
||||
|
||||
#endif
|
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
|
||||
// Copyright (c) 2020 Alexander Grund
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_UTF_CONVERT_HPP_INCLUDED
|
||||
#define NOWIDE_UTF_CONVERT_HPP_INCLUDED
|
||||
|
||||
#include <nowide/detail/is_string_container.hpp>
|
||||
#include <nowide/replacement.hpp>
|
||||
#include <nowide/utf/utf.hpp>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
|
||||
namespace nowide {
|
||||
namespace utf {
|
||||
|
||||
/// Return the length of the given string in code units.
|
||||
/// That is the number of elements of type Char until the first NULL character.
|
||||
/// Equivalent to `std::strlen(s)` but can handle wide-strings
|
||||
template<typename Char>
|
||||
size_t strlen(const Char* s)
|
||||
{
|
||||
const Char* end = s;
|
||||
while(*end)
|
||||
end++;
|
||||
return end - s;
|
||||
}
|
||||
|
||||
/// Convert a buffer of UTF sequences in the range [source_begin, source_end)
|
||||
/// from \a CharIn to \a CharOut to the output \a buffer of size \a buffer_size.
|
||||
///
|
||||
/// \return original buffer containing the NULL terminated string or NULL
|
||||
///
|
||||
/// If there is not enough room in the buffer NULL is returned, and the content of the buffer is undefined.
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
template<typename CharOut, typename CharIn>
|
||||
CharOut*
|
||||
convert_buffer(CharOut* buffer, size_t buffer_size, const CharIn* source_begin, const CharIn* source_end)
|
||||
{
|
||||
CharOut* rv = buffer;
|
||||
if(buffer_size == 0)
|
||||
return nullptr;
|
||||
buffer_size--;
|
||||
while(source_begin != source_end)
|
||||
{
|
||||
code_point c = utf_traits<CharIn>::decode(source_begin, source_end);
|
||||
if(c == illegal || c == incomplete)
|
||||
{
|
||||
c = NOWIDE_REPLACEMENT_CHARACTER;
|
||||
}
|
||||
size_t width = utf_traits<CharOut>::width(c);
|
||||
if(buffer_size < width)
|
||||
{
|
||||
rv = NULL;
|
||||
break;
|
||||
}
|
||||
buffer = utf_traits<CharOut>::encode(c, buffer);
|
||||
buffer_size -= width;
|
||||
}
|
||||
*buffer++ = 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/// Convert the UTF sequences in range [begin, end) from \a CharIn to \a CharOut
|
||||
/// and return it as a string
|
||||
///
|
||||
/// Any illegal sequences are replaced with the replacement character, see #NOWIDE_REPLACEMENT_CHARACTER
|
||||
/// \tparam CharOut Output character type
|
||||
template<typename CharOut, typename CharIn>
|
||||
std::basic_string<CharOut> convert_string(const CharIn* begin, const CharIn* end)
|
||||
{
|
||||
std::basic_string<CharOut> result;
|
||||
result.reserve(end - begin);
|
||||
using inserter_type = std::back_insert_iterator<std::basic_string<CharOut>>;
|
||||
inserter_type inserter(result);
|
||||
code_point c;
|
||||
while(begin != end)
|
||||
{
|
||||
c = utf_traits<CharIn>::decode(begin, end);
|
||||
if(c == illegal || c == incomplete)
|
||||
{
|
||||
c = NOWIDE_REPLACEMENT_CHARACTER;
|
||||
}
|
||||
utf_traits<CharOut>::encode(c, inserter);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace utf
|
||||
} // namespace nowide
|
||||
|
||||
#endif
|
|
@ -0,0 +1,450 @@
|
|||
//
|
||||
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
|
||||
// Copyright (c) 2020 Alexander Grund
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_UTF_HPP_INCLUDED
|
||||
#define NOWIDE_UTF_HPP_INCLUDED
|
||||
|
||||
#include <nowide/config.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace nowide {
|
||||
///
|
||||
/// \brief Namespace that holds basic operations on UTF encoded sequences
|
||||
///
|
||||
/// All functions defined in this namespace do not require linking with Boost.Nowide library.
|
||||
/// Extracted from Boost.Locale
|
||||
///
|
||||
namespace utf {
|
||||
|
||||
///
|
||||
/// \brief The integral type that can hold a Unicode code point
|
||||
///
|
||||
using code_point = uint32_t;
|
||||
|
||||
///
|
||||
/// \brief Special constant that defines illegal code point
|
||||
///
|
||||
static const code_point illegal = 0xFFFFFFFFu;
|
||||
|
||||
///
|
||||
/// \brief Special constant that defines incomplete code point
|
||||
///
|
||||
static const code_point incomplete = 0xFFFFFFFEu;
|
||||
|
||||
///
|
||||
/// \brief the function checks if \a v is a valid code point
|
||||
///
|
||||
inline bool is_valid_codepoint(code_point v)
|
||||
{
|
||||
if(v > 0x10FFFF)
|
||||
return false;
|
||||
if(0xD800 <= v && v <= 0xDFFF) // surrogates
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef NOWIDE_DOXYGEN
|
||||
///
|
||||
/// \brief UTF Traits class - functions to convert UTF sequences to and from Unicode code points
|
||||
///
|
||||
template<typename CharType, int size = sizeof(CharType)>
|
||||
struct utf_traits
|
||||
{
|
||||
///
|
||||
/// The type of the character
|
||||
///
|
||||
using char_type = CharType;
|
||||
///
|
||||
/// Read one code point from the range [p,e) and return it.
|
||||
///
|
||||
/// - If the sequence that was read is incomplete sequence returns \ref incomplete,
|
||||
/// - If illegal sequence detected returns \ref illegal
|
||||
///
|
||||
/// Requirements
|
||||
///
|
||||
/// - Iterator is valid input iterator
|
||||
///
|
||||
/// Postconditions
|
||||
///
|
||||
/// - p points to the last consumed character
|
||||
///
|
||||
template<typename Iterator>
|
||||
static code_point decode(Iterator& p, Iterator e);
|
||||
|
||||
///
|
||||
/// Maximal width of valid sequence in the code units:
|
||||
///
|
||||
/// - UTF-8 - 4
|
||||
/// - UTF-16 - 2
|
||||
/// - UTF-32 - 1
|
||||
///
|
||||
static const int max_width;
|
||||
///
|
||||
/// The width of specific code point in the code units.
|
||||
///
|
||||
/// Requirement: value is a valid Unicode code point
|
||||
/// Returns value in range [1..max_width]
|
||||
///
|
||||
static int width(code_point value);
|
||||
|
||||
///
|
||||
/// Get the size of the trail part of variable length encoded sequence.
|
||||
///
|
||||
/// Returns -1 if C is not valid lead character
|
||||
///
|
||||
static int trail_length(char_type c);
|
||||
///
|
||||
/// Returns true if c is trail code unit, always false for UTF-32
|
||||
///
|
||||
static bool is_trail(char_type c);
|
||||
///
|
||||
/// Returns true if c is lead code unit, always true of UTF-32
|
||||
///
|
||||
static bool is_lead(char_type c);
|
||||
|
||||
///
|
||||
/// Convert valid Unicode code point \a value to the UTF sequence.
|
||||
///
|
||||
/// Requirements:
|
||||
///
|
||||
/// - \a value is valid code point
|
||||
/// - \a out is an output iterator should be able to accept at least width(value) units
|
||||
///
|
||||
/// Returns the iterator past the last written code unit.
|
||||
///
|
||||
template<typename Iterator>
|
||||
static Iterator encode(code_point value, Iterator out);
|
||||
///
|
||||
/// Decodes valid UTF sequence that is pointed by p into code point.
|
||||
///
|
||||
/// If the sequence is invalid or points to end the behavior is undefined
|
||||
///
|
||||
template<typename Iterator>
|
||||
static code_point decode_valid(Iterator& p);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template<typename CharType, int size = sizeof(CharType)>
|
||||
struct utf_traits;
|
||||
|
||||
template<typename CharType>
|
||||
struct utf_traits<CharType, 1>
|
||||
{
|
||||
using char_type = CharType;
|
||||
|
||||
static int trail_length(char_type ci)
|
||||
{
|
||||
unsigned char c = ci;
|
||||
if(c < 128)
|
||||
return 0;
|
||||
if(NOWIDE_UNLIKELY(c < 194))
|
||||
return -1;
|
||||
if(c < 224)
|
||||
return 1;
|
||||
if(c < 240)
|
||||
return 2;
|
||||
if(NOWIDE_LIKELY(c <= 244))
|
||||
return 3;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const int max_width = 4;
|
||||
|
||||
static int width(code_point value)
|
||||
{
|
||||
if(value <= 0x7F)
|
||||
{
|
||||
return 1;
|
||||
} else if(value <= 0x7FF)
|
||||
{
|
||||
return 2;
|
||||
} else if(NOWIDE_LIKELY(value <= 0xFFFF))
|
||||
{
|
||||
return 3;
|
||||
} else
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_trail(char_type ci)
|
||||
{
|
||||
unsigned char c = ci;
|
||||
return (c & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
static bool is_lead(char_type ci)
|
||||
{
|
||||
return !is_trail(ci);
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
static code_point decode(Iterator& p, Iterator e)
|
||||
{
|
||||
if(NOWIDE_UNLIKELY(p == e))
|
||||
return incomplete;
|
||||
|
||||
unsigned char lead = *p++;
|
||||
|
||||
// First byte is fully validated here
|
||||
int trail_size = trail_length(lead);
|
||||
|
||||
if(NOWIDE_UNLIKELY(trail_size < 0))
|
||||
return illegal;
|
||||
|
||||
// OK as only ASCII may be of size = 0
|
||||
// also optimize for ASCII text
|
||||
if(trail_size == 0)
|
||||
return lead;
|
||||
|
||||
code_point c = lead & ((1 << (6 - trail_size)) - 1);
|
||||
|
||||
// Read the rest
|
||||
unsigned char tmp;
|
||||
switch(trail_size)
|
||||
{
|
||||
case 3:
|
||||
if(NOWIDE_UNLIKELY(p == e))
|
||||
return incomplete;
|
||||
tmp = *p++;
|
||||
if(!is_trail(tmp))
|
||||
return illegal;
|
||||
c = (c << 6) | (tmp & 0x3F);
|
||||
NOWIDE_FALLTHROUGH;
|
||||
case 2:
|
||||
if(NOWIDE_UNLIKELY(p == e))
|
||||
return incomplete;
|
||||
tmp = *p++;
|
||||
if(!is_trail(tmp))
|
||||
return illegal;
|
||||
c = (c << 6) | (tmp & 0x3F);
|
||||
NOWIDE_FALLTHROUGH;
|
||||
case 1:
|
||||
if(NOWIDE_UNLIKELY(p == e))
|
||||
return incomplete;
|
||||
tmp = *p++;
|
||||
if(!is_trail(tmp))
|
||||
return illegal;
|
||||
c = (c << 6) | (tmp & 0x3F);
|
||||
}
|
||||
|
||||
// Check code point validity:
|
||||
// - no surrogates and valid range
|
||||
// - most compact representation
|
||||
if(NOWIDE_UNLIKELY(!is_valid_codepoint(c)) || NOWIDE_UNLIKELY(width(c) != trail_size + 1))
|
||||
{
|
||||
p -= trail_size;
|
||||
return illegal;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
static code_point decode_valid(Iterator& p)
|
||||
{
|
||||
unsigned char lead = *p++;
|
||||
if(lead < 192)
|
||||
return lead;
|
||||
|
||||
int trail_size;
|
||||
|
||||
if(lead < 224)
|
||||
trail_size = 1;
|
||||
else if(NOWIDE_LIKELY(lead < 240)) // non-BMP rare
|
||||
trail_size = 2;
|
||||
else
|
||||
trail_size = 3;
|
||||
|
||||
code_point c = lead & ((1 << (6 - trail_size)) - 1);
|
||||
|
||||
switch(trail_size)
|
||||
{
|
||||
case 3: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F); NOWIDE_FALLTHROUGH;
|
||||
case 2: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F); NOWIDE_FALLTHROUGH;
|
||||
case 1: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
static Iterator encode(code_point value, Iterator out)
|
||||
{
|
||||
if(value <= 0x7F)
|
||||
{
|
||||
*out++ = static_cast<char_type>(value);
|
||||
} else if(value <= 0x7FF)
|
||||
{
|
||||
*out++ = static_cast<char_type>((value >> 6) | 0xC0);
|
||||
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
|
||||
} else if(NOWIDE_LIKELY(value <= 0xFFFF))
|
||||
{
|
||||
*out++ = static_cast<char_type>((value >> 12) | 0xE0);
|
||||
*out++ = static_cast<char_type>(((value >> 6) & 0x3F) | 0x80);
|
||||
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
|
||||
} else
|
||||
{
|
||||
*out++ = static_cast<char_type>((value >> 18) | 0xF0);
|
||||
*out++ = static_cast<char_type>(((value >> 12) & 0x3F) | 0x80);
|
||||
*out++ = static_cast<char_type>(((value >> 6) & 0x3F) | 0x80);
|
||||
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}; // utf8
|
||||
|
||||
template<typename CharType>
|
||||
struct utf_traits<CharType, 2>
|
||||
{
|
||||
using char_type = CharType;
|
||||
|
||||
// See RFC 2781
|
||||
static bool is_first_surrogate(uint16_t x)
|
||||
{
|
||||
return 0xD800 <= x && x <= 0xDBFF;
|
||||
}
|
||||
static bool is_second_surrogate(uint16_t x)
|
||||
{
|
||||
return 0xDC00 <= x && x <= 0xDFFF;
|
||||
}
|
||||
static code_point combine_surrogate(uint16_t w1, uint16_t w2)
|
||||
{
|
||||
return ((code_point(w1 & 0x3FF) << 10) | (w2 & 0x3FF)) + 0x10000;
|
||||
}
|
||||
static int trail_length(char_type c)
|
||||
{
|
||||
if(is_first_surrogate(c))
|
||||
return 1;
|
||||
if(is_second_surrogate(c))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
///
|
||||
/// Returns true if c is trail code unit, always false for UTF-32
|
||||
///
|
||||
static bool is_trail(char_type c)
|
||||
{
|
||||
return is_second_surrogate(c);
|
||||
}
|
||||
///
|
||||
/// Returns true if c is lead code unit, always true of UTF-32
|
||||
///
|
||||
static bool is_lead(char_type c)
|
||||
{
|
||||
return !is_second_surrogate(c);
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
static code_point decode(It& current, It last)
|
||||
{
|
||||
if(NOWIDE_UNLIKELY(current == last))
|
||||
return incomplete;
|
||||
uint16_t w1 = *current++;
|
||||
if(NOWIDE_LIKELY(w1 < 0xD800 || 0xDFFF < w1))
|
||||
{
|
||||
return w1;
|
||||
}
|
||||
if(w1 > 0xDBFF)
|
||||
return illegal;
|
||||
if(current == last)
|
||||
return incomplete;
|
||||
uint16_t w2 = *current++;
|
||||
if(w2 < 0xDC00 || 0xDFFF < w2)
|
||||
return illegal;
|
||||
return combine_surrogate(w1, w2);
|
||||
}
|
||||
template<typename It>
|
||||
static code_point decode_valid(It& current)
|
||||
{
|
||||
uint16_t w1 = *current++;
|
||||
if(NOWIDE_LIKELY(w1 < 0xD800 || 0xDFFF < w1))
|
||||
{
|
||||
return w1;
|
||||
}
|
||||
uint16_t w2 = *current++;
|
||||
return combine_surrogate(w1, w2);
|
||||
}
|
||||
|
||||
static const int max_width = 2;
|
||||
static int width(code_point u)
|
||||
{
|
||||
return u >= 0x10000 ? 2 : 1;
|
||||
}
|
||||
template<typename It>
|
||||
static It encode(code_point u, It out)
|
||||
{
|
||||
if(NOWIDE_LIKELY(u <= 0xFFFF))
|
||||
{
|
||||
*out++ = static_cast<char_type>(u);
|
||||
} else
|
||||
{
|
||||
u -= 0x10000;
|
||||
*out++ = static_cast<char_type>(0xD800 | (u >> 10));
|
||||
*out++ = static_cast<char_type>(0xDC00 | (u & 0x3FF));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}; // utf16;
|
||||
|
||||
template<typename CharType>
|
||||
struct utf_traits<CharType, 4>
|
||||
{
|
||||
using char_type = CharType;
|
||||
static int trail_length(char_type c)
|
||||
{
|
||||
if(is_valid_codepoint(c))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
static bool is_trail(char_type /*c*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static bool is_lead(char_type /*c*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
static code_point decode_valid(It& current)
|
||||
{
|
||||
return *current++;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
static code_point decode(It& current, It last)
|
||||
{
|
||||
if(NOWIDE_UNLIKELY(current == last))
|
||||
return incomplete;
|
||||
code_point c = *current++;
|
||||
if(NOWIDE_UNLIKELY(!is_valid_codepoint(c)))
|
||||
return illegal;
|
||||
return c;
|
||||
}
|
||||
static const int max_width = 1;
|
||||
static int width(code_point /*u*/)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
template<typename It>
|
||||
static It encode(code_point u, It out)
|
||||
{
|
||||
*out++ = static_cast<char_type>(u);
|
||||
return out;
|
||||
}
|
||||
|
||||
}; // utf32
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace utf
|
||||
} // namespace nowide
|
||||
|
||||
#endif
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#ifndef NOWIDE_WINDOWS_HPP_INCLUDED
|
||||
#define NOWIDE_WINDOWS_HPP_INCLUDED
|
||||
|
||||
#ifdef NOWIDE_USE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#else
|
||||
|
||||
//
|
||||
// These are function prototypes... Allow to avoid including windows.h
|
||||
//
|
||||
extern "C" {
|
||||
|
||||
__declspec(dllimport) wchar_t* __stdcall GetEnvironmentStringsW(void);
|
||||
__declspec(dllimport) int __stdcall FreeEnvironmentStringsW(wchar_t*);
|
||||
__declspec(dllimport) wchar_t* __stdcall GetCommandLineW(void);
|
||||
__declspec(dllimport) wchar_t** __stdcall CommandLineToArgvW(const wchar_t*, int*);
|
||||
__declspec(dllimport) unsigned long __stdcall GetLastError();
|
||||
__declspec(dllimport) void* __stdcall LocalFree(void*);
|
||||
__declspec(dllimport) int __stdcall SetEnvironmentVariableW(const wchar_t*, const wchar_t*);
|
||||
__declspec(dllimport) unsigned long __stdcall GetEnvironmentVariableW(const wchar_t*, wchar_t*, unsigned long);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
288
lib/logvisor.cpp
288
lib/logvisor.cpp
|
@ -49,14 +49,6 @@
|
|||
#define BOLD "\x1b[1m"
|
||||
#define NORMAL "\x1b[0m"
|
||||
|
||||
#define W_RED L"\x001b[1;31m"
|
||||
#define W_YELLOW L"\x001b[1;33m"
|
||||
#define W_GREEN L"\x001b[1;32m"
|
||||
#define W_MAGENTA L"\x001b[1;35m"
|
||||
#define W_CYAN L"\x001b[1;36m"
|
||||
#define W_BOLD L"\x001b[1m"
|
||||
#define W_NORMAL L"\x001b[0m"
|
||||
|
||||
#if _WIN32
|
||||
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
#endif
|
||||
|
@ -72,13 +64,6 @@ static Module Log("logvisor");
|
|||
|
||||
static std::unordered_map<std::thread::id, const char*> ThreadMap;
|
||||
|
||||
static std::wstring Widen(std::string str) {
|
||||
std::wstring wide(str.size(), L'\0');
|
||||
std::locale loc;
|
||||
std::use_facet<std::ctype<wchar_t>>(loc).widen(str.data(), str.data()+str.size(), wide.data());
|
||||
return wide;
|
||||
}
|
||||
|
||||
static void AddThreadToMap(const char* name) {
|
||||
auto lk = LockLog();
|
||||
ThreadMap[std::this_thread::get_id()] = name;
|
||||
|
@ -119,16 +104,16 @@ void KillProcessTree() {
|
|||
|
||||
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (::Process32FirstW(hSnap, &pe)) {
|
||||
if (::Process32FirstW(hSnap, &pe) == TRUE) {
|
||||
BOOL bContinue = TRUE;
|
||||
|
||||
// kill child processes
|
||||
while (bContinue) {
|
||||
while (bContinue == TRUE) {
|
||||
// only kill child processes and let console window remain
|
||||
if (pe.th32ParentProcessID == myprocID && std::wcscmp(pe.szExeFile, L"conhost.exe") != 0) {
|
||||
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
|
||||
|
||||
if (hChildProc) {
|
||||
if (hChildProc != nullptr) {
|
||||
::TerminateProcess(hChildProc, 1);
|
||||
::CloseHandle(hChildProc);
|
||||
}
|
||||
|
@ -148,9 +133,9 @@ void KillProcessTree() {
|
|||
HANDLE process;
|
||||
|
||||
process = GetCurrentProcess();
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
frames = CaptureStackBackTrace(0, 100, stack, NULL);
|
||||
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
|
||||
SymInitialize(process, nullptr, TRUE);
|
||||
frames = CaptureStackBackTrace(0, 100, stack, nullptr);
|
||||
symbol = static_cast<SYMBOL_INFO*>(calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1));
|
||||
symbol->MaxNameLen = 255;
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
|
||||
|
@ -268,28 +253,6 @@ void KillProcessTree() {}
|
|||
|
||||
LogMutex _LogMutex;
|
||||
|
||||
#if _WIN32
|
||||
static void AbortHandler(int signum) {
|
||||
_LogMutex.enabled = false;
|
||||
switch (signum) {
|
||||
case SIGSEGV:
|
||||
Log.report(logvisor::Fatal, FMT_STRING(L"Segmentation Fault"));
|
||||
break;
|
||||
case SIGILL:
|
||||
Log.report(logvisor::Fatal, FMT_STRING(L"Bad Execution"));
|
||||
break;
|
||||
case SIGFPE:
|
||||
Log.report(logvisor::Fatal, FMT_STRING(L"Floating Point Exception"));
|
||||
break;
|
||||
case SIGABRT:
|
||||
Log.report(logvisor::Fatal, FMT_STRING(L"Abort Signal"));
|
||||
break;
|
||||
default:
|
||||
Log.report(logvisor::Fatal, FMT_STRING(L"unknown signal {}"), signum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void AbortHandler(int signum) {
|
||||
_LogMutex.enabled = false;
|
||||
switch (signum) {
|
||||
|
@ -310,15 +273,14 @@ static void AbortHandler(int signum) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t _LogCounter;
|
||||
|
||||
std::vector<std::unique_ptr<ILogger>> MainLoggers;
|
||||
std::atomic_size_t ErrorCount(0);
|
||||
static std::chrono::steady_clock MonoClock;
|
||||
static std::chrono::steady_clock::time_point GlobalStart = MonoClock.now();
|
||||
static inline std::chrono::steady_clock::duration CurrentUptime() { return MonoClock.now() - GlobalStart; }
|
||||
using MonoClock = std::chrono::steady_clock;
|
||||
static MonoClock::time_point GlobalStart = MonoClock::now();
|
||||
static inline MonoClock::duration CurrentUptime() { return MonoClock::now() - GlobalStart; }
|
||||
std::atomic_uint_fast64_t FrameIndex(0);
|
||||
|
||||
static inline int ConsoleWidth() {
|
||||
|
@ -506,8 +468,6 @@ struct ConsoleLogger : public ILogger {
|
|||
SendBuffer(bufOut);
|
||||
}
|
||||
|
||||
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {}
|
||||
|
||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
||||
fmt::format_args args) override {
|
||||
if (!m_ready)
|
||||
|
@ -566,9 +526,6 @@ struct ConsoleLogger : public ILogger {
|
|||
|
||||
SendBuffer(bufOut);
|
||||
}
|
||||
|
||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
|
||||
fmt::wformat_args args) override {}
|
||||
};
|
||||
|
||||
#else
|
||||
|
@ -603,7 +560,7 @@ struct ConsoleLogger : public ILogger {
|
|||
|
||||
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
||||
/* Clear current line out */
|
||||
std::fprintf(stderr, "\r%*c\r", ConsoleWidth(), ' ');
|
||||
// std::fprintf(stderr, "\r%*c\r", ConsoleWidth(), ' ');
|
||||
|
||||
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
||||
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
||||
|
@ -715,120 +672,6 @@ struct ConsoleLogger : public ILogger {
|
|||
}
|
||||
}
|
||||
|
||||
static void _reportHead(const wchar_t* modName, const wchar_t* sourceInfo, Level severity) {
|
||||
/* Clear current line out */
|
||||
std::fwprintf(stderr, L"\r%*c\r", ConsoleWidth(), L' ');
|
||||
|
||||
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
||||
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
||||
static_cast<double>(std::chrono::steady_clock::duration::period::den);
|
||||
const std::thread::id thrId = std::this_thread::get_id();
|
||||
std::optional<std::wstring> thrName;
|
||||
if (ThreadMap.find(thrId) != ThreadMap.end())
|
||||
thrName = Widen(ThreadMap[thrId]);
|
||||
|
||||
if (XtermColor) {
|
||||
std::fputws(W_BOLD L"[", stderr);
|
||||
fmt::print(stderr, FMT_STRING(W_GREEN L"{:.4f} "), tmd);
|
||||
const uint_fast64_t fIdx = FrameIndex.load();
|
||||
if (fIdx != 0)
|
||||
fmt::print(stderr, FMT_STRING(L"({}) "), fIdx);
|
||||
switch (severity) {
|
||||
case Info:
|
||||
std::fputws(W_BOLD W_CYAN L"INFO", stderr);
|
||||
break;
|
||||
case Warning:
|
||||
std::fputws(W_BOLD W_YELLOW L"WARNING", stderr);
|
||||
break;
|
||||
case Error:
|
||||
std::fputws(W_RED W_BOLD L"ERROR", stderr);
|
||||
break;
|
||||
case Fatal:
|
||||
std::fputws(W_BOLD W_RED L"FATAL ERROR", stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
fmt::print(stderr, FMT_STRING(W_NORMAL W_BOLD L" {}"), modName);
|
||||
if (sourceInfo)
|
||||
fmt::print(stderr, FMT_STRING(W_BOLD W_YELLOW L" {{}}"), sourceInfo);
|
||||
if (thrName)
|
||||
fmt::print(stderr, FMT_STRING(W_BOLD W_MAGENTA L" ({})"), *thrName);
|
||||
std::fputws(W_NORMAL W_BOLD L"] " W_NORMAL, stderr);
|
||||
} else {
|
||||
#if _WIN32
|
||||
#if !WINDOWS_STORE
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||
std::fputwc(L'[', stderr);
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
|
||||
fmt::print(stderr, FMT_STRING(L"{:.4f} "), tmd);
|
||||
const uint64_t fi = FrameIndex.load();
|
||||
if (fi != 0)
|
||||
std::fwprintf(stderr, L"(%" PRIu64 L") ", fi);
|
||||
switch (severity) {
|
||||
case Info:
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
std::fputws(L"INFO", stderr);
|
||||
break;
|
||||
case Warning:
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
||||
std::fputws(L"WARNING", stderr);
|
||||
break;
|
||||
case Error:
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
||||
std::fputws(L"ERROR", stderr);
|
||||
break;
|
||||
case Fatal:
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
||||
std::fputws(L"FATAL ERROR", stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||
fmt::print(stderr, FMT_STRING(L" {}"), modName);
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
||||
if (sourceInfo)
|
||||
fmt::print(stderr, FMT_STRING(L" {{}}"), sourceInfo);
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
|
||||
if (thrName)
|
||||
fmt::print(stderr, FMT_STRING(L" ({})"), *thrName);
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||
std::fputws(L"] ", stderr);
|
||||
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
|
||||
#endif
|
||||
#else
|
||||
std::fputwc(L'[', stderr);
|
||||
fmt::print(stderr, FMT_STRING(L"{:.4f} "), tmd);
|
||||
uint_fast64_t fIdx = FrameIndex.load();
|
||||
if (fIdx)
|
||||
fmt::print(stderr, FMT_STRING(L"({}) "), fIdx);
|
||||
switch (severity) {
|
||||
case Info:
|
||||
std::fputws(L"INFO", stderr);
|
||||
break;
|
||||
case Warning:
|
||||
std::fputws(L"WARNING", stderr);
|
||||
break;
|
||||
case Error:
|
||||
std::fputws(L"ERROR", stderr);
|
||||
break;
|
||||
case Fatal:
|
||||
std::fputws(L"FATAL ERROR", stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fmt::print(stderr, FMT_STRING(L" {}"), modName);
|
||||
if (sourceInfo)
|
||||
fmt::print(stderr, FMT_STRING(L" {{}}"), sourceInfo);
|
||||
if (thrName)
|
||||
fmt::print(stderr, FMT_STRING(L" ({})"), *thrName);
|
||||
std::fputws(L"] ", stderr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
||||
_reportHead(modName, nullptr, severity);
|
||||
fmt::vprint(stderr, format, args);
|
||||
|
@ -836,13 +679,6 @@ struct ConsoleLogger : public ILogger {
|
|||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
|
||||
_reportHead(Widen(modName).c_str(), nullptr, severity);
|
||||
fmt::vprint(stderr, format, args);
|
||||
std::fputwc(L'\n', stderr);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
||||
fmt::format_args args) override {
|
||||
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
||||
|
@ -850,14 +686,6 @@ struct ConsoleLogger : public ILogger {
|
|||
std::fputc('\n', stderr);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
|
||||
fmt::wformat_args args) override {
|
||||
_reportHead(Widen(modName).c_str(), fmt::format(FMT_STRING(L"{}:{}"), Widen(file).c_str(), linenum).c_str(), severity);
|
||||
fmt::vprint(stderr, format, args);
|
||||
std::fputwc(L'\n', stderr);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -868,6 +696,15 @@ void RegisterConsoleLogger() {
|
|||
if (!ConsoleLoggerRegistered) {
|
||||
MainLoggers.emplace_back(new ConsoleLogger);
|
||||
ConsoleLoggerRegistered = true;
|
||||
#if _WIN32
|
||||
#if 0
|
||||
if (GetACP() != CP_UTF8) {
|
||||
Log.report(Fatal, FMT_STRING("UTF-8 codepage not active! (Windows 10 1903+ required)"));
|
||||
}
|
||||
#else
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -921,7 +758,7 @@ struct FileLogger : public ILogger {
|
|||
openFile();
|
||||
}
|
||||
}
|
||||
virtual void closeFile() {
|
||||
virtual void closeFile() {
|
||||
if (fp) {
|
||||
std::fclose(fp);
|
||||
fp = nullptr;
|
||||
|
@ -971,48 +808,6 @@ struct FileLogger : public ILogger {
|
|||
std::fputs("] ", fp);
|
||||
}
|
||||
|
||||
void _reportHead(const wchar_t* modName, const wchar_t* sourceInfo, Level severity) {
|
||||
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
||||
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
||||
static_cast<double>(std::chrono::steady_clock::duration::period::den);
|
||||
const std::thread::id thrId = std::this_thread::get_id();
|
||||
const wchar_t* thrName = nullptr;
|
||||
if (ThreadMap.find(thrId) != ThreadMap.end()) {
|
||||
thrName = Widen(ThreadMap[thrId]).c_str();
|
||||
}
|
||||
|
||||
std::fputwc(L'[', fp);
|
||||
std::fwprintf(fp, L"%5.4f ", tmd);
|
||||
const uint_fast64_t fIdx = FrameIndex.load();
|
||||
if (fIdx != 0) {
|
||||
std::fwprintf(fp, L"(%" PRIu64 L") ", fIdx);
|
||||
}
|
||||
switch (severity) {
|
||||
case Info:
|
||||
std::fputws(L"INFO", fp);
|
||||
break;
|
||||
case Warning:
|
||||
std::fputws(L"WARNING", fp);
|
||||
break;
|
||||
case Error:
|
||||
std::fputws(L"ERROR", fp);
|
||||
break;
|
||||
case Fatal:
|
||||
std::fputws(L"FATAL ERROR", fp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
std::fwprintf(fp, L" %s", modName);
|
||||
if (sourceInfo) {
|
||||
std::fwprintf(fp, L" {%s}", sourceInfo);
|
||||
}
|
||||
if (thrName) {
|
||||
std::fwprintf(fp, L" (%s)", thrName);
|
||||
}
|
||||
std::fputws(L"] ", fp);
|
||||
}
|
||||
|
||||
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
||||
openFileIfNeeded();
|
||||
_reportHead(modName, nullptr, severity);
|
||||
|
@ -1020,13 +815,6 @@ struct FileLogger : public ILogger {
|
|||
std::fputc('\n', fp);
|
||||
}
|
||||
|
||||
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) override {
|
||||
openFileIfNeeded();
|
||||
_reportHead(Widen(modName).c_str(), nullptr, severity);
|
||||
fmt::vprint(fp, format, args);
|
||||
std::fputwc(L'\n', fp);
|
||||
}
|
||||
|
||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
||||
fmt::format_args args) override {
|
||||
openFileIfNeeded();
|
||||
|
@ -1034,19 +822,11 @@ struct FileLogger : public ILogger {
|
|||
fmt::vprint(fp, format, args);
|
||||
std::fputc('\n', fp);
|
||||
}
|
||||
|
||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::wstring_view format,
|
||||
fmt::wformat_args args) override {
|
||||
openFileIfNeeded();
|
||||
_reportHead(Widen(modName).c_str(), Widen(fmt::format(FMT_STRING("{}:{}"), file, linenum)).c_str(), severity);
|
||||
fmt::vprint(fp, format, args);
|
||||
std::fputwc(L'\n', fp);
|
||||
}
|
||||
};
|
||||
|
||||
struct FileLogger8 : public FileLogger {
|
||||
const char* m_filepath;
|
||||
FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {}
|
||||
explicit FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {}
|
||||
void openFile() override { fp = std::fopen(m_filepath, "a"); }
|
||||
~FileLogger8() override = default;
|
||||
};
|
||||
|
@ -1056,30 +836,4 @@ void RegisterFileLogger(const char* filepath) {
|
|||
MainLoggers.emplace_back(new FileLogger8(filepath));
|
||||
}
|
||||
|
||||
#if LOG_UCS2
|
||||
|
||||
struct FileLogger16 : public FileLogger {
|
||||
const wchar_t* m_filepath;
|
||||
FileLogger16(const wchar_t* filepath) : FileLogger(log_typeid(FileLogger16)), m_filepath(filepath) {}
|
||||
void openFile() override { fp = _wfopen(m_filepath, L"a"); }
|
||||
~FileLogger16() override = default;
|
||||
};
|
||||
|
||||
void RegisterFileLogger(const wchar_t* filepath) {
|
||||
/* Determine if file logger already added */
|
||||
for (const auto& logger : MainLoggers) {
|
||||
if (logger->getTypeId() == log_typeid(FileLogger16)) {
|
||||
const auto* fl = static_cast<const FileLogger16*>(logger.get());
|
||||
if (!wcscmp(filepath, fl->m_filepath)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise construct new file logger */
|
||||
MainLoggers.emplace_back(new FileLogger16(filepath));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace logvisor
|
||||
|
|
Loading…
Reference in New Issue