rpcs3/Utilities/StrFmt.cpp

887 lines
19 KiB
C++
Raw Normal View History

#include "StrFmt.h"
#include "StrUtil.h"
#include "cfmt.h"
#include "util/endian.hpp"
#include "util/v128.hpp"
#include <locale>
#include <codecvt>
2016-05-13 16:01:48 +02:00
#include <algorithm>
#include <string_view>
#include "Thread.h"
2016-08-14 02:22:19 +02:00
#ifdef _WIN32
#include <Windows.h>
#else
#include <errno.h>
#endif
2024-08-01 01:11:54 +02:00
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
std::string wchar_to_utf8(std::wstring_view src)
2020-03-26 21:48:56 +01:00
{
#ifdef _WIN32
2020-03-26 21:48:56 +01:00
std::string utf8_string;
2023-12-29 18:33:29 +01:00
const int size = ::narrow<int>(src.size());
const auto tmp_size = WideCharToMultiByte(CP_UTF8, 0, src.data(), size, nullptr, 0, nullptr, nullptr);
2020-03-26 21:48:56 +01:00
utf8_string.resize(tmp_size);
2023-12-29 18:33:29 +01:00
WideCharToMultiByte(CP_UTF8, 0, src.data(), size, utf8_string.data(), tmp_size, nullptr, nullptr);
2020-03-26 21:48:56 +01:00
return utf8_string;
#else
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter{};
return converter.to_bytes(src.data());
#endif
2020-03-26 21:48:56 +01:00
}
std::string utf16_to_utf8(std::u16string_view src)
{
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter{};
return converter.to_bytes(src.data());
}
std::u16string utf8_to_utf16(std::string_view src)
{
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter{};
return converter.from_bytes(src.data());
}
std::wstring utf8_to_wchar(std::string_view src)
2020-03-26 21:48:56 +01:00
{
#ifdef _WIN32
std::wstring wchar_string;
2023-12-29 18:33:29 +01:00
const int size = ::narrow<int>(src.size());
const auto tmp_size = MultiByteToWideChar(CP_UTF8, 0, src.data(), size, nullptr, 0);
wchar_string.resize(tmp_size);
2023-12-29 18:33:29 +01:00
MultiByteToWideChar(CP_UTF8, 0, src.data(), size, wchar_string.data(), tmp_size);
return wchar_string;
#else
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter{};
return converter.from_bytes(src.data());
#endif
2020-03-26 21:48:56 +01:00
}
2024-08-01 01:11:54 +02:00
#ifdef _MSC_VER
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#else
#pragma GCC diagnostic pop
#endif
2022-03-16 19:59:20 +01:00
#ifdef _WIN32
2022-03-16 20:44:47 +01:00
std::string fmt::win_error_to_string(unsigned long error, void* module_handle)
2022-03-16 19:59:20 +01:00
{
std::string message;
LPWSTR message_buffer = nullptr;
2022-03-16 20:44:47 +01:00
if (FormatMessageW((module_handle ? FORMAT_MESSAGE_FROM_HMODULE : FORMAT_MESSAGE_FROM_SYSTEM) | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
module_handle, error, 0, reinterpret_cast<LPWSTR>(&message_buffer), 0, nullptr))
2022-03-16 19:59:20 +01:00
{
message = fmt::format("%s (0x%x)", fmt::trim(wchar_to_utf8(message_buffer), " \t\n\r\f\v"), error);
}
else
{
message = fmt::format("0x%x", error);
}
if (message_buffer)
{
LocalFree(message_buffer);
}
return message;
}
2023-03-04 11:05:32 +01:00
std::string fmt::win_error_to_string(const fmt::win_error& error)
{
return fmt::win_error_to_string(error.error, error.module_handle);
}
template <>
void fmt_class_string<fmt::win_error>::format(std::string& out, u64 arg)
{
fmt::append(out, "%s", fmt::win_error_to_string(get_object(arg)));
}
2020-03-26 21:48:56 +01:00
#endif
2017-02-02 18:47:25 +01:00
template <>
void fmt_class_string<std::pair<const fmt_type_info*, u64>>::format(std::string& out, u64 arg)
{
// Dynamic format arg
const auto& pair = get_object(arg);
if (pair.first)
{
pair.first->fmt_string(out, pair.second);
}
}
2018-03-22 20:48:38 +01:00
template <>
void fmt_class_string<fmt::base57>::format(std::string& out, u64 arg)
{
const auto& _arg = get_object(arg);
if (_arg.data && _arg.size)
{
// Precomputed tail sizes if input data is not multiple of 8
static constexpr u8 s_tail[8] = {0, 2, 3, 5, 6, 7, 9, 10};
// Get full output size
2020-12-18 08:39:54 +01:00
const usz out_size = _arg.size / 8 * 11 + s_tail[_arg.size % 8];
2018-03-22 20:48:38 +01:00
out.resize(out.size() + out_size);
const auto ptr = &out.front() + (out.size() - out_size);
// Each 8 bytes of input data produce 11 bytes of base57 output
2020-12-18 08:39:54 +01:00
for (usz i = 0, p = 0; i < _arg.size; i += 8, p += 11)
2018-03-22 20:48:38 +01:00
{
// Load up to 8 bytes
be_t<u64> be_value;
if (_arg.size - i < sizeof(be_value))
{
std::memset(&be_value, 0, sizeof(be_value));
std::memcpy(&be_value, _arg.data + i, _arg.size - i);
}
else
{
std::memcpy(&be_value, _arg.data + i, sizeof(be_value));
}
u64 value = be_value;
for (int j = 10; j >= 0; j--)
{
if (p + j < out_size)
{
ptr[p + j] = "0123456789ACEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"[value % 57];
}
value /= 57;
}
}
}
}
2024-07-27 08:49:22 +02:00
fmt::base57_result fmt::base57_result::from_string(std::string_view str)
{
fmt::base57_result result(str.size() / 11 * 8 + (str.size() % 11 ? 8 : 0));
// Each 11 chars of input produces 8 bytes of byte output
for (usz i = 0, p = 0; i < result.size; i += 8, p += 11)
{
// Load up to 8 bytes
const std::string_view be_value = str.substr(p);
be_t<u64> value = 0;
for (u64 j = 10, multiplier = 0; j != umax; j--)
{
if (multiplier == 0)
{
multiplier = 1;
}
else
{
// Do it first to avoid overflow
multiplier *= 57;
}
if (j < be_value.size())
{
auto to_val = [](u8 c) -> u64
{
if (std::isdigit(c))
{
return c - '0';
}
if (std::isupper(c))
{
// Omitted characters
if (c == 'B' || c == 'D' || c == 'I' || c == 'O')
{
return umax;
}
if (c > 'O')
{
c -= 4;
}
else if (c > 'I')
{
c -= 3;
}
else if (c > 'D')
{
c -= 2;
}
else if (c > 'B')
{
c--;
}
return c - 'A' + 10;
}
if (std::islower(c))
{
// Omitted characters
if (c == 'l')
{
return umax;
}
if (c > 'l')
{
c--;
}
return c - 'a' + 10 + 22;
}
return umax;
};
const u64 res = to_val(be_value[j]);
if (res == umax)
{
// Invalid input character
result = {};
break;
}
if (u64{umax} / multiplier < res)
{
// Overflow
result = {};
break;
}
const u64 addend = res * multiplier;
if (~value < addend)
{
// Overflow
result = {};
break;
}
value += addend;
}
}
if (!result.size)
{
break;
}
if (result.size - i < sizeof(value))
{
std::memcpy(result.memory.get() + i, &value, result.size - i);
}
else
{
std::memcpy(result.memory.get() + i, &value, sizeof(value));
}
}
2024-08-01 01:12:58 +02:00
return result;
2024-07-27 08:49:22 +02:00
}
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
2015-01-12 21:37:29 +01:00
{
fmt::append(out, "%p", arg);
2015-01-12 21:37:29 +01:00
}
void fmt_class_string<const char*>::format(std::string& out, u64 arg)
2015-01-12 21:37:29 +01:00
{
2016-08-15 15:57:51 +02:00
if (arg)
{
out += reinterpret_cast<const char*>(arg);
2016-08-15 15:57:51 +02:00
}
else
{
out += "(NULLSTR)";
2016-08-15 15:57:51 +02:00
}
2015-01-12 21:37:29 +01:00
}
void fmt_class_string<const wchar_t*>::format(std::string& out, u64 arg)
{
out += wchar_to_utf8(reinterpret_cast<const wchar_t*>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<std::string>::format(std::string& out, u64 arg)
{
out += get_object(arg);
}
template <>
void fmt_class_string<std::string_view>::format(std::string& out, u64 arg)
{
out += get_object(arg);
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<std::vector<char>>::format(std::string& out, u64 arg)
{
const std::vector<char>& obj = get_object(arg);
out.append(obj.cbegin(), obj.cend());
}
2021-07-10 20:11:52 +02:00
template <>
void fmt_class_string<std::u8string>::format(std::string& out, u64 arg)
{
const std::u8string& obj = get_object(arg);
out.append(obj.cbegin(), obj.cend());
}
template <>
void fmt_class_string<std::u8string_view>::format(std::string& out, u64 arg)
{
const std::u8string_view& obj = get_object(arg);
out.append(obj.cbegin(), obj.cend());
}
template <>
void fmt_class_string<std::vector<char8_t>>::format(std::string& out, u64 arg)
{
const std::vector<char8_t>& obj = get_object(arg);
out.append(obj.cbegin(), obj.cend());
}
2023-03-17 13:43:23 +01:00
template <>
void fmt_class_string<fmt::buf_to_hexstring>::format(std::string& out, u64 arg)
{
const auto& _arg = get_object(arg);
const std::vector<u8> buf(_arg.buf, _arg.buf + _arg.len);
out.reserve(out.size() + (buf.size() * 3));
static constexpr char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
const bool use_linebreak = _arg.line_length > 0;
2023-03-17 13:43:23 +01:00
for (usz index = 0; index < buf.size(); index++)
{
if (index > 0)
{
if (use_linebreak && (index % _arg.line_length) == 0)
out += '\n';
else
out += ' ';
}
if (_arg.with_prefix)
out += "0x";
2023-03-17 13:43:23 +01:00
out += hex[buf[index] >> 4];
out += hex[buf[index] & 15];
}
}
void format_byte_array(std::string& out, const uchar* data, usz size)
{
if (!size)
{
out += "{ EMPTY }";
return;
}
out += "{ ";
for (usz i = 0;; i++)
{
if (i == size - 1)
{
fmt::append(out, "%02X", data[i]);
break;
}
if ((i % 4) == 3)
{
// Place a comma each 4 bytes for ease of byte placement finding
fmt::append(out, "%02X, ", data[i]);
continue;
}
fmt::append(out, "%02X ", data[i]);
}
out += " }";
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<char>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#hhx", static_cast<char>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<uchar>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#hhx", static_cast<uchar>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<schar>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#hhx", static_cast<schar>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<short>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#hx", static_cast<short>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<ushort>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#hx", static_cast<ushort>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<int>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#x", static_cast<int>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<uint>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#x", static_cast<uint>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<long>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#lx", static_cast<long>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<ulong>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#lx", static_cast<ulong>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<llong>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#llx", static_cast<llong>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<ullong>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
fmt::append(out, "%#llx", static_cast<ullong>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<float>::format(std::string& out, u64 arg)
{
fmt::append(out, "%gf", static_cast<float>(std::bit_cast<f64>(arg)));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<double>::format(std::string& out, u64 arg)
{
fmt::append(out, "%g", std::bit_cast<f64>(arg));
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<bool>::format(std::string& out, u64 arg)
{
2016-08-06 12:25:05 +02:00
out += arg ? "true" : "false";
}
template <>
void fmt_class_string<b8>::format(std::string& out, u64 arg)
{
out += get_object(arg) ? "true" : "false";
}
2016-08-13 15:36:04 +02:00
template <>
void fmt_class_string<v128>::format(std::string& out, u64 arg)
{
const v128& vec = get_object(arg);
fmt::append(out, "0x%016llx%016llx", vec._u64[1], vec._u64[0]);
}
template <>
void fmt_class_string<u128>::format(std::string& out, u64 arg)
{
// TODO: it should be supported as full-fledged integral type (with %u, %d, etc, fmt)
const u128& num = get_object(arg);
2021-04-24 14:28:15 +02:00
if (!num)
{
out += '0';
return;
}
clang-cl ffmpeg Update rpcs3.yml Update rpcs3.yml Update rpcs3.yml folded scalar with neg newline x64-windows-release x64-windows-rel if: "!cancelled()" vcpkg_build_type Update rpcs3.yml no ccache ${{github.run_id}} zlib vcpkg qt vulkan ffmpeg llvm build with clang-cl Create build-windows-clang-cl.ps1 llvm --keep-going llvm[clang,core,tools,lld,target-x86]:x64-windows-release llvm:x64-windows-release@17.0.2 llvm with debug on Create vcpkg.json Update rpcs3.yml ffmpeg features vcpkg nuget minimal vcpkg.json Update rpcs3.yml fetch nuget more packages vcpkg.json libpng vcpkg classic cmake 3.29.0 Rename vcpkg.json to x_vcpkg.json Update rpcs3.yml Update rpcs3.yml llvm Update rpcs3.yml git llvm LLVM with cache Update rpcs3.yml Update rpcs3.yml build llvm LLVM_TARGETS_TO_BUILD="X86" llvm binary set path build rpcs3 DIA SDK Update rpcs3.yml Update rpcs3.yml Update rpcs3.yml fix conditionals fix conditionals set shell VCPKG env vars DIA SDK Update asm.hpp Update types.hpp Update aesni.cpp Update CMakeLists.txt Update ConfigureCompiler.cmake Update StrFmt.cpp Update CMakeLists.txt Update CMakeLists.txt Build with changes Update CMakeLists.txt D:\a\rpcs3\rpcs3\llvm D:\a\rpcs3\rpcs3\llvm llvm-* llvm-${{ matrix.llvmver }} clangpath llvm-* $llvmver $clangPath $clangPath include bin rm duplicate "add_compile_options" USE_SYSTEM_ZSTD USE_SYSTEM_ZSTD USE_SYSTEM_ZSTD USE_SYSTEM_ZSTD zstd Update CMakeLists.txt PkgConfig zstd zstd::zstd ALIAS PkgConfig::libzstd clang-cl only cache hit Update CMakeLists.txt cache-hit cache vcpkg/vcpkg.exe NOT USE_SYSTEM_ZSTD vcpkg_root revert zstd Update CMakeLists.txt Update CMakeLists.txt Update CMakeLists.txt /defaultlib:zstd_static.lib Remove else /defaultlib:zstd.lib Zstd ahared Nodefaultlib Create Findzstd.cmake zstd CMakeLists.txt not use zstd system CMakeLists.txt dont add 3rdparty::libzstd add_library(PkgConfig::libzstd ALIAS 3rdparty::zstd) add_library(PkgConfig::libzstd ALIAS 3rdparty::zstd) add_library(3rdparty::libzstd ALIAS PkgConfig::zstd) add_library(3rdparty::zstd ALIAS PkgConfig::zstd) Update Findzstd.cmake zstd::zstd Update CMakeLists.txt zstd::zstd zstd::zstd CMakeLists.txt PkgConfig::libzstd CMakeLists.txt zstd::libzstd Update CMakeLists.txt Update CMakeLists.txt vcpkg zstd CMakeLists.txt MODULES CMakeLists.txt zstd::libzstd add_library(3rdparty::7zip ALIAS 3rdparty_7zip) LLVM Static-link on set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Release>:Release>") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded") Update CMakeLists.txt message(STATUS "MSVC Runtime Library: ${CMAKE_MSVC_RUNTIME_LIBRARY}") revert CMakeLists.txt DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded" rpcs3_emu SHARED STATIC CMakeLists.txt cmake_policy(SET CMP0091 NEW) LLVM_AVAILABLE=ON add_compile_definitions(LLVM_AVAILABLE=true) add_compile_options(/MT) LLVM_AVAILABLE=1 add_compile_definitions(_DISABLE_STRING_ANNOTATION=1 _DISABLE_VECTOR_ANNOTATION=1) Update build-windows-clang-cl.ps1 clang msvc17-msvcrt rm compressed archve cachee name cache name again builtin clang-rt build all set $llvmPath extract into llvm copy with -verbose mv destination path build llvm cache $clangPath full build LLVM static LLVM -> LLVMCore STATIC_LINK_LLVM=OFF no lookup llvm bin dir revert revert revert LLVMCore -> LLVM LLVM -> LLVM-C llvm_map_components_to_libnames Update CMakeLists.txt LLVM CMakeLists.txt LLVM_DIR=$llvmPath MultiThreadedDLL CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON clang-cl version cmake -v --log-level=VERBOSE built-in LLVM llvm lib folder PF short name built-in LLVM/Clang 20.1.8 mt.exe path Use llvm-mt "$clangPath/llvm-mt.exe" fix terminator Add winqtdeploy to PATH Test windeployqt6.exe with version Missing ) No winqtdeploy Build no quotes prep artifacts no winqtdeploy $VcpkgWindeployqt --version $($VcpkgWindeployqt) --version --version Invoke-Expression Update build-windows-clang-cl.ps1 rpcs3_win_clang.7z rpcs3 artifacts dir cp artifacts build/bin dir clone recent asmjit Update build-windows-clang-cl.ps1 CMAKE_CXX_SCAN_FOR_MODULES=ON default-openal USE_NATIVE_INSTRUCTIONS=ON Update ConfigureCompiler.cmake USE_NATIVE_INSTRUCTIONS=OFF / rm 512 instuctions revert set(LLVM_LIBS LLVM) COMPILER_X86 only add_compile_options(-msse -msse2 -mcx16 -mavx512f -mavx -mavx2 -maes -mrtm -mpclmul -mmwaitx -mwaitpkg) avx512 flags add_compile_options(-march=native) check_cxx_compiler_flag("-march=native" add_compile_options(-maes -mrtm -mpclmul -mmwaitx -mwaitpkg) COMMAND Qt6::windeployqt --version COMMAND Qt6::windeployqt $<TARGET_FILE:rpcs3> add vcpkg bin to PATH check vcpkg is added to PATH update PATH cache [System.EnvironmentVariableTarget]::Machine display all paths cmd.exe /c "echo %PATH%" make windeployqt verbose verbose 2 Invoke-Expression "cmd.exe /c "set PATH=%PATH%;$VcpkgBin"" Update build-windows-clang-cl.ps1 no invoke cmd.exe /c "set PATH=$VcpkgBin;%PATH%" --ignore-library-errors -DQTPATH_EXE="$VcpkgQtpath" no --ignore-library-errors Update CMakeLists.txt Update CMakeLists.txt Update CMakeLists.txt Update CMakeLists.txt Update CMakeLists.txt Update CMakeLists.txt Update CMakeLists.txt Update CMakeLists.txt change \ to / ${WINDEPLOYQT_EXECUTABLE} Qt6::windeployqt gci vcpkg tools bin --ignore-library-errors x64-windows/tools VCPKG_TRIPLET: x64-windows Save vcpkg cache revert revert remove MSVC runtime message revert rm dupes revert Delete buildfiles/cmake/Findzstd.cmake WINDEPLOYQT_EXECUTABLE Delete x_vcpkg.json add AVX512 compile options Wno-deprecated-anon-enum-enum-conversion clean-up silence warnings terminate properly set PRIVATE CFLAGS --no-vulkan rm vulkan lib at package step override cflags remove OpenGL_GL_PREFERENCE=LEGACY rm --no-vulkan switch Update CMakeLists.txt restore LLVM dir ASMJIT_CMAKE_FLAGS revert order check_cxx_compiler_flag Wno-unused-value revert revert Update CMakeLists.txt Update llvm_build_clang_cl.vcxproj Update llvm_build_clang_cl.vcxproj Update rpcs3.yml go to deploy if successful Create deploy-windows-clang-cl.sh build then deploy Update build-windows-clang-cl.ps1 deploy step test Update ProgramStateCache.cpp remove AVX512 Update JITLLVM.cpp FFMPEG 8.0 FFMPEG 8.0 LLVM 21 LLVM 21 set CMAKE_MSVC_RUNTIME_LIBRARY update OpenAL msys2 clang git openal-soft reset update yaml reset to master reset to master "Build succeeded" ALSOFT_ENABLE_MODULES OFF Build All Jobs Run Builds when not on Main branch Win Qt 6.9.3 Update build-mac-arm64.sh Update build-mac.sh Create TCDarwinX86_64.cmake
2025-07-20 06:02:56 +02:00
#if defined(_MSC_VER) && !defined(__clang__)
fmt::append(out, "0x%016llx%016llx", num.hi, num.lo);
#else
fmt::append(out, "0x%016llx%016llx", static_cast<u64>(num >> 64), static_cast<u64>(num));
#endif
}
2021-04-24 14:28:15 +02:00
template <>
void fmt_class_string<s128>::format(std::string& out, u64 arg)
{
return fmt_class_string<u128>::format(out, arg);
}
template <>
void fmt_class_string<std::source_location>::format(std::string& out, u64 arg)
{
const std::source_location& loc = get_object(arg);
auto is_valid = [](auto num)
{
return num && num != umax;
};
const bool has_line = is_valid(loc.line());
if (has_line && is_valid(loc.column()))
{
fmt::append(out, "\n(in file %s:%u[:%u]", loc.file_name(), loc.line(), loc.column());
}
else if (has_line)
{
fmt::append(out, "\n(in file %s:%u", loc.file_name(), loc.line());
}
// Let's not care about such useless corner cases
// else if (is_valid(loc.column())
else
{
fmt::append(out, "\n(in file %s", loc.file_name());
}
if (std::string_view full_func{loc.function_name() ? loc.function_name() : ""}; !full_func.empty())
{
// Remove useless disambiguators
std::string func = fmt::replace_all(std::string(full_func), {
{"struct ", ""},
{"class ", ""},
{"enum ", ""},
{"typename ", ""},
#ifdef _MSC_VER
{"__cdecl ", ""},
#endif
{"unsigned long long", "ullong"},
//{"unsigned long", "ulong"}, // ullong
{"unsigned int", "uint"},
{"unsigned short", "ushort"},
{"unsigned char", "uchar"}});
// Remove function argument signature for long names
for (usz index = func.find_first_of('('); index != umax && func.size() >= 100u; index = func.find_first_of('(', index))
{
// Operator() function
if (func.compare(0, 3, "()("sv) == 0 || func.compare(0, 3, "() "sv))
{
if (usz not_space = func.find_first_not_of(' ', index + 2); not_space != umax && func[not_space] == '(')
{
index += 2;
continue;
}
}
2024-12-25 06:02:42 +01:00
func = func.substr(0, index) + "()";
break;
}
fmt::append(out, ", in function '%s')", func);
2016-08-13 15:36:04 +02:00
}
else
{
out += ')';
}
// Print error code (may be irrelevant)
#ifdef _WIN32
if (DWORD error = GetLastError())
{
2022-03-16 19:59:20 +01:00
fmt::append(out, " (error=%s)", error, fmt::win_error_to_string(error));
}
#else
if (int error = errno)
{
2022-03-16 19:59:20 +01:00
fmt::append(out, " (errno=%d=%s)", error, strerror(errno));
}
#endif
}
2016-08-13 15:36:04 +02:00
namespace fmt
{
[[noreturn]] void raw_verify_error(std::source_location loc, const char8_t* msg, usz object)
2016-08-13 15:36:04 +02:00
{
std::string out;
fmt::append(out, "%s (object: 0x%x)%s", msg ? msg : u8"Verification failed", object, loc);
thread_ctrl::emergency_exit(out);
}
[[noreturn]] void raw_range_error(std::source_location loc, std::string_view index, usz container_size)
{
std::string out;
if (container_size != umax)
{
fmt::append(out, "Range check failed (index: %s, container_size: %u)%s", index, container_size, loc);
}
else
{
fmt::append(out, "Range check failed (index: %s)%s", index, loc);
}
thread_ctrl::emergency_exit(out);
}
[[noreturn]] void raw_range_error(std::source_location loc, usz index, usz container_size)
{
std::string out;
if (container_size != umax)
{
fmt::append(out, "Range check failed (index: %u, container_size: %u)%s", index, container_size, loc);
}
else
{
fmt::append(out, "Range check failed (index: %u)%s", index, loc);
}
thread_ctrl::emergency_exit(out);
}
[[noreturn]] void raw_throw_exception(std::source_location loc, const char* fmt, const fmt_type_info* sup, const u64* args)
{
std::string out;
raw_append(out, fmt, sup, args);
fmt::append(out, "%s", loc);
thread_ctrl::emergency_exit(out);
}
struct cfmt_src;
}
// Temporary implementation
struct fmt::cfmt_src
{
2016-08-05 18:49:45 +02:00
const fmt_type_info* sup;
const u64* args;
2020-12-18 08:39:54 +01:00
bool test(usz index) const
{
2016-08-05 18:49:45 +02:00
if (!sup[index].fmt_string)
{
2016-08-05 18:49:45 +02:00
return false;
}
return true;
}
2016-08-13 15:36:04 +02:00
template <typename T>
2020-12-18 08:39:54 +01:00
T get(usz index) const
{
T res{};
std::memcpy(&res, reinterpret_cast<const u8*>(args + index), sizeof(res));
return res;
}
2020-12-18 08:39:54 +01:00
void skip(usz extra)
{
sup += extra + 1;
args += extra + 1;
}
2020-12-18 08:39:54 +01:00
usz fmt_string(std::string& out, usz extra) const
{
2020-12-18 08:39:54 +01:00
const usz start = out.size();
2016-08-05 18:49:45 +02:00
sup[extra].fmt_string(out, args[extra]);
return out.size() - start;
}
2016-08-05 18:49:45 +02:00
2016-08-06 12:25:05 +02:00
// Returns type size (0 if unknown, pointer, unsigned, assumed max)
2020-12-18 08:39:54 +01:00
usz type(usz extra) const
2016-08-05 18:49:45 +02:00
{
2016-08-13 15:36:04 +02:00
// Hack: use known function pointers to determine type
#define TYPE(type) \
if (sup[extra].fmt_string == &fmt_class_string<type>::format) return sizeof(type);
2016-08-05 18:49:45 +02:00
2016-08-06 12:25:05 +02:00
TYPE(int);
TYPE(llong);
2016-08-05 18:49:45 +02:00
TYPE(schar);
TYPE(short);
2024-11-16 13:40:53 +01:00
if constexpr (std::is_signed_v<char>) TYPE(char);
2016-08-05 18:49:45 +02:00
TYPE(long);
2021-04-24 14:28:15 +02:00
TYPE(s128);
2016-08-05 18:49:45 +02:00
#undef TYPE
if (sup[extra].fmt_string == &fmt_class_string<u128>::format)
return -1;
2016-08-05 18:49:45 +02:00
return 0;
}
2017-05-16 13:29:07 +02:00
2020-12-18 08:39:54 +01:00
static constexpr usz size_char = 1;
static constexpr usz size_short = 2;
static constexpr usz size_int = 0;
static constexpr usz size_long = sizeof(ulong);
static constexpr usz size_llong = sizeof(ullong);
static constexpr usz size_size = sizeof(usz);
static constexpr usz size_max = sizeof(std::uintmax_t);
static constexpr usz size_diff = sizeof(std::ptrdiff_t);
};
2016-08-05 18:49:45 +02:00
void fmt::raw_append(std::string& out, const char* fmt, const fmt_type_info* sup, const u64* args) noexcept
{
cfmt_append(out, fmt, cfmt_src{sup, args});
}
std::string fmt::replace_all(std::string_view src, std::string_view from, std::string_view to, usz count)
{
if (src.empty())
return {};
if (from.empty() || count == 0)
return std::string(src);
std::string target;
target.reserve(src.size() + to.size());
for (usz i = 0, replaced = 0; i < src.size();)
{
const usz pos = src.find(from, i);
if (pos == umax || replaced++ >= count)
{
// No match or too many encountered, append the rest of the string as is
target.append(src.substr(i));
break;
}
// Append source until the matched string position
target.append(src.substr(i, pos - i));
// Replace string
target.append(to);
i = pos + from.size();
}
return target;
}
2021-01-05 16:34:35 +01:00
std::vector<std::string> fmt::split(std::string_view source, std::initializer_list<std::string_view> separators, bool is_skip_empty)
2014-11-29 14:16:53 +01:00
{
std::vector<std::string> result;
2021-01-05 16:34:35 +01:00
for (usz index = 0; index < source.size();)
2014-11-29 14:16:53 +01:00
{
2021-01-05 16:34:35 +01:00
usz pos = -1;
usz sep_size = 0;
2016-08-13 15:36:04 +02:00
for (auto& separator : separators)
2014-11-29 14:16:53 +01:00
{
if (usz pos0 = source.find(separator, index); pos0 < pos)
2014-11-29 14:16:53 +01:00
{
2021-01-05 16:34:35 +01:00
pos = pos0;
sep_size = separator.size();
2014-11-29 14:16:53 +01:00
}
}
2021-01-05 16:34:35 +01:00
if (!sep_size)
{
result.emplace_back(&source[index], source.size() - index);
return result;
}
std::string_view piece = {&source[index], pos - index};
index = pos + sep_size;
if (piece.empty() && is_skip_empty)
{
continue;
}
result.emplace_back(std::string(piece));
2014-11-29 14:16:53 +01:00
}
if (result.empty() && !is_skip_empty)
{
result.emplace_back();
}
return result;
}
std::string fmt::trim(const std::string& source, std::string_view values)
{
2024-01-01 23:59:27 +01:00
const usz begin = source.find_first_not_of(values);
if (begin == source.npos)
2016-08-13 15:36:04 +02:00
return {};
const usz end = source.find_last_not_of(values);
if (end == source.npos)
return source.substr(begin);
return source.substr(begin, end + 1 - begin);
}
2024-01-01 23:59:27 +01:00
std::string fmt::trim_front(const std::string& source, std::string_view values)
{
const usz begin = source.find_first_not_of(values);
if (begin == source.npos)
return {};
return source.substr(begin);
}
void fmt::trim_back(std::string& source, std::string_view values)
{
const usz index = source.find_last_not_of(values);
source.resize(index + 1);
}
std::string fmt::to_upper(std::string_view string)
2016-02-01 22:55:43 +01:00
{
std::string result;
result.resize(string.size());
std::transform(string.begin(), string.end(), result.begin(), ::toupper);
2016-02-01 22:55:43 +01:00
return result;
}
std::string fmt::to_lower(std::string_view string)
{
std::string result;
result.resize(string.size());
std::transform(string.begin(), string.end(), result.begin(), ::tolower);
return result;
2024-01-05 09:43:26 +01:00
}
std::string fmt::truncate(std::string_view src, usz length)
{
return std::string(src.begin(), src.begin() + std::min(src.size(), length));
}
std::string get_file_extension(const std::string& file_path)
{
if (usz dotpos = file_path.find_last_of('.'); dotpos != std::string::npos && dotpos + 1 < file_path.size())
{
return file_path.substr(dotpos + 1);
}
return {};
}