mirror of
https://github.com/AxioDL/logvisor.git
synced 2025-12-08 21:17:44 +00:00
Compare commits
44 Commits
3bedd268e8
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 208a8c1f84 | |||
|
6926b6a7c9
|
|||
| 9420e3007e | |||
| 4f7f3f2147 | |||
| 6eadd55f7a | |||
| df3c283186 | |||
| 139281f667 | |||
|
274ad5ef07
|
|||
|
|
ed3714e55c | ||
|
ee3276d7c5
|
|||
|
|
f23179e168 | ||
| 7901fc496d | |||
| d874350205 | |||
| 028617d225 | |||
| 984d87348e | |||
| 33e7322595 | |||
| bb113e03f8 | |||
| 240491ccad | |||
| fd343eb234 | |||
| aac06c3465 | |||
| 0e3fe3e766 | |||
| 17aa25cfa9 | |||
|
|
d4af1a1088 | ||
| 8913e55e34 | |||
| 81fb4e4c2d | |||
|
|
7f63cabaea | ||
| 41432143fd | |||
|
|
14ea54f8b5 | ||
|
|
d62b4ce26e | ||
|
|
27814c5276 | ||
|
|
f04a9777eb | ||
|
|
187c35f3c4 | ||
| 8c2e711362 | |||
|
|
63cb911d09 | ||
| f623ace3b4 | |||
| 8ea97c524c | |||
| 81c72ad220 | |||
|
|
59f651f24b | ||
|
|
2a40c6dc92 | ||
|
|
2f3f06e5ca | ||
|
|
94d1c558a0 | ||
| aa9aa0a82c | |||
|
|
a57409828c | ||
|
|
dcd0ffcaec |
1
.gitmodules
vendored
1
.gitmodules
vendored
@@ -1,3 +1,4 @@
|
|||||||
[submodule "fmt"]
|
[submodule "fmt"]
|
||||||
path = fmt
|
path = fmt
|
||||||
url = https://github.com/fmtlib/fmt
|
url = https://github.com/fmtlib/fmt
|
||||||
|
branch = master
|
||||||
|
|||||||
@@ -1,24 +1,77 @@
|
|||||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
|
||||||
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
|
project(nod VERSION 0.1)
|
||||||
project(nod VERSION 0.1)
|
if (NOT MSVC)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
endif()
|
endif ()
|
||||||
|
|
||||||
include (CMakePackageConfigHelpers)
|
include (CMakePackageConfigHelpers)
|
||||||
|
|
||||||
add_subdirectory(fmt)
|
if (NOT TARGET fmt)
|
||||||
|
add_subdirectory(fmt)
|
||||||
|
target_compile_definitions(fmt PUBLIC
|
||||||
|
FMT_ARM_ABI_COMPATIBILITY=1
|
||||||
|
FMT_EXCEPTIONS=0)
|
||||||
|
target_compile_definitions(fmt INTERFACE
|
||||||
|
FMT_ENFORCE_COMPILE_STRING=1
|
||||||
|
FMT_UNICODE=1)
|
||||||
|
set(FMT_LIB fmt)
|
||||||
|
endif ()
|
||||||
|
|
||||||
add_library(logvisor
|
add_library(logvisor
|
||||||
lib/logvisor.cpp
|
lib/logvisor.cpp
|
||||||
include/logvisor/logvisor.hpp)
|
include/logvisor/logvisor.hpp)
|
||||||
|
|
||||||
target_link_libraries(logvisor PUBLIC ${CMAKE_DL_LIBS} fmt)
|
if ("${SENTRY_DSN}" STREQUAL "")
|
||||||
|
message(STATUS "SENTRY_DSN not set, not enabling Sentry")
|
||||||
|
target_compile_definitions(logvisor PUBLIC SENTRY_ENABLED=0)
|
||||||
|
set(SENTRY_LIB "")
|
||||||
|
set(BREAKPAD_CLIENT "")
|
||||||
|
else ()
|
||||||
|
message(STATUS "Enabling Sentry integration")
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(sentry
|
||||||
|
GIT_REPOSITORY https://github.com/getsentry/sentry-native.git
|
||||||
|
GIT_TAG 5fcb1dc4d1c8b85fc5ab225d29c7394b375fd1e9
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(sentry)
|
||||||
|
target_compile_definitions(logvisor PUBLIC SENTRY_ENABLED=1)
|
||||||
|
target_compile_definitions(logvisor PRIVATE SENTRY_DSN="${SENTRY_DSN}")
|
||||||
|
set(SENTRY_LIB sentry)
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
set(BREAKPAD_CLIENT breakpad_client)
|
||||||
|
target_compile_options(breakpad_client PRIVATE -Wno-implicit-fallthrough -Wno-array-bounds)
|
||||||
|
target_compile_options(sentry PRIVATE "-Wno-implicit-fallthrough")
|
||||||
|
set_property(TARGET breakpad_client PROPERTY CXX_STANDARD 17)
|
||||||
|
set_property(TARGET sentry PROPERTY CXX_STANDARD 17)
|
||||||
|
else ()
|
||||||
|
set(BREAKPAD_CLIENT "")
|
||||||
|
endif ()
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_options(crashpad_client PRIVATE "/W0")
|
||||||
|
target_compile_options(crashpad_util PRIVATE "/W0")
|
||||||
|
target_compile_options(crashpad_snapshot PRIVATE "/W0")
|
||||||
|
target_compile_options(mini_chromium PRIVATE "/W0")
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
target_compile_options(crashpad_zlib PRIVATE "-mpclmul")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
target_link_libraries(logvisor PUBLIC fmt ${SENTRY_LIB})
|
||||||
|
if(NX)
|
||||||
|
target_link_libraries(logvisor PUBLIC debug nxd optimized nx)
|
||||||
|
else()
|
||||||
|
target_link_libraries(logvisor PUBLIC ${CMAKE_DL_LIBS})
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(logvisor PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
target_include_directories(logvisor PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
||||||
|
|
||||||
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||||
install(DIRECTORY include/logvisor DESTINATION include)
|
install(DIRECTORY include/logvisor DESTINATION include)
|
||||||
install(DIRECTORY fmt/include/fmt DESTINATION include)
|
if (FMT_LIB)
|
||||||
|
install(DIRECTORY fmt/include/fmt DESTINATION include)
|
||||||
|
endif ()
|
||||||
|
|
||||||
set(version_config_file "${PROJECT_BINARY_DIR}/logvisorConfigVersion.cmake")
|
set(version_config_file "${PROJECT_BINARY_DIR}/logvisorConfigVersion.cmake")
|
||||||
set(config_file "${PROJECT_BINARY_DIR}/logvisorConfig.cmake")
|
set(config_file "${PROJECT_BINARY_DIR}/logvisorConfig.cmake")
|
||||||
@@ -26,7 +79,7 @@ set(config_install_dir "lib/cmake/logvisor")
|
|||||||
|
|
||||||
# Associate target with export
|
# Associate target with export
|
||||||
install(
|
install(
|
||||||
TARGETS logvisor fmt
|
TARGETS logvisor ${FMT_LIB} ${SENTRY_LIB} ${BREAKPAD_CLIENT}
|
||||||
EXPORT logvisorTargets
|
EXPORT logvisorTargets
|
||||||
ARCHIVE DESTINATION "lib"
|
ARCHIVE DESTINATION "lib"
|
||||||
INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target.
|
INCLUDES DESTINATION include # This sets the INTERFACE_INCLUDE_DIRECTORIES property of the target.
|
||||||
@@ -57,3 +110,4 @@ install(
|
|||||||
FILES "${config_file}" "${version_config_file}"
|
FILES "${config_file}" "${version_config_file}"
|
||||||
DESTINATION ${config_install_dir}
|
DESTINATION ${config_install_dir}
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|||||||
2
fmt
2
fmt
Submodule fmt updated: 6bcc3fd216...d141cdbeb0
@@ -8,24 +8,18 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#define FMT_STRING_ALIAS 1
|
|
||||||
#define FMT_ENFORCE_COMPILE_STRING 1
|
|
||||||
#define FMT_USE_GRISU 0
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
#if defined(__SWITCH__) && !defined(LOGVISOR_NX_LM)
|
||||||
#include "nxstl/mutex"
|
#define LOGVISOR_NX_LM 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" void logvisorBp();
|
extern "C" void logvisorBp();
|
||||||
|
#define log_typeid(type) std::hash<std::string>()(#type)
|
||||||
|
|
||||||
namespace logvisor {
|
namespace logvisor {
|
||||||
|
|
||||||
void logvisorAbort();
|
[[noreturn]] void logvisorAbort();
|
||||||
|
|
||||||
#if _WIN32 && UNICODE
|
|
||||||
#define LOG_UCS2 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* True if ANSI color available */
|
/* True if ANSI color available */
|
||||||
extern bool XtermColor;
|
extern bool XtermColor;
|
||||||
@@ -44,13 +38,16 @@ enum Level {
|
|||||||
* @brief Backend interface for receiving app-wide log events
|
* @brief Backend interface for receiving app-wide log events
|
||||||
*/
|
*/
|
||||||
struct ILogger {
|
struct ILogger {
|
||||||
|
private:
|
||||||
|
uint64_t m_typeHash;
|
||||||
|
public:
|
||||||
|
ILogger(uint64_t typeHash) : m_typeHash(typeHash) {}
|
||||||
virtual ~ILogger() = default;
|
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::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,
|
virtual void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
|
||||||
fmt::string_view format, fmt::format_args args) = 0;
|
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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,7 +99,7 @@ struct LogMutex {
|
|||||||
if (enabled)
|
if (enabled)
|
||||||
return std::unique_lock<std::recursive_mutex>(mutex);
|
return std::unique_lock<std::recursive_mutex>(mutex);
|
||||||
else
|
else
|
||||||
return std::unique_lock<std::recursive_mutex>();
|
return {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
extern LogMutex _LogMutex;
|
extern LogMutex _LogMutex;
|
||||||
@@ -147,6 +144,16 @@ void RegisterFileLogger(const char* filepath);
|
|||||||
*/
|
*/
|
||||||
void RegisterStandardExceptions();
|
void RegisterStandardExceptions();
|
||||||
|
|
||||||
|
#if SENTRY_ENABLED
|
||||||
|
/**
|
||||||
|
* @brief Register Sentry crash reporting & logging.
|
||||||
|
* @param appName The application name
|
||||||
|
* @param appVersion The application version
|
||||||
|
* @param cacheDir Directory for Sentry cache files
|
||||||
|
*/
|
||||||
|
void RegisterSentry(const char* appName, const char* appVersion, const char* cacheDir);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
/**
|
/**
|
||||||
* @brief Spawn an application-owned cmd.exe window for displaying console output
|
* @brief Spawn an application-owned cmd.exe window for displaying console output
|
||||||
@@ -154,20 +161,8 @@ void RegisterStandardExceptions();
|
|||||||
void CreateWin32Console();
|
void CreateWin32Console();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LOG_UCS2
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct and register a file logger (wchar_t version)
|
* @brief This is constructed per-subsystem in a locally centralized fashion
|
||||||
* @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 fashon
|
|
||||||
*/
|
*/
|
||||||
class Module {
|
class Module {
|
||||||
const char* m_modName;
|
const char* m_modName;
|
||||||
@@ -207,12 +202,12 @@ class Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Module(const char* modName) : m_modName(modName) {}
|
constexpr Module(const char* modName) : m_modName(modName) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Route new log message to centralized ILogger
|
* @brief Route new log message to centralized ILogger
|
||||||
* @param severity Level of log report severity
|
* @param severity Level of log report severity
|
||||||
* @param format Standard printf-style format string
|
* @param format fmt-style format string
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
|
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
|
||||||
void report(Level severity, const S& format, Args&&... args) {
|
void report(Level severity, const S& format, Args&&... args) {
|
||||||
@@ -220,7 +215,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
_vreport(severity, fmt::to_string_view<Char>(format),
|
_vreport(severity, fmt::to_string_view<Char>(format),
|
||||||
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
||||||
fmt::internal::make_args_checked<Args...>(format, args...)));
|
fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
@@ -236,16 +231,15 @@ public:
|
|||||||
* @param severity Level of log report severity
|
* @param severity Level of log report severity
|
||||||
* @param file Source file name from __FILE__ macro
|
* @param file Source file name from __FILE__ macro
|
||||||
* @param linenum Source line number from __LINE__ macro
|
* @param linenum Source line number from __LINE__ macro
|
||||||
* @param format Standard printf-style format string
|
* @param format fmt-style format string
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
|
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
|
||||||
void reportSource(Level severity, const char* file, unsigned linenum, const S& format, Args&&... args) {
|
void reportSource(Level severity, const char* file, unsigned linenum, const S& format, Args&&... args) {
|
||||||
if (MainLoggers.empty() && severity != Level::Fatal)
|
if (MainLoggers.empty() && severity != Level::Fatal)
|
||||||
return;
|
return;
|
||||||
_vreportSource(
|
_vreportSource(severity, file, linenum, fmt::to_string_view<Char>(format),
|
||||||
severity, file, linenum, fmt::to_string_view<Char>(format),
|
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
||||||
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||||
fmt::internal::make_args_checked<Args...>(format, args...)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
@@ -257,10 +251,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define Lfmt(str) fmt(L##str)
|
#define FMT_CUSTOM_FORMATTER(tp, fmtstr, ...) \
|
||||||
#define ufmt(str) fmt(u##str)
|
|
||||||
#define Ufmt(str) fmt(U##str)
|
|
||||||
#define FMT_CUSTOM_FORMATTER(tp, ...) \
|
|
||||||
namespace fmt { \
|
namespace fmt { \
|
||||||
template <> \
|
template <> \
|
||||||
struct formatter<tp, char> { \
|
struct formatter<tp, char> { \
|
||||||
@@ -268,7 +259,7 @@ struct formatter<tp, char> { \
|
|||||||
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
|
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
|
||||||
template <typename FormatContext> \
|
template <typename FormatContext> \
|
||||||
auto format(const tp &obj, FormatContext &ctx) { \
|
auto format(const tp &obj, FormatContext &ctx) { \
|
||||||
return format_to(ctx.out(), __VA_ARGS__); \
|
return format_to(ctx.out(), FMT_STRING(fmtstr), __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
template <> \
|
template <> \
|
||||||
@@ -277,7 +268,7 @@ struct formatter<tp, wchar_t> { \
|
|||||||
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
|
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
|
||||||
template <typename FormatContext> \
|
template <typename FormatContext> \
|
||||||
auto format(const tp &obj, FormatContext &ctx) { \
|
auto format(const tp &obj, FormatContext &ctx) { \
|
||||||
return format_to(ctx.out(), L##__VA_ARGS__); \
|
return format_to(ctx.out(), FMT_STRING(L##fmtstr), __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
template <> \
|
template <> \
|
||||||
@@ -286,7 +277,7 @@ struct formatter<tp, char16_t> { \
|
|||||||
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
|
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
|
||||||
template <typename FormatContext> \
|
template <typename FormatContext> \
|
||||||
auto format(const tp &obj, FormatContext &ctx) { \
|
auto format(const tp &obj, FormatContext &ctx) { \
|
||||||
return format_to(ctx.out(), u##__VA_ARGS__); \
|
return format_to(ctx.out(), FMT_STRING(u##fmtstr), __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
template <> \
|
template <> \
|
||||||
@@ -295,9 +286,17 @@ struct formatter<tp, char32_t> { \
|
|||||||
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
|
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); } \
|
||||||
template <typename FormatContext> \
|
template <typename FormatContext> \
|
||||||
auto format(const tp &obj, FormatContext &ctx) { \
|
auto format(const tp &obj, FormatContext &ctx) { \
|
||||||
return format_to(ctx.out(), U##__VA_ARGS__); \
|
return format_to(ctx.out(), FMT_STRING(U##fmtstr), __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace logvisor
|
} // namespace logvisor
|
||||||
|
|
||||||
|
template <typename S, typename... Args, typename Char = fmt::char_t<S>>
|
||||||
|
void quicklog(const S& format, Args&&... args) {
|
||||||
|
logvisor::MainLoggers[0]->report(
|
||||||
|
"quick", logvisor::Info, fmt::to_string_view<Char>(format),
|
||||||
|
fmt::basic_format_args<fmt::buffer_context<Char>>(
|
||||||
|
fmt::make_args_checked<Args...>(format, std::forward<Args>(args)...)));
|
||||||
|
}
|
||||||
|
|||||||
194
include/nowide/args.hpp
Normal file
194
include/nowide/args.hpp
Normal file
@@ -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
|
||||||
85
include/nowide/config.hpp
Normal file
85
include/nowide/config.hpp
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// 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(__has_cpp_attribute)
|
||||||
|
#define NOWIDE_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
|
||||||
|
#else
|
||||||
|
#define NOWIDE_HAS_CPP_ATTRIBUTE(attr) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if NOWIDE_HAS_CPP_ATTRIBUTE(fallthrough)
|
||||||
|
#define NOWIDE_FALLTHROUGH [[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
|
||||||
135
include/nowide/convert.hpp
Normal file
135
include/nowide/convert.hpp
Normal file
@@ -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
|
||||||
94
include/nowide/detail/is_string_container.hpp
Normal file
94
include/nowide/detail/is_string_container.hpp
Normal file
@@ -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
|
||||||
19
include/nowide/replacement.hpp
Normal file
19
include/nowide/replacement.hpp
Normal file
@@ -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
|
||||||
213
include/nowide/stackstring.hpp
Normal file
213
include/nowide/stackstring.hpp
Normal file
@@ -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
|
||||||
96
include/nowide/utf/convert.hpp
Normal file
96
include/nowide/utf/convert.hpp
Normal file
@@ -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
|
||||||
450
include/nowide/utf/utf.hpp
Normal file
450
include/nowide/utf/utf.hpp
Normal file
@@ -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
|
||||||
32
include/nowide/windows.hpp
Normal file
32
include/nowide/windows.hpp
Normal file
@@ -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
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
#ifndef _NXSTL_CONDVAR
|
|
||||||
#define _NXSTL_CONDVAR 1
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
extern "C" {
|
|
||||||
#include <switch/kernel/condvar.h>
|
|
||||||
#include <switch/result.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <bits/std_mutex.h>
|
|
||||||
#include <ext/concurrence.h>
|
|
||||||
#include <bits/alloc_traits.h>
|
|
||||||
#include <bits/allocator.h>
|
|
||||||
#include <bits/unique_ptr.h>
|
|
||||||
#include <bits/shared_ptr.h>
|
|
||||||
#include <bits/cxxabi_forced.h>
|
|
||||||
|
|
||||||
namespace std _GLIBCXX_VISIBILITY(default)
|
|
||||||
{
|
|
||||||
|
|
||||||
/// cv_status
|
|
||||||
enum class cv_status { no_timeout, timeout };
|
|
||||||
|
|
||||||
/// condition_variable
|
|
||||||
class condition_variable
|
|
||||||
{
|
|
||||||
typedef chrono::system_clock __clock_t;
|
|
||||||
typedef CondVar __native_type;
|
|
||||||
|
|
||||||
__native_type _M_cond = {};
|
|
||||||
public:
|
|
||||||
typedef __native_type* native_handle_type;
|
|
||||||
|
|
||||||
constexpr condition_variable() noexcept = default;
|
|
||||||
~condition_variable() noexcept = default;
|
|
||||||
|
|
||||||
condition_variable(const condition_variable&) = delete;
|
|
||||||
condition_variable& operator=(const condition_variable&) = delete;
|
|
||||||
|
|
||||||
void
|
|
||||||
notify_one() noexcept
|
|
||||||
{
|
|
||||||
condvarWakeOne(&_M_cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
notify_all() noexcept
|
|
||||||
{
|
|
||||||
condvarWakeAll(&_M_cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
wait(unique_lock<mutex>& __lock) noexcept
|
|
||||||
{
|
|
||||||
condvarWait(&_M_cond, __lock.mutex()->native_handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Predicate>
|
|
||||||
void
|
|
||||||
wait(unique_lock<mutex>& __lock, _Predicate __p)
|
|
||||||
{
|
|
||||||
while (!__p())
|
|
||||||
wait(__lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Duration>
|
|
||||||
cv_status
|
|
||||||
wait_until(unique_lock<mutex>& __lock,
|
|
||||||
const chrono::time_point<__clock_t, _Duration>& __atime)
|
|
||||||
{ return __wait_until_impl(__lock, __atime); }
|
|
||||||
|
|
||||||
template<typename _Clock, typename _Duration>
|
|
||||||
cv_status
|
|
||||||
wait_until(unique_lock<mutex>& __lock,
|
|
||||||
const chrono::time_point<_Clock, _Duration>& __atime)
|
|
||||||
{
|
|
||||||
// DR 887 - Sync unknown clock to known clock.
|
|
||||||
const typename _Clock::time_point __c_entry = _Clock::now();
|
|
||||||
const __clock_t::time_point __s_entry = __clock_t::now();
|
|
||||||
const auto __delta = __atime - __c_entry;
|
|
||||||
const auto __s_atime = __s_entry + __delta;
|
|
||||||
|
|
||||||
return __wait_until_impl(__lock, __s_atime);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Clock, typename _Duration, typename _Predicate>
|
|
||||||
bool
|
|
||||||
wait_until(unique_lock<mutex>& __lock,
|
|
||||||
const chrono::time_point<_Clock, _Duration>& __atime,
|
|
||||||
_Predicate __p)
|
|
||||||
{
|
|
||||||
while (!__p())
|
|
||||||
if (wait_until(__lock, __atime) == cv_status::timeout)
|
|
||||||
return __p();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Rep, typename _Period>
|
|
||||||
cv_status
|
|
||||||
wait_for(unique_lock<mutex>& __lock,
|
|
||||||
const chrono::duration<_Rep, _Period>& __rtime)
|
|
||||||
{
|
|
||||||
using __dur = typename __clock_t::duration;
|
|
||||||
auto __reltime = chrono::duration_cast<__dur>(__rtime);
|
|
||||||
if (__reltime < __rtime)
|
|
||||||
++__reltime;
|
|
||||||
return wait_until(__lock, __clock_t::now() + __reltime);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Rep, typename _Period, typename _Predicate>
|
|
||||||
bool
|
|
||||||
wait_for(unique_lock<mutex>& __lock,
|
|
||||||
const chrono::duration<_Rep, _Period>& __rtime,
|
|
||||||
_Predicate __p)
|
|
||||||
{
|
|
||||||
using __dur = typename __clock_t::duration;
|
|
||||||
auto __reltime = chrono::duration_cast<__dur>(__rtime);
|
|
||||||
if (__reltime < __rtime)
|
|
||||||
++__reltime;
|
|
||||||
return wait_until(__lock, __clock_t::now() + __reltime, std::move(__p));
|
|
||||||
}
|
|
||||||
|
|
||||||
native_handle_type
|
|
||||||
native_handle()
|
|
||||||
{ return &_M_cond; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<typename _Dur>
|
|
||||||
cv_status
|
|
||||||
__wait_until_impl(unique_lock<mutex>& __lock,
|
|
||||||
const chrono::time_point<__clock_t, _Dur>& __atime)
|
|
||||||
{
|
|
||||||
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime);
|
|
||||||
|
|
||||||
Result res = condvarWaitTimeout(&_M_cond, __lock.mutex()->native_handle(), __ns.count());
|
|
||||||
|
|
||||||
if (R_DESCRIPTION(res) == KernelError_Timeout)
|
|
||||||
return cv_status::timeout;
|
|
||||||
return cv_status::no_timeout;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
notify_all_at_thread_exit(condition_variable&, unique_lock<mutex>);
|
|
||||||
|
|
||||||
struct __at_thread_exit_elt
|
|
||||||
{
|
|
||||||
__at_thread_exit_elt* _M_next;
|
|
||||||
void (*_M_cb)(void*);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
#ifndef _NXSTL_MUTEX
|
|
||||||
#define _NXSTL_MUTEX 1
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
extern "C" {
|
|
||||||
#include <switch/kernel/mutex.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace std _GLIBCXX_VISIBILITY(default)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Common base class for std::mutex
|
|
||||||
class __mutex_base
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
typedef Mutex __native_type;
|
|
||||||
|
|
||||||
__native_type _M_mutex = {};
|
|
||||||
|
|
||||||
constexpr __mutex_base() noexcept = default;
|
|
||||||
|
|
||||||
__mutex_base(const __mutex_base&) = delete;
|
|
||||||
__mutex_base& operator=(const __mutex_base&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The standard mutex type.
|
|
||||||
class mutex : private __mutex_base
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef __native_type* native_handle_type;
|
|
||||||
|
|
||||||
constexpr mutex() noexcept = default;
|
|
||||||
~mutex() = default;
|
|
||||||
|
|
||||||
mutex(const mutex&) = delete;
|
|
||||||
mutex& operator=(const mutex&) = delete;
|
|
||||||
|
|
||||||
void
|
|
||||||
lock()
|
|
||||||
{
|
|
||||||
mutexLock(&_M_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
try_lock() noexcept
|
|
||||||
{
|
|
||||||
return mutexTryLock(&_M_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
unlock()
|
|
||||||
{
|
|
||||||
mutexUnlock(&_M_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
native_handle_type
|
|
||||||
native_handle() noexcept
|
|
||||||
{ return &_M_mutex; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Common base class for std::recursive_mutex
|
|
||||||
class __recursive_mutex_base
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
typedef RMutex __native_type;
|
|
||||||
|
|
||||||
__recursive_mutex_base(const __recursive_mutex_base&) = delete;
|
|
||||||
__recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;
|
|
||||||
|
|
||||||
__native_type _M_mutex = {};
|
|
||||||
|
|
||||||
__recursive_mutex_base() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The standard recursive mutex type.
|
|
||||||
class recursive_mutex : private __recursive_mutex_base
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef __native_type* native_handle_type;
|
|
||||||
|
|
||||||
constexpr recursive_mutex() = default;
|
|
||||||
~recursive_mutex() = default;
|
|
||||||
|
|
||||||
recursive_mutex(const recursive_mutex&) = delete;
|
|
||||||
recursive_mutex& operator=(const recursive_mutex&) = delete;
|
|
||||||
|
|
||||||
void
|
|
||||||
lock()
|
|
||||||
{
|
|
||||||
rmutexLock(&_M_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
try_lock() noexcept
|
|
||||||
{
|
|
||||||
return rmutexTryLock(&_M_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
unlock()
|
|
||||||
{
|
|
||||||
rmutexUnlock(&_M_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
native_handle_type
|
|
||||||
native_handle() noexcept
|
|
||||||
{ return &_M_mutex; }
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
#ifndef _NXSTL_THREAD
|
|
||||||
#define _NXSTL_THREAD 1
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
extern "C" {
|
|
||||||
#include <switch/kernel/thread.h>
|
|
||||||
#include <switch/arm/tls.h>
|
|
||||||
#include <switch/result.h>
|
|
||||||
}
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace std _GLIBCXX_VISIBILITY(default)
|
|
||||||
{
|
|
||||||
|
|
||||||
/// thread
|
|
||||||
class thread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Abstract base class for types that wrap arbitrary functors to be
|
|
||||||
// invoked in the new thread of execution.
|
|
||||||
struct _State
|
|
||||||
{
|
|
||||||
virtual ~_State() = default;
|
|
||||||
virtual void _M_run() = 0;
|
|
||||||
};
|
|
||||||
using _State_ptr = unique_ptr<_State>;
|
|
||||||
|
|
||||||
typedef Thread native_handle_type;
|
|
||||||
|
|
||||||
/// thread::id
|
|
||||||
class id
|
|
||||||
{
|
|
||||||
native_handle_type _M_thread;
|
|
||||||
|
|
||||||
public:
|
|
||||||
id() noexcept : _M_thread() { }
|
|
||||||
|
|
||||||
explicit
|
|
||||||
id(native_handle_type __id) : _M_thread(__id) { }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class thread;
|
|
||||||
friend class hash<thread::id>;
|
|
||||||
|
|
||||||
friend bool
|
|
||||||
operator==(thread::id __x, thread::id __y) noexcept;
|
|
||||||
|
|
||||||
friend bool
|
|
||||||
operator<(thread::id __x, thread::id __y) noexcept;
|
|
||||||
|
|
||||||
template<class _CharT, class _Traits>
|
|
||||||
friend basic_ostream<_CharT, _Traits>&
|
|
||||||
operator<<(basic_ostream<_CharT, _Traits>& __out, thread::id __id);
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
id _M_id;
|
|
||||||
|
|
||||||
public:
|
|
||||||
thread() noexcept = default;
|
|
||||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
|
||||||
// 2097. packaged_task constructors should be constrained
|
|
||||||
thread(thread&) = delete;
|
|
||||||
thread(const thread&) = delete;
|
|
||||||
thread(const thread&&) = delete;
|
|
||||||
|
|
||||||
thread(thread&& __t) noexcept
|
|
||||||
{ swap(__t); }
|
|
||||||
|
|
||||||
template<typename _Callable, typename... _Args>
|
|
||||||
explicit
|
|
||||||
thread(_Callable&& __f, _Args&&... __args)
|
|
||||||
{
|
|
||||||
_M_start_thread(_S_make_state(
|
|
||||||
__make_invoker(std::forward<_Callable>(__f),
|
|
||||||
std::forward<_Args>(__args)...)));
|
|
||||||
}
|
|
||||||
|
|
||||||
~thread()
|
|
||||||
{
|
|
||||||
if (joinable())
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
thread& operator=(const thread&) = delete;
|
|
||||||
|
|
||||||
thread& operator=(thread&& __t) noexcept
|
|
||||||
{
|
|
||||||
if (joinable())
|
|
||||||
std::terminate();
|
|
||||||
swap(__t);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
swap(thread& __t) noexcept
|
|
||||||
{ std::swap(_M_id, __t._M_id); }
|
|
||||||
|
|
||||||
bool
|
|
||||||
joinable() const noexcept
|
|
||||||
{ return !(_M_id == id()); }
|
|
||||||
|
|
||||||
void
|
|
||||||
join()
|
|
||||||
{
|
|
||||||
threadWaitForExit(&_M_id._M_thread);
|
|
||||||
_M_id = id();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
detach()
|
|
||||||
{
|
|
||||||
_M_id = id();
|
|
||||||
}
|
|
||||||
|
|
||||||
thread::id
|
|
||||||
get_id() const noexcept
|
|
||||||
{ return _M_id; }
|
|
||||||
|
|
||||||
/** @pre thread is joinable
|
|
||||||
*/
|
|
||||||
native_handle_type
|
|
||||||
native_handle()
|
|
||||||
{ return _M_id._M_thread; }
|
|
||||||
|
|
||||||
// Returns a value that hints at the number of hardware thread contexts.
|
|
||||||
static unsigned int
|
|
||||||
hardware_concurrency() noexcept
|
|
||||||
{
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<typename _Callable>
|
|
||||||
struct _State_impl : public _State
|
|
||||||
{
|
|
||||||
_Callable _M_func;
|
|
||||||
|
|
||||||
_State_impl(_Callable&& __f) : _M_func(std::forward<_Callable>(__f))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void
|
|
||||||
_M_run() { _M_func(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
execute_native_thread_routine(void *arg)
|
|
||||||
{
|
|
||||||
reinterpret_cast<_State*>(arg)->_M_run();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
_M_start_thread(_State_ptr state)
|
|
||||||
{
|
|
||||||
Result res = threadCreate(&_M_id._M_thread, execute_native_thread_routine,
|
|
||||||
state.get(), 8192, 0x2B, -2);
|
|
||||||
if (R_FAILED(res))
|
|
||||||
__throw_system_error(res);
|
|
||||||
res = threadStart(&_M_id._M_thread);
|
|
||||||
if (R_FAILED(res))
|
|
||||||
__throw_system_error(res);
|
|
||||||
state.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Callable>
|
|
||||||
static _State_ptr
|
|
||||||
_S_make_state(_Callable&& __f)
|
|
||||||
{
|
|
||||||
using _Impl = _State_impl<_Callable>;
|
|
||||||
return _State_ptr{new _Impl{std::forward<_Callable>(__f)}};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// A call wrapper that does INVOKE(forwarded tuple elements...)
|
|
||||||
template<typename _Tuple>
|
|
||||||
struct _Invoker
|
|
||||||
{
|
|
||||||
_Tuple _M_t;
|
|
||||||
|
|
||||||
template<size_t _Index>
|
|
||||||
static __tuple_element_t<_Index, _Tuple>&&
|
|
||||||
_S_declval();
|
|
||||||
|
|
||||||
template<size_t... _Ind>
|
|
||||||
auto
|
|
||||||
_M_invoke(_Index_tuple<_Ind...>)
|
|
||||||
noexcept(noexcept(std::__invoke(_S_declval<_Ind>()...)))
|
|
||||||
-> decltype(std::__invoke(_S_declval<_Ind>()...))
|
|
||||||
{ return std::__invoke(std::get<_Ind>(std::move(_M_t))...); }
|
|
||||||
|
|
||||||
using _Indices
|
|
||||||
= typename _Build_index_tuple<tuple_size<_Tuple>::value>::__type;
|
|
||||||
|
|
||||||
auto
|
|
||||||
operator()()
|
|
||||||
noexcept(noexcept(std::declval<_Invoker&>()._M_invoke(_Indices())))
|
|
||||||
-> decltype(std::declval<_Invoker&>()._M_invoke(_Indices()))
|
|
||||||
{ return _M_invoke(_Indices()); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename... _Tp>
|
|
||||||
using __decayed_tuple = tuple<typename std::decay<_Tp>::type...>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Returns a call wrapper that stores
|
|
||||||
// tuple{DECAY_COPY(__callable), DECAY_COPY(__args)...}.
|
|
||||||
template<typename _Callable, typename... _Args>
|
|
||||||
static _Invoker<__decayed_tuple<_Callable, _Args...>>
|
|
||||||
__make_invoker(_Callable&& __callable, _Args&&... __args)
|
|
||||||
{
|
|
||||||
return { __decayed_tuple<_Callable, _Args...>{
|
|
||||||
std::forward<_Callable>(__callable), std::forward<_Args>(__args)...
|
|
||||||
} };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void
|
|
||||||
swap(thread& __x, thread& __y) noexcept
|
|
||||||
{ __x.swap(__y); }
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator==(thread::id __x, thread::id __y) noexcept
|
|
||||||
{
|
|
||||||
// pthread_equal is undefined if either thread ID is not valid, so we
|
|
||||||
// can't safely use __gthread_equal on default-constructed values (nor
|
|
||||||
// the non-zero value returned by this_thread::get_id() for
|
|
||||||
// single-threaded programs using GNU libc). Assume EqualityComparable.
|
|
||||||
return __x._M_thread.handle == __y._M_thread.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator!=(thread::id __x, thread::id __y) noexcept
|
|
||||||
{ return !(__x == __y); }
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator<(thread::id __x, thread::id __y) noexcept
|
|
||||||
{
|
|
||||||
// Pthreads doesn't define any way to do this, so we just have to
|
|
||||||
// assume native_handle_type is LessThanComparable.
|
|
||||||
return __x._M_thread.handle < __y._M_thread.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator<=(thread::id __x, thread::id __y) noexcept
|
|
||||||
{ return !(__y < __x); }
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator>(thread::id __x, thread::id __y) noexcept
|
|
||||||
{ return __y < __x; }
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator>=(thread::id __x, thread::id __y) noexcept
|
|
||||||
{ return !(__x < __y); }
|
|
||||||
|
|
||||||
// DR 889.
|
|
||||||
/// std::hash specialization for thread::id.
|
|
||||||
template<>
|
|
||||||
struct hash<thread::id>
|
|
||||||
: public __hash_base<size_t, thread::id>
|
|
||||||
{
|
|
||||||
size_t
|
|
||||||
operator()(const thread::id& __id) const noexcept
|
|
||||||
{ return std::_Hash_impl::hash(__id._M_thread); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class _CharT, class _Traits>
|
|
||||||
inline basic_ostream<_CharT, _Traits>&
|
|
||||||
operator<<(basic_ostream<_CharT, _Traits>& __out, thread::id __id)
|
|
||||||
{
|
|
||||||
if (__id == thread::id())
|
|
||||||
return __out << "thread::id of a non-executing thread";
|
|
||||||
else
|
|
||||||
return __out << __id._M_thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace this_thread
|
|
||||||
{
|
|
||||||
/// get_id
|
|
||||||
inline thread::id
|
|
||||||
get_id() noexcept
|
|
||||||
{
|
|
||||||
Thread ret;
|
|
||||||
uint8_t* tls = (uint8_t*)armGetTls();
|
|
||||||
uint8_t* threadCtx = *(uint8_t**)(tls + 0x1F8);
|
|
||||||
ret.handle = *(Handle*)(threadCtx + 0x1B8);
|
|
||||||
ret.stack_mem = *(void**)(threadCtx + 0x48);
|
|
||||||
ret.stack_mirror = *(void**)(threadCtx + 0x50);
|
|
||||||
ret.stack_sz = *(size_t*)(threadCtx + 0x58);
|
|
||||||
return thread::id(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
584
lib/logvisor.cpp
584
lib/logvisor.cpp
@@ -11,7 +11,7 @@
|
|||||||
#include <TlHelp32.h>
|
#include <TlHelp32.h>
|
||||||
#elif defined(__SWITCH__)
|
#elif defined(__SWITCH__)
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "nxstl/thread"
|
#include <switch.h>
|
||||||
#else
|
#else
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -31,9 +31,15 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <signal.h>
|
#include <csignal>
|
||||||
|
#include <locale>
|
||||||
|
#include <optional>
|
||||||
#include "logvisor/logvisor.hpp"
|
#include "logvisor/logvisor.hpp"
|
||||||
|
|
||||||
|
#if SENTRY_ENABLED
|
||||||
|
#include <sentry.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ANSI sequences */
|
/* ANSI sequences */
|
||||||
#define RED "\x1b[1;31m"
|
#define RED "\x1b[1;31m"
|
||||||
#define YELLOW "\x1b[1;33m"
|
#define YELLOW "\x1b[1;33m"
|
||||||
@@ -47,7 +53,9 @@
|
|||||||
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
||||||
|
#endif
|
||||||
|
|
||||||
void logvisorBp() {}
|
void logvisorBp() {}
|
||||||
|
|
||||||
@@ -91,32 +99,32 @@ void RegisterThreadName(const char* name) {
|
|||||||
|
|
||||||
void KillProcessTree() {
|
void KillProcessTree() {
|
||||||
DWORD myprocID = GetCurrentProcessId();
|
DWORD myprocID = GetCurrentProcessId();
|
||||||
PROCESSENTRY32 pe = {};
|
PROCESSENTRY32W pe = {};
|
||||||
pe.dwSize = sizeof(PROCESSENTRY32);
|
pe.dwSize = sizeof(pe);
|
||||||
|
|
||||||
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
|
||||||
if (::Process32First(hSnap, &pe)) {
|
if (::Process32FirstW(hSnap, &pe) == TRUE) {
|
||||||
BOOL bContinue = TRUE;
|
BOOL bContinue = TRUE;
|
||||||
|
|
||||||
// kill child processes
|
// kill child processes
|
||||||
while (bContinue) {
|
while (bContinue == TRUE) {
|
||||||
// only kill child processes and let console window remain
|
// only kill child processes and let console window remain
|
||||||
if (pe.th32ParentProcessID == myprocID && wcscmp(pe.szExeFile, L"conhost.exe")) {
|
if (pe.th32ParentProcessID == myprocID && std::wcscmp(pe.szExeFile, L"conhost.exe") != 0) {
|
||||||
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
|
HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
|
||||||
|
|
||||||
if (hChildProc) {
|
if (hChildProc != nullptr) {
|
||||||
::TerminateProcess(hChildProc, 1);
|
::TerminateProcess(hChildProc, 1);
|
||||||
::CloseHandle(hChildProc);
|
::CloseHandle(hChildProc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bContinue = ::Process32Next(hSnap, &pe);
|
bContinue = ::Process32NextW(hSnap, &pe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logvisorAbort() {
|
[[noreturn]] void logvisorAbort() {
|
||||||
#if !WINDOWS_STORE
|
#if !WINDOWS_STORE
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
void* stack[100];
|
void* stack[100];
|
||||||
@@ -125,16 +133,16 @@ void logvisorAbort() {
|
|||||||
HANDLE process;
|
HANDLE process;
|
||||||
|
|
||||||
process = GetCurrentProcess();
|
process = GetCurrentProcess();
|
||||||
SymInitialize(process, NULL, TRUE);
|
SymInitialize(process, nullptr, TRUE);
|
||||||
frames = CaptureStackBackTrace(0, 100, stack, NULL);
|
frames = CaptureStackBackTrace(0, 100, stack, nullptr);
|
||||||
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
|
symbol = static_cast<SYMBOL_INFO*>(calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1));
|
||||||
symbol->MaxNameLen = 255;
|
symbol->MaxNameLen = 255;
|
||||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||||
|
|
||||||
for (i = 0; i < frames; i++) {
|
for (i = 0; i < frames; i++) {
|
||||||
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
|
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
|
||||||
|
|
||||||
fprintf(stderr, "%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
|
std::fwprintf(stderr, L"%i: %S - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
|
||||||
|
|
||||||
DWORD dwDisplacement;
|
DWORD dwDisplacement;
|
||||||
IMAGEHLP_LINE64 line;
|
IMAGEHLP_LINE64 line;
|
||||||
@@ -143,14 +151,14 @@ void logvisorAbort() {
|
|||||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
|
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
|
||||||
// SymGetLineFromAddr64 returned success
|
// SymGetLineFromAddr64 returned success
|
||||||
fprintf(stderr, " LINE %d\n", int(line.LineNumber));
|
std::fwprintf(stderr, L" LINE %d\n", int(line.LineNumber));
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "\n");
|
std::fputwc('\n', stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fflush(stderr);
|
std::fflush(stderr);
|
||||||
free(symbol);
|
std::free(symbol);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -167,13 +175,21 @@ void logvisorAbort() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__SWITCH__)
|
#elif defined(__SWITCH__)
|
||||||
void logvisorAbort() { exit(1); }
|
[[noreturn]] void logvisorAbort() {
|
||||||
|
MainLoggers.clear();
|
||||||
|
nvExit();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
#elif defined(EMSCRIPTEN)
|
||||||
|
[[noreturn]] void logvisorAbort() {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void KillProcessTree() {}
|
void KillProcessTree() {}
|
||||||
|
|
||||||
#include <execinfo.h>
|
#include <execinfo.h>
|
||||||
void logvisorAbort() {
|
[[noreturn]] void logvisorAbort() {
|
||||||
void* array[128];
|
void* array[128];
|
||||||
size_t size = backtrace(array, 128);
|
size_t size = backtrace(array, 128);
|
||||||
|
|
||||||
@@ -185,20 +201,20 @@ void logvisorAbort() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
std::string cmdLineStr = fmt::format(fmt("atos -p {}"), getpid());
|
std::string cmdLineStr = fmt::format(FMT_STRING("atos -p {}"), getpid());
|
||||||
#else
|
#else
|
||||||
std::string cmdLineStr = fmt::format(fmt("2>/dev/null addr2line -C -f -e \"{}\""), exeNameBuffer);
|
std::string cmdLineStr = fmt::format(FMT_STRING("2>/dev/null addr2line -C -f -e \"{}\""), exeNameBuffer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
#if __linux__
|
#if __linux__
|
||||||
Dl_info dlip;
|
Dl_info dlip;
|
||||||
if (dladdr(array[i], &dlip))
|
if (dladdr(array[i], &dlip))
|
||||||
cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
|
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
|
||||||
else
|
else
|
||||||
cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)array[i]);
|
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)array[i]);
|
||||||
#else
|
#else
|
||||||
cmdLineStr += fmt::format(fmt(" 0x{:016X}"), (uintptr_t)array[i]);
|
cmdLineStr += fmt::format(FMT_STRING(" 0x{:016X}"), (uintptr_t)array[i]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,33 +258,33 @@ void logvisorAbort() {
|
|||||||
LogMutex _LogMutex;
|
LogMutex _LogMutex;
|
||||||
|
|
||||||
static void AbortHandler(int signum) {
|
static void AbortHandler(int signum) {
|
||||||
_LogMutex.enabled = false;
|
_LogMutex.enabled = false;
|
||||||
switch (signum) {
|
switch (signum) {
|
||||||
case SIGSEGV:
|
case SIGSEGV:
|
||||||
Log.report(logvisor::Fatal, fmt("Segmentation Fault"));
|
Log.report(logvisor::Fatal, FMT_STRING("Segmentation Fault"));
|
||||||
break;
|
break;
|
||||||
case SIGILL:
|
case SIGILL:
|
||||||
Log.report(logvisor::Fatal, fmt("Bad Execution"));
|
Log.report(logvisor::Fatal, FMT_STRING("Bad Execution"));
|
||||||
break;
|
break;
|
||||||
case SIGFPE:
|
case SIGFPE:
|
||||||
Log.report(logvisor::Fatal, fmt("Floating Point Exception"));
|
Log.report(logvisor::Fatal, FMT_STRING("Floating Point Exception"));
|
||||||
break;
|
break;
|
||||||
case SIGABRT:
|
case SIGABRT:
|
||||||
Log.report(logvisor::Fatal, fmt("Abort Signal"));
|
Log.report(logvisor::Fatal, FMT_STRING("Abort Signal"));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.report(logvisor::Fatal, fmt("unknown signal {}"), signum);
|
Log.report(logvisor::Fatal, FMT_STRING("unknown signal {}"), signum);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t _LogCounter;
|
uint64_t _LogCounter;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<ILogger>> MainLoggers;
|
std::vector<std::unique_ptr<ILogger>> MainLoggers;
|
||||||
std::atomic_size_t ErrorCount(0);
|
std::atomic_size_t ErrorCount(0);
|
||||||
static std::chrono::steady_clock MonoClock;
|
using MonoClock = std::chrono::steady_clock;
|
||||||
static std::chrono::steady_clock::time_point GlobalStart = MonoClock.now();
|
static MonoClock::time_point GlobalStart = MonoClock::now();
|
||||||
static inline std::chrono::steady_clock::duration CurrentUptime() { return MonoClock.now() - GlobalStart; }
|
static inline MonoClock::duration CurrentUptime() { return MonoClock::now() - GlobalStart; }
|
||||||
std::atomic_uint_fast64_t FrameIndex(0);
|
std::atomic_uint_fast64_t FrameIndex(0);
|
||||||
|
|
||||||
static inline int ConsoleWidth() {
|
static inline int ConsoleWidth() {
|
||||||
@@ -291,6 +307,233 @@ static inline int ConsoleWidth() {
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LOGVISOR_NX_LM
|
||||||
|
|
||||||
|
struct ConsoleLogger : public ILogger {
|
||||||
|
Service m_svc{};
|
||||||
|
Service m_logger{};
|
||||||
|
bool m_ready = false;
|
||||||
|
|
||||||
|
struct MessageHeader {
|
||||||
|
enum Flags : u32 {
|
||||||
|
IsHead = 1,
|
||||||
|
IsTail = 2,
|
||||||
|
};
|
||||||
|
enum Severity : u32 {
|
||||||
|
Trace,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
Critical,
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 pid;
|
||||||
|
u64 thread_context;
|
||||||
|
//union {
|
||||||
|
//BitField<0, 16, Flags> flags;
|
||||||
|
//BitField<16, 8, Severity> severity;
|
||||||
|
//BitField<24, 8, u32> verbosity;
|
||||||
|
//};
|
||||||
|
u32 flags;
|
||||||
|
u32 payload_size;
|
||||||
|
|
||||||
|
Flags GetFlags() const {
|
||||||
|
return Flags(flags & u32(0xffff));
|
||||||
|
}
|
||||||
|
void SetFlags(Flags f) {
|
||||||
|
flags &= ~u32(0xffff);
|
||||||
|
flags |= f;
|
||||||
|
}
|
||||||
|
Severity GetSeverity() const {
|
||||||
|
return Severity((flags >> u32(16)) & u32(0xff));
|
||||||
|
}
|
||||||
|
void SetSeverity(Severity f) {
|
||||||
|
flags &= ~u32(0xff0000);
|
||||||
|
flags |= f << u32(16);
|
||||||
|
}
|
||||||
|
u32 GetVerbosity() const {
|
||||||
|
return u32((flags >> u32(24)) & u32(0xff));
|
||||||
|
}
|
||||||
|
void SetVerbosity(u32 f) {
|
||||||
|
flags &= ~u32(0xff000000);
|
||||||
|
flags |= f << u32(24);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsHeadLog() const {
|
||||||
|
return flags & IsHead;
|
||||||
|
}
|
||||||
|
bool IsTailLog() const {
|
||||||
|
return flags & IsTail;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
|
||||||
|
|
||||||
|
enum class Field : u8 {
|
||||||
|
Skip = 1,
|
||||||
|
Message = 2,
|
||||||
|
Line = 3,
|
||||||
|
Filename = 4,
|
||||||
|
Function = 5,
|
||||||
|
Module = 6,
|
||||||
|
Thread = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr MessageHeader::Severity LevelToSeverity(Level l) {
|
||||||
|
switch (l) {
|
||||||
|
case Level::Info:
|
||||||
|
default:
|
||||||
|
return MessageHeader::Info;
|
||||||
|
case Level::Warning:
|
||||||
|
return MessageHeader::Warning;
|
||||||
|
case Level::Error:
|
||||||
|
return MessageHeader::Error;
|
||||||
|
case Level::Fatal:
|
||||||
|
return MessageHeader::Critical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleLogger() {
|
||||||
|
if (R_SUCCEEDED(smGetService(&m_svc, "lm"))) {
|
||||||
|
auto pid = getpid();
|
||||||
|
if (R_SUCCEEDED(serviceDispatchIn(&m_svc, 0, pid, .out_num_objects = 1, .out_objects = &m_logger))) {
|
||||||
|
m_ready = true;
|
||||||
|
MessageHeader head{};
|
||||||
|
head.pid = getpid();
|
||||||
|
head.SetFlags(MessageHeader::IsHead);
|
||||||
|
serviceDispatch(&m_logger, 0,
|
||||||
|
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
|
||||||
|
.buffers = { { &head, sizeof(head) } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ConsoleLogger() override {
|
||||||
|
if (m_ready) {
|
||||||
|
MessageHeader head{};
|
||||||
|
head.pid = getpid();
|
||||||
|
head.SetFlags(MessageHeader::IsTail);
|
||||||
|
serviceDispatch(&m_logger, 0,
|
||||||
|
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
|
||||||
|
.buffers = { { &head, sizeof(head) } });
|
||||||
|
}
|
||||||
|
serviceClose(&m_logger);
|
||||||
|
serviceClose(&m_svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendBuffer(const std::vector<u8>& buf) {
|
||||||
|
serviceDispatch(&m_logger, 0,
|
||||||
|
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
|
||||||
|
.buffers = { { buf.data(), buf.size() } });
|
||||||
|
}
|
||||||
|
|
||||||
|
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
||||||
|
if (!m_ready)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::thread::id thrId = std::this_thread::get_id();
|
||||||
|
const char* thrName = nullptr;
|
||||||
|
size_t thrNameSize = 0;
|
||||||
|
if (ThreadMap.find(thrId) != ThreadMap.end()) {
|
||||||
|
thrName = ThreadMap[thrId];
|
||||||
|
thrNameSize = std::min(std::strlen(thrName), size_t(255));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto modNameSize = std::min(std::strlen(modName), size_t(255));
|
||||||
|
auto message = fmt::vformat(format, args);
|
||||||
|
auto messageSize = std::min(message.size(), size_t(255));
|
||||||
|
|
||||||
|
std::vector<u8> bufOut(sizeof(MessageHeader) + (thrNameSize ? 2 + thrNameSize : 0) + 2 + modNameSize + 2 + messageSize, '\0');
|
||||||
|
|
||||||
|
auto it = bufOut.begin();
|
||||||
|
|
||||||
|
auto& head = *reinterpret_cast<MessageHeader*>(&*it);
|
||||||
|
head.pid = getpid();
|
||||||
|
head.payload_size = bufOut.size() - sizeof(MessageHeader);
|
||||||
|
head.SetSeverity(LevelToSeverity(severity));
|
||||||
|
it += sizeof(MessageHeader);
|
||||||
|
|
||||||
|
if (thrNameSize) {
|
||||||
|
*it++ = u8(Field::Thread);
|
||||||
|
*it++ = thrNameSize;
|
||||||
|
std::memcpy(&*it, thrName, thrNameSize);
|
||||||
|
it += thrNameSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
*it++ = u8(Field::Module);
|
||||||
|
*it++ = modNameSize;
|
||||||
|
std::memcpy(&*it, modName, modNameSize);
|
||||||
|
it += modNameSize;
|
||||||
|
|
||||||
|
*it++ = u8(Field::Message);
|
||||||
|
*it++ = messageSize;
|
||||||
|
std::memcpy(&*it, message.data(), messageSize);
|
||||||
|
it += messageSize;
|
||||||
|
|
||||||
|
SendBuffer(bufOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
||||||
|
fmt::format_args args) override {
|
||||||
|
if (!m_ready)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::thread::id thrId = std::this_thread::get_id();
|
||||||
|
const char* thrName = nullptr;
|
||||||
|
size_t thrNameSize = 0;
|
||||||
|
if (ThreadMap.find(thrId) != ThreadMap.end()) {
|
||||||
|
thrName = ThreadMap[thrId];
|
||||||
|
thrNameSize = std::min(std::strlen(thrName), size_t(255));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto modNameSize = std::min(std::strlen(modName), size_t(255));
|
||||||
|
auto fileNameSize = std::min(std::strlen(file), size_t(255));
|
||||||
|
auto message = fmt::vformat(format, args);
|
||||||
|
auto messageSize = std::min(message.size(), size_t(255));
|
||||||
|
|
||||||
|
std::vector<u8> bufOut(sizeof(MessageHeader) + (thrNameSize ? 2 + thrNameSize : 0) + 2 + modNameSize + 2 + fileNameSize + 3 + 4 + 2 + messageSize, '\0');
|
||||||
|
|
||||||
|
auto it = bufOut.begin();
|
||||||
|
|
||||||
|
auto& head = *reinterpret_cast<MessageHeader*>(&*it);
|
||||||
|
head.pid = getpid();
|
||||||
|
head.payload_size = bufOut.size() - sizeof(MessageHeader);
|
||||||
|
head.SetSeverity(LevelToSeverity(severity));
|
||||||
|
it += sizeof(MessageHeader);
|
||||||
|
|
||||||
|
if (thrNameSize) {
|
||||||
|
*it++ = u8(Field::Thread);
|
||||||
|
*it++ = thrNameSize;
|
||||||
|
std::memcpy(&*it, thrName, thrNameSize);
|
||||||
|
it += thrNameSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
*it++ = u8(Field::Module);
|
||||||
|
*it++ = modNameSize;
|
||||||
|
std::memcpy(&*it, modName, modNameSize);
|
||||||
|
it += modNameSize;
|
||||||
|
|
||||||
|
*it++ = u8(Field::Filename);
|
||||||
|
*it++ = fileNameSize;
|
||||||
|
std::memcpy(&*it, file, fileNameSize);
|
||||||
|
it += fileNameSize;
|
||||||
|
|
||||||
|
*it++ = u8(Field::Line);
|
||||||
|
*it++ = 4;
|
||||||
|
*it++ = u8(Field::Skip);
|
||||||
|
std::memcpy(&*it, &linenum, 4);
|
||||||
|
it += 4;
|
||||||
|
|
||||||
|
*it++ = u8(Field::Message);
|
||||||
|
*it++ = messageSize;
|
||||||
|
std::memcpy(&*it, message.data(), messageSize);
|
||||||
|
it += messageSize;
|
||||||
|
|
||||||
|
SendBuffer(bufOut);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
static HANDLE Term = 0;
|
static HANDLE Term = 0;
|
||||||
#else
|
#else
|
||||||
@@ -298,7 +541,7 @@ static const char* Term = nullptr;
|
|||||||
#endif
|
#endif
|
||||||
bool XtermColor = false;
|
bool XtermColor = false;
|
||||||
struct ConsoleLogger : public ILogger {
|
struct ConsoleLogger : public ILogger {
|
||||||
ConsoleLogger() {
|
ConsoleLogger() : ILogger(log_typeid(ConsoleLogger)) {
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#if !WINDOWS_STORE
|
#if !WINDOWS_STORE
|
||||||
const char* conemuANSI = getenv("ConEmuANSI");
|
const char* conemuANSI = getenv("ConEmuANSI");
|
||||||
@@ -317,29 +560,26 @@ struct ConsoleLogger : public ILogger {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
~ConsoleLogger() override = default;
|
||||||
|
|
||||||
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
static void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
||||||
/* Clear current line out */
|
/* Clear current line out */
|
||||||
int width = ConsoleWidth();
|
// std::fprintf(stderr, "\r%*c\r", ConsoleWidth(), ' ');
|
||||||
std::fputs("\r", stderr);
|
|
||||||
for (int w = 0; w < width; ++w)
|
|
||||||
std::fputs(" ", stderr);
|
|
||||||
std::fputs("\r", stderr);
|
|
||||||
|
|
||||||
std::chrono::steady_clock::duration tm = CurrentUptime();
|
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
||||||
double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
||||||
(double)std::chrono::steady_clock::duration::period::den;
|
static_cast<double>(std::chrono::steady_clock::duration::period::den);
|
||||||
std::thread::id thrId = std::this_thread::get_id();
|
const std::thread::id thrId = std::this_thread::get_id();
|
||||||
const char* thrName = nullptr;
|
const char* thrName = nullptr;
|
||||||
if (ThreadMap.find(thrId) != ThreadMap.end())
|
if (ThreadMap.find(thrId) != ThreadMap.end())
|
||||||
thrName = ThreadMap[thrId];
|
thrName = ThreadMap[thrId];
|
||||||
|
|
||||||
if (XtermColor) {
|
if (XtermColor) {
|
||||||
std::fputs(BOLD "[", stderr);
|
std::fputs(BOLD "[", stderr);
|
||||||
fmt::print(stderr, fmt(GREEN "{:5.4} "), tmd);
|
fmt::print(stderr, FMT_STRING(GREEN "{:.4f} "), tmd);
|
||||||
uint_fast64_t fIdx = FrameIndex.load();
|
const uint_fast64_t fIdx = FrameIndex.load();
|
||||||
if (fIdx)
|
if (fIdx != 0)
|
||||||
fmt::print(stderr, fmt("({}) "), fIdx);
|
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case Info:
|
case Info:
|
||||||
std::fputs(BOLD CYAN "INFO", stderr);
|
std::fputs(BOLD CYAN "INFO", stderr);
|
||||||
@@ -356,60 +596,60 @@ struct ConsoleLogger : public ILogger {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
fmt::print(stderr, fmt(NORMAL BOLD " {}"), modName);
|
fmt::print(stderr, FMT_STRING(NORMAL BOLD " {}"), modName);
|
||||||
if (sourceInfo)
|
if (sourceInfo)
|
||||||
fmt::print(stderr, fmt(BOLD YELLOW " {{}}"), sourceInfo);
|
fmt::print(stderr, FMT_STRING(BOLD YELLOW " {{}}"), sourceInfo);
|
||||||
if (thrName)
|
if (thrName)
|
||||||
fmt::print(stderr, fmt(BOLD MAGENTA " ({})"), thrName);
|
fmt::print(stderr, FMT_STRING(BOLD MAGENTA " ({})"), thrName);
|
||||||
std::fputs(NORMAL BOLD "] " NORMAL, stderr);
|
std::fputs(NORMAL BOLD "] " NORMAL, stderr);
|
||||||
} else {
|
} else {
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#if !WINDOWS_STORE
|
#if !WINDOWS_STORE
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||||
fprintf(stderr, "[");
|
std::fputc('[', stderr);
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
|
||||||
fprintf(stderr, "%5.4f ", tmd);
|
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
|
||||||
uint64_t fi = FrameIndex.load();
|
const uint64_t fi = FrameIndex.load();
|
||||||
if (fi)
|
if (fi != 0)
|
||||||
fprintf(stderr, "(%" PRIu64 ") ", fi);
|
std::fprintf(stderr, "(%" PRIu64 ") ", fi);
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case Info:
|
case Info:
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||||
fprintf(stderr, "INFO");
|
std::fputs("INFO", stderr);
|
||||||
break;
|
break;
|
||||||
case Warning:
|
case Warning:
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
||||||
fprintf(stderr, "WARNING");
|
std::fputs("WARNING", stderr);
|
||||||
break;
|
break;
|
||||||
case Error:
|
case Error:
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
||||||
fprintf(stderr, "ERROR");
|
std::fputs("ERROR", stderr);
|
||||||
break;
|
break;
|
||||||
case Fatal:
|
case Fatal:
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
|
||||||
fprintf(stderr, "FATAL ERROR");
|
std::fputs("FATAL ERROR", stderr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
}
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||||
fprintf(stderr, " %s", modName);
|
fmt::print(stderr, FMT_STRING(" {}"), modName);
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
|
||||||
if (sourceInfo)
|
if (sourceInfo)
|
||||||
fprintf(stderr, " {%s}", sourceInfo);
|
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
|
||||||
if (thrName)
|
if (thrName)
|
||||||
fprintf(stderr, " (%s)", thrName);
|
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
|
||||||
fprintf(stderr, "] ");
|
std::fputs("] ", stderr);
|
||||||
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
|
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
std::fputs("[", stderr);
|
std::fputc('[', stderr);
|
||||||
fmt::print(stderr, fmt("{:5.4} "), tmd);
|
fmt::print(stderr, FMT_STRING("{:.4f} "), tmd);
|
||||||
uint_fast64_t fIdx = FrameIndex.load();
|
uint_fast64_t fIdx = FrameIndex.load();
|
||||||
if (fIdx)
|
if (fIdx)
|
||||||
fmt::print(stderr, fmt("({}) "), fIdx);
|
fmt::print(stderr, FMT_STRING("({}) "), fIdx);
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case Info:
|
case Info:
|
||||||
std::fputs("INFO", stderr);
|
std::fputs("INFO", stderr);
|
||||||
@@ -425,51 +665,51 @@ struct ConsoleLogger : public ILogger {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
}
|
||||||
fmt::print(stderr, fmt(" {}"), modName);
|
fmt::print(stderr, FMT_STRING(" {}"), modName);
|
||||||
if (sourceInfo)
|
if (sourceInfo)
|
||||||
fmt::print(stderr, fmt(" {{}}"), sourceInfo);
|
fmt::print(stderr, FMT_STRING(" {{}}"), sourceInfo);
|
||||||
if (thrName)
|
if (thrName)
|
||||||
fmt::print(stderr, fmt(" ({})"), thrName);
|
fmt::print(stderr, FMT_STRING(" ({})"), thrName);
|
||||||
std::fputs("] ", stderr);
|
std::fputs("] ", stderr);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) {
|
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
||||||
_reportHead(modName, nullptr, severity);
|
_reportHead(modName, nullptr, severity);
|
||||||
fmt::vprint(stderr, format, args);
|
fmt::vprint(stderr, format, args);
|
||||||
std::fputs("\n", stderr);
|
std::fputc('\n', stderr);
|
||||||
std::fflush(stderr);
|
std::fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) {
|
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
||||||
_reportHead(modName, nullptr, severity);
|
fmt::format_args args) override {
|
||||||
|
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
||||||
fmt::vprint(stderr, format, args);
|
fmt::vprint(stderr, format, args);
|
||||||
std::fputs("\n", stderr);
|
std::fputc('\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) {
|
|
||||||
_reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity);
|
|
||||||
fmt::vprint(stderr, format, args);
|
|
||||||
std::fputs("\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) {
|
|
||||||
_reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity);
|
|
||||||
fmt::vprint(stderr, format, args);
|
|
||||||
std::fputs("\n", stderr);
|
|
||||||
std::fflush(stderr);
|
std::fflush(stderr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool ConsoleLoggerRegistered = false;
|
||||||
|
|
||||||
void RegisterConsoleLogger() {
|
void RegisterConsoleLogger() {
|
||||||
/* Otherwise construct new console logger */
|
/* Otherwise construct new console logger */
|
||||||
MainLoggers.emplace_back(new ConsoleLogger);
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
@@ -478,9 +718,9 @@ void CreateWin32Console() {
|
|||||||
/* Debug console */
|
/* Debug console */
|
||||||
AllocConsole();
|
AllocConsole();
|
||||||
|
|
||||||
freopen("CONIN$", "r", stdin);
|
std::freopen("CONIN$", "r", stdin);
|
||||||
freopen("CONOUT$", "w", stdout);
|
std::freopen("CONOUT$", "w", stdout);
|
||||||
freopen("CONOUT$", "w", stderr);
|
std::freopen("CONOUT$", "w", stderr);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -492,25 +732,61 @@ void RegisterStandardExceptions() {
|
|||||||
signal(SIGFPE, AbortHandler);
|
signal(SIGFPE, AbortHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SENTRY_ENABLED
|
||||||
|
void RegisterSentry(const char* appName, const char* appVersion, const char* cacheDir) {
|
||||||
|
sentry_options_t *options = sentry_options_new();
|
||||||
|
sentry_options_set_database_path(options, cacheDir);
|
||||||
|
sentry_options_set_auto_session_tracking(options, true);
|
||||||
|
sentry_options_set_symbolize_stacktraces(options, true);
|
||||||
|
sentry_options_set_dsn(options, SENTRY_DSN);
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
sentry_options_set_environment(options, "release");
|
||||||
|
#else
|
||||||
|
sentry_options_set_environment(options, "debug");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string release = fmt::format(FMT_STRING("{}@{}"), appName, appVersion);
|
||||||
|
sentry_options_set_release(options, release.c_str());
|
||||||
|
|
||||||
|
sentry_init(options);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct FileLogger : public ILogger {
|
struct FileLogger : public ILogger {
|
||||||
FILE* fp;
|
FILE* fp = nullptr;
|
||||||
|
FileLogger(uint64_t typeHash) : ILogger(typeHash) {}
|
||||||
virtual void openFile() = 0;
|
virtual void openFile() = 0;
|
||||||
virtual void closeFile() { std::fclose(fp); }
|
void openFileIfNeeded() {
|
||||||
|
if (!fp) {
|
||||||
|
openFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void closeFile() {
|
||||||
|
if (fp) {
|
||||||
|
std::fflush(fp);
|
||||||
|
std::fclose(fp);
|
||||||
|
fp = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual ~FileLogger() { closeFile(); }
|
||||||
|
|
||||||
void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
void _reportHead(const char* modName, const char* sourceInfo, Level severity) {
|
||||||
std::chrono::steady_clock::duration tm = CurrentUptime();
|
const std::chrono::steady_clock::duration tm = CurrentUptime();
|
||||||
double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
const double tmd = tm.count() * std::chrono::steady_clock::duration::period::num /
|
||||||
(double)std::chrono::steady_clock::duration::period::den;
|
static_cast<double>(std::chrono::steady_clock::duration::period::den);
|
||||||
std::thread::id thrId = std::this_thread::get_id();
|
const std::thread::id thrId = std::this_thread::get_id();
|
||||||
const char* thrName = nullptr;
|
const char* thrName = nullptr;
|
||||||
if (ThreadMap.find(thrId) != ThreadMap.end())
|
if (ThreadMap.find(thrId) != ThreadMap.end()) {
|
||||||
thrName = ThreadMap[thrId];
|
thrName = ThreadMap[thrId];
|
||||||
|
}
|
||||||
|
|
||||||
std::fputs("[", fp);
|
std::fputc('[', fp);
|
||||||
std::fprintf(fp, "%5.4f ", tmd);
|
std::fprintf(fp, "%5.4f ", tmd);
|
||||||
uint_fast64_t fIdx = FrameIndex.load();
|
const uint_fast64_t fIdx = FrameIndex.load();
|
||||||
if (fIdx)
|
if (fIdx != 0) {
|
||||||
std::fprintf(fp, "(%" PRIu64 ") ", fIdx);
|
std::fprintf(fp, "(%" PRIu64 ") ", fIdx);
|
||||||
|
}
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case Info:
|
case Info:
|
||||||
std::fputs("INFO", fp);
|
std::fputs("INFO", fp);
|
||||||
@@ -528,52 +804,36 @@ struct FileLogger : public ILogger {
|
|||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
std::fprintf(fp, " %s", modName);
|
std::fprintf(fp, " %s", modName);
|
||||||
if (sourceInfo)
|
if (sourceInfo) {
|
||||||
std::fprintf(fp, " {%s}", sourceInfo);
|
std::fprintf(fp, " {%s}", sourceInfo);
|
||||||
if (thrName)
|
}
|
||||||
|
if (thrName) {
|
||||||
std::fprintf(fp, " (%s)", thrName);
|
std::fprintf(fp, " (%s)", thrName);
|
||||||
|
}
|
||||||
std::fputs("] ", fp);
|
std::fputs("] ", fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) {
|
void report(const char* modName, Level severity, fmt::string_view format, fmt::format_args args) override {
|
||||||
openFile();
|
openFileIfNeeded();
|
||||||
_reportHead(modName, nullptr, severity);
|
_reportHead(modName, nullptr, severity);
|
||||||
fmt::vprint(fp, format, args);
|
fmt::vprint(fp, format, args);
|
||||||
std::fputs("\n", fp);
|
std::fputc('\n', fp);
|
||||||
closeFile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void report(const char* modName, Level severity, fmt::wstring_view format, fmt::wformat_args args) {
|
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum, fmt::string_view format,
|
||||||
openFile();
|
fmt::format_args args) override {
|
||||||
_reportHead(modName, nullptr, severity);
|
openFileIfNeeded();
|
||||||
|
_reportHead(modName, fmt::format(FMT_STRING("{}:{}"), file, linenum).c_str(), severity);
|
||||||
fmt::vprint(fp, format, args);
|
fmt::vprint(fp, format, args);
|
||||||
std::fputs("\n", fp);
|
std::fputc('\n', fp);
|
||||||
closeFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
|
|
||||||
fmt::string_view format, fmt::format_args args) {
|
|
||||||
openFile();
|
|
||||||
_reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity);
|
|
||||||
fmt::vprint(fp, format, args);
|
|
||||||
std::fputs("\n", fp);
|
|
||||||
closeFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reportSource(const char* modName, Level severity, const char* file, unsigned linenum,
|
|
||||||
fmt::wstring_view format, fmt::wformat_args args) {
|
|
||||||
openFile();
|
|
||||||
_reportHead(modName, fmt::format(fmt("{}:{}"), file, linenum).c_str(), severity);
|
|
||||||
fmt::vprint(fp, format, args);
|
|
||||||
std::fputs("\n", fp);
|
|
||||||
closeFile();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileLogger8 : public FileLogger {
|
struct FileLogger8 : public FileLogger {
|
||||||
const char* m_filepath;
|
const char* m_filepath;
|
||||||
FileLogger8(const char* filepath) : m_filepath(filepath) {}
|
explicit FileLogger8(const char* filepath) : FileLogger(log_typeid(FileLogger8)), m_filepath(filepath) {}
|
||||||
void openFile() { fp = std::fopen(m_filepath, "a"); }
|
void openFile() override { fp = std::fopen(m_filepath, "a"); }
|
||||||
|
~FileLogger8() override = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterFileLogger(const char* filepath) {
|
void RegisterFileLogger(const char* filepath) {
|
||||||
@@ -581,28 +841,4 @@ void RegisterFileLogger(const char* filepath) {
|
|||||||
MainLoggers.emplace_back(new FileLogger8(filepath));
|
MainLoggers.emplace_back(new FileLogger8(filepath));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LOG_UCS2
|
|
||||||
|
|
||||||
struct FileLogger16 : public FileLogger {
|
|
||||||
const wchar_t* m_filepath;
|
|
||||||
FileLogger16(const wchar_t* filepath) : m_filepath(filepath) {}
|
|
||||||
void openFile() { fp = _wfopen(m_filepath, L"a"); }
|
|
||||||
};
|
|
||||||
|
|
||||||
void RegisterFileLogger(const wchar_t* filepath) {
|
|
||||||
/* Determine if file logger already added */
|
|
||||||
for (auto& logger : MainLoggers) {
|
|
||||||
FileLogger16* filelogger = dynamic_cast<FileLogger16*>(logger.get());
|
|
||||||
if (filelogger) {
|
|
||||||
if (!wcscmp(filepath, filelogger->m_filepath))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise construct new file logger */
|
|
||||||
MainLoggers.emplace_back(new FileLogger16(filepath));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace logvisor
|
} // namespace logvisor
|
||||||
|
|||||||
Reference in New Issue
Block a user