diff --git a/Dumper/Dumper.vcxproj b/Dumper/Dumper.vcxproj
index 11acf15..0a44490 100644
--- a/Dumper/Dumper.vcxproj
+++ b/Dumper/Dumper.vcxproj
@@ -21,13 +21,13 @@
Application
true
- v142
+ v143
MultiByte
Application
false
- v142
+ v143
true
MultiByte
@@ -58,6 +58,9 @@
false
$(SolutionDir)libs;$(LibraryPath)
+
+ true
+
Level3
@@ -102,7 +105,7 @@
Console
true
true
- true
+ false
ntdll.lib;%(AdditionalDependencies)
diff --git a/Dumper/Dumper.vcxproj.filters b/Dumper/Dumper.vcxproj.filters
index 8d73f28..8ca3ea3 100644
--- a/Dumper/Dumper.vcxproj.filters
+++ b/Dumper/Dumper.vcxproj.filters
@@ -1,68 +1,68 @@
-
+
+ {7feed74c-41d0-4a17-93ca-9e368259955d}
+
+
+ {fa04ece9-af5e-44b6-9f99-319055d65faa}
+
+
{93995380-89BD-4b04-88EB-625FBE52EBFB}
h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
-
+
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}
cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
-
- {7feed74c-41d0-4a17-93ca-9e368259955d}
-
-
- {fa04ece9-af5e-44b6-9f99-319055d65faa}
-
- Sources
+ sources
- Sources
+ sources
- Sources
+ sources
- Sources
+ sources
- Sources\include\fmt
+ include\fmt
- Sources
+ sources
- Sources
+ sources
- Sources
+ sources
- Headers
+ headers
- Headers
+ headers
- Headers
+ headers
- Headers
+ headers
- Headers
+ headers
- Headers
+ headers
- Headers
+ headers
\ No newline at end of file
diff --git a/Dumper/dumper.cpp b/Dumper/dumper.cpp
index 04c46ca..481d744 100644
--- a/Dumper/dumper.cpp
+++ b/Dumper/dumper.cpp
@@ -21,7 +21,7 @@ STATUS Dumper::Init(int argc, char *argv[]) {
Full = false;
} else if (arg16 == 'w-') {
Wait = true;
- } else if ((arg16 == 'f-')) {
+ } else if (arg16 == 'f-') {
i++;
if (i < argc) { PackageName = argv[i]; }
else { return STATUS::FAILED; }
@@ -68,13 +68,14 @@ STATUS Dumper::Init(int argc, char *argv[]) {
Directory = root / "Games" / game;
fs::create_directories(Directory);
- auto [base, size] = GetModuleInfo(pid, processName);
- if (!(base && size)) { return STATUS::MODULE_NOT_FOUND; }
- Base = (uint64)base;
+ uint64 size = GetImageSize();
+ if (!size) { return STATUS::MODULE_NOT_FOUND; }
+
Image = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
- if (!Read(base, Image, size)) {
+ if (!Read((void*)Base, Image, size)) {
return STATUS::CANNOT_READ;
}
+
return EngineInit(game.string(), Image);
}
}
@@ -108,9 +109,16 @@ STATUS Dumper::Dump() {
std::function callback;
if (Full) {
callback = [&file, &size, &packages](UE_UObject object) {
- fmt::print(file, "[{:0>6}] <{}> <{}> {}\n", object.GetIndex(), object.GetAddress(), Read(object.GetAddress()), object.GetFullName());
+
+ auto isFunction = object.IsA();
+ if (isFunction) {
+ fmt::print(file, "[{:0>6}] <{}> <{}> {} {:x}\n", object.GetIndex(), object.GetAddress(), Read(object.GetAddress()), object.GetFullName(), object.Cast().GetFunc() - Base);
+ }
+ else {
+ fmt::print(file, "[{:0>6}] <{}> <{}> {}\n", object.GetIndex(), object.GetAddress(), Read(object.GetAddress()), object.GetFullName());
+ }
size++;
- if (object.IsA() || object.IsA()) {
+ if (isFunction || object.IsA() || object.IsA()) {
auto packageObj = object.GetPackageObject();
packages[packageObj].push_back(object);
}
diff --git a/Dumper/engine.cpp b/Dumper/engine.cpp
index 0688859..f5380f2 100644
--- a/Dumper/engine.cpp
+++ b/Dumper/engine.cpp
@@ -242,11 +242,12 @@ struct {
} Brickadia;
static_assert(sizeof(Brickadia) == sizeof(Offsets));
+
struct {
void* offsets; // address to filled offsets structure
std::pair names; // NamePoolData signature
std::pair objects; // ObjObjects signature
- std::function*)> callback;
+ std::function callback;
} engines[] = {
{ // RogueCompany | PropWitchHuntModule-Win64-Shipping | Scum
&Default,
@@ -282,9 +283,9 @@ struct {
&Default,
{"\x4C\x8D\x35\x00\x00\x00\x00\x0F\x10\x07\x83\xFB\x01", 13},
{"\x48\x8B\x05\x00\x00\x00\x00\x48\x8B\x0C\xC8\x48\x8D\x04\xD1\xEB", 16},
- [](std::pair* s) {
+ [](void* start, void* end) {
if (!Decrypt_ANSI) {
- auto decryptAnsi = FindPointer(s->first, s->second, "\xE8\x00\x00\x00\x00\x0F\xB7\x1B\xC1\xEB\x06\x4C\x89\x36\x4C\x89\x76\x08\x85\xDB\x74\x48", 22);
+ auto decryptAnsi = FindPointer(start, end, "\xE8\x00\x00\x00\x00\x0F\xB7\x1B\xC1\xEB\x06\x4C\x89\x36\x4C\x89\x76\x08\x85\xDB\x74\x48", 22);
if (decryptAnsi) {
/*
mov [rsp +8], rbx
@@ -299,7 +300,7 @@ struct {
*(uint64*)(trampoline + 17) = (uint64)((uint8*)decryptAnsi + 0x4A); // https://i.imgur.com/zWtMDar.png
Decrypt_ANSI = (ansi_fn)VirtualAlloc(0, sizeof(trampoline), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (Decrypt_ANSI) {
- memcpy(Decrypt_ANSI, trampoline, sizeof(trampoline));
+ memcpy((void*)Decrypt_ANSI, trampoline, sizeof(trampoline));
return true;
}
}
@@ -332,11 +333,12 @@ std::unordered_map games = {
{"POLYGON-Win64-Shipping", &engines[4]},
{"FortniteClient-Win64-Shipping", &engines[5]},
{"TheIsleClient-Win64-Shipping", &engines[6]},
- {"PortalWars-Win64-Shipping", &engines[7]}
+ {"PortalWars-Win64-Shipping", &engines[7]},
+ {"Tiger-Win64-Shipping", &engines[0]}
};
STATUS EngineInit(std::string game, void* image) {
- auto sections = GetExSections(image);
+
auto it = games.find(game);
if (it == games.end()) { return STATUS::ENGINE_NOT_FOUND; }
@@ -345,21 +347,22 @@ STATUS EngineInit(std::string game, void* image) {
void* names = nullptr;
void* objects = nullptr;
- bool callback = false;
uint8 found = 0;
if (!engine->callback) {
- callback = true;
found |= 4;
}
- for (auto i = 0; i < sections.size(); i++) {
- auto &s = sections.at(i);
- if (!names) if (names = FindPointer(s.first, s.second, engine->names.first, engine->names.second)) found |= 1;
- if (!objects) if (objects = FindPointer(s.first, s.second, engine->objects.first, engine->objects.second)) found |= 2;
- if (!callback) if (callback = engine->callback(&s)) found |= 4;
- if (found == 7) break;
- }
+ IterateExSections(
+ image,
+ [&](void* start, void* end)->bool {
+ if (!(found & 1)) if (names = FindPointer(start, end, engine->names.first, engine->names.second)) found |= 1;
+ if (!(found & 2)) if (objects = FindPointer(start, end, engine->objects.first, engine->objects.second)) found |= 2;
+ if (!(found & 4)) if (engine->callback(start, end)) found |= 4;
+ if (found == 7) return 1;
+ return 0;
+ }
+ );
if (found != 7) return STATUS::ENGINE_FAILED;
diff --git a/Dumper/generic.cpp b/Dumper/generic.cpp
index ee2ef5f..406f409 100644
--- a/Dumper/generic.cpp
+++ b/Dumper/generic.cpp
@@ -15,7 +15,7 @@ void FNamePool::DumpBlock(uint32 blockId, uint32 blockSize, std::function
+#include
#include "memory.h"
HANDLE hProcess;
uint64 Base;
-bool Read(void *address, void *buffer, uint64 size) {
- return ReadProcessMemory(hProcess, address, buffer, size, nullptr);
+bool Read(void* address, void* buffer, uint64 size) {
+ uint64 read;
+ return ReadProcessMemory(hProcess, address, buffer, size, &read) && read == size;
}
bool ReaderInit(uint32 pid) {
- hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
- return hProcess != nullptr;
-}
\ No newline at end of file
+ PROCESS_BASIC_INFORMATION pbi;
+ hProcess = OpenProcess(GENERIC_READ, 0, pid);
+ if (!hProcess) return false;
+ if (0 > NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), 0)) goto failed;
+ Base = Read((uint8*)pbi.PebBaseAddress + 0x10);
+ if (!Base) goto failed;
+ return true;
+failed:
+ CloseHandle(hProcess);
+ return false;
+}
+
+uint64 GetImageSize() {
+ char buffer[0x400];
+ if (!Read((void*)Base, buffer, 0x400)) return 0;
+ auto nt = (PIMAGE_NT_HEADERS)(buffer + ((PIMAGE_DOS_HEADER)buffer)->e_lfanew);
+ return nt->OptionalHeader.SizeOfImage;
+}
diff --git a/Dumper/memory.h b/Dumper/memory.h
index 199478f..259517f 100644
--- a/Dumper/memory.h
+++ b/Dumper/memory.h
@@ -11,3 +11,5 @@ template T Read(void *address) {
}
bool ReaderInit(uint32 pid);
+
+uint64 GetImageSize();
diff --git a/Dumper/utils.cpp b/Dumper/utils.cpp
index 23385d4..14688f9 100644
--- a/Dumper/utils.cpp
+++ b/Dumper/utils.cpp
@@ -1,39 +1,8 @@
#include
-#include
-#include
+#include
+#include "memory.h"
#include "utils.h"
-uint32 GetProcessId(std::wstring name) {
- uint32 pid = 0;
- HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if (snapshot != INVALID_HANDLE_VALUE) {
- PROCESSENTRY32W entry = {sizeof(entry)};
- while (Process32NextW(snapshot, &entry)) {
- if (name == entry.szExeFile) {
- pid = entry.th32ProcessID;
- break;
- }
- }
- CloseHandle(snapshot);
- }
- return pid;
-}
-
-std::pair GetModuleInfo(uint32 pid, std::wstring name) {
- std::pair info;
- HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
- if (snapshot != INVALID_HANDLE_VALUE) {
- MODULEENTRY32W modEntry = {sizeof(modEntry)};
- while (Module32NextW(snapshot, &modEntry)) {
- if (name == modEntry.szModule) {
- info = {modEntry.modBaseAddr, modEntry.modBaseSize};
- break;
- }
- }
- }
- return info;
-}
-
bool Compare(uint8* data, uint8 *sig, uint32 size) {
for (uint32 i = 0; i < size; i++) {
if (data[i] != sig[i] && sig[i] != 0x00) {
@@ -43,16 +12,16 @@ bool Compare(uint8* data, uint8 *sig, uint32 size) {
return true;
}
-uint8* FindSignature(uint8* start, uint8* end, const char* sig, uint32 size) {
- for (uint8* it = start; it < end - size; it++) {
+uint8* FindSignature(void* start, void* end, const char* sig, uint32 size) {
+ for (uint8* it = (uint8*)start; it < (uint8*)end - size; it++) {
if (Compare(it, (uint8*)sig, size)) {
return it;
};
}
- return nullptr;
+ return 0;
}
-void* FindPointer(uint8* start, uint8* end, const char* sig, uint32 size, int32 addition) {
+void* FindPointer(void* start, void* end, const char* sig, uint32 size, int32 addition) {
uint8* address = FindSignature(start, end, sig, size);
if (!address) return nullptr;
int32 k = 0;
@@ -61,34 +30,28 @@ void* FindPointer(uint8* start, uint8* end, const char* sig, uint32 size, int32
return address + k + 4 + offset + addition;
}
-std::vector> GetExSections(void* data) {
- std::vector> sections;
+void IterateExSections(void* data, std::function callback) {
auto dos = (PIMAGE_DOS_HEADER)data;
auto nt = (PIMAGE_NT_HEADERS)((uint8*)data + dos->e_lfanew);
auto s = IMAGE_FIRST_SECTION(nt);
for (auto i = 0; i < nt->FileHeader.NumberOfSections; i++, s++) {
if (s->Characteristics & IMAGE_SCN_CNT_CODE) {
- auto start = (uint8*)data + s->PointerToRawData;
+ auto start = (uint8*)data + s->VirtualAddress;
auto end = start + s->SizeOfRawData;
- sections.push_back({start, end});
+ if (callback(start, end)) break;
}
}
- return sections;
}
uint32 GetProccessPath(uint32 pid, wchar_t* processName, uint32 size) {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
- if (!QueryFullProcessImageNameW(hProcess, 0, processName, (DWORD*)(&size))) {
- size = 0;
- };
+ if (!QueryFullProcessImageNameW(hProcess, 0, processName, (DWORD*)(&size))) size = 0;
CloseHandle(hProcess);
return size;
}
-extern "C" NTSTATUS NtQuerySystemTime(uint64* SystemTime);
-
uint64 GetTime() {
- uint64 ret;
+ LARGE_INTEGER ret;
NtQuerySystemTime(&ret);
- return ret;
+ return ret.QuadPart;
}
diff --git a/Dumper/utils.h b/Dumper/utils.h
index e0adaa2..25b5efe 100644
--- a/Dumper/utils.h
+++ b/Dumper/utils.h
@@ -1,13 +1,15 @@
#pragma once
+#include
#include "defs.h"
-#include
-#include
-uint32 GetProcessId(std::wstring name);
-std::pair GetModuleInfo(uint32 pid, std::wstring name);
bool Compare(uint8* data, uint8* sig, uint32 size);
-uint8* FindSignature(uint8* start, uint8* end, const char* sig, uint32 size);
-void* FindPointer(uint8* start, uint8* end, const char* sig, uint32 size, int32 addition = 0);
-std::vector> GetExSections(void *data);
+
+uint8* FindSignature(void* start, void* end, const char* sig, uint32 size);
+
+void* FindPointer(void* start, void* end, const char* sig, uint32 size, int32 addition = 0);
+
+void IterateExSections(void* data, std::function callback);
+
uint32 GetProccessPath(uint32 pid, wchar_t* processName, uint32 size);
+
uint64 GetTime();
diff --git a/include/fmt/core.h b/include/fmt/core.h
index 27c1ff5..bb56da7 100644
--- a/include/fmt/core.h
+++ b/include/fmt/core.h
@@ -16,29 +16,33 @@
#include
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 80000
+#define FMT_VERSION 80001
-#ifdef __clang__
+#if defined (__clang__ ) && !defined(__ibmxl__)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
# define FMT_CLANG_VERSION 0
#endif
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
+ !defined(__NVCOMPILER)
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-# define FMT_GCC_PRAGMA(arg) _Pragma(arg)
#else
# define FMT_GCC_VERSION 0
-# define FMT_GCC_PRAGMA(arg)
#endif
-#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
-# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION
-#else
-# define FMT_HAS_GXX_CXX11 0
+#ifndef FMT_GCC_PRAGMA
+// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
+# if FMT_GCC_VERSION >= 504
+# define FMT_GCC_PRAGMA(arg) _Pragma(arg)
+# else
+# define FMT_GCC_PRAGMA(arg)
+# endif
#endif
-#if defined(__INTEL_COMPILER)
+#ifdef __ICL
+# define FMT_ICC_VERSION __ICL
+#elif defined(__INTEL_COMPILER)
# define FMT_ICC_VERSION __INTEL_COMPILER
#else
# define FMT_ICC_VERSION 0
@@ -88,7 +92,7 @@
// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
#ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \
- (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
+ (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
!FMT_NVCC && !FMT_ICC_VERSION
#endif
@@ -100,6 +104,14 @@
# define FMT_CONSTEXPR_DECL
#endif
+#if ((__cplusplus >= 202002L) && \
+ (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
+ (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002)
+# define FMT_CONSTEXPR20 constexpr
+#else
+# define FMT_CONSTEXPR20
+#endif
+
// Check if constexpr std::char_traits<>::compare,length is supported.
#if defined(__GLIBCXX__)
# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \
@@ -116,15 +128,6 @@
# define FMT_CONSTEXPR_CHAR_TRAITS
#endif
-#ifndef FMT_OVERRIDE
-# if FMT_HAS_FEATURE(cxx_override_control) || \
- (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
-# define FMT_OVERRIDE override
-# else
-# define FMT_OVERRIDE
-# endif
-#endif
-
// Check if exceptions are disabled.
#ifndef FMT_EXCEPTIONS
# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
@@ -141,7 +144,7 @@
#endif
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
- (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
+ FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900
# define FMT_DETECTED_NOEXCEPT noexcept
# define FMT_HAS_CXX11_NOEXCEPT 1
#else
@@ -166,14 +169,6 @@
# define FMT_NORETURN
#endif
-#ifndef FMT_MAYBE_UNUSED
-# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
-# define FMT_MAYBE_UNUSED [[maybe_unused]]
-# else
-# define FMT_MAYBE_UNUSED
-# endif
-#endif
-
#if __cplusplus == 201103L || __cplusplus == 201402L
# if defined(__INTEL_COMPILER) || defined(__PGI)
# define FMT_FALLTHROUGH
@@ -210,31 +205,13 @@
# endif
#endif
-#ifndef FMT_USE_INLINE_NAMESPACES
-# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
- (FMT_MSC_VER >= 1900 && (!defined(_MANAGED) || !_MANAGED))
-# define FMT_USE_INLINE_NAMESPACES 1
-# else
-# define FMT_USE_INLINE_NAMESPACES 0
-# endif
-#endif
-
#ifndef FMT_BEGIN_NAMESPACE
-# if FMT_USE_INLINE_NAMESPACES
-# define FMT_INLINE_NAMESPACE inline namespace
-# define FMT_END_NAMESPACE \
- } \
- }
-# else
-# define FMT_INLINE_NAMESPACE namespace
-# define FMT_END_NAMESPACE \
- } \
- using namespace v8; \
- }
-# endif
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
- FMT_INLINE_NAMESPACE v8 {
+ inline namespace v8 {
+# define FMT_END_NAMESPACE \
+ } \
+ }
#endif
#ifndef FMT_MODULE_EXPORT
@@ -264,12 +241,6 @@
# define FMT_API
#endif
-#if FMT_GCC_VERSION
-# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
-#else
-# define FMT_GCC_VISIBILITY_HIDDEN
-#endif
-
// libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE() && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
@@ -286,10 +257,10 @@
#endif
#ifndef FMT_CONSTEVAL
-# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
- __cplusplus > 201703L) || \
- (defined(__cpp_consteval) && \
- !FMT_MSC_VER) // consteval is broken in MSVC.
+# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
+ __cplusplus > 201703L && !defined(__apple_build_version__)) || \
+ (defined(__cpp_consteval) && (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704))
+ // consteval is broken in MSVC before VS2022 and Apple clang 13.
# define FMT_CONSTEVAL consteval
# define FMT_HAS_CONSTEVAL
# else
@@ -317,14 +288,16 @@ FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
// Implementations of enable_if_t and other metafunctions for older systems.
-template
+template
using enable_if_t = typename std::enable_if::type;
-template
+template
using conditional_t = typename std::conditional::type;
template using bool_constant = std::integral_constant;
template
using remove_reference_t = typename std::remove_reference::type;
template
+using remove_const_t = typename std::remove_const::type;
+template
using remove_cvref_t = typename std::remove_cv>::type;
template struct type_identity { using type = T; };
template using type_identity_t = typename type_identity::type;
@@ -344,16 +317,25 @@ struct monostate {
FMT_BEGIN_DETAIL_NAMESPACE
-constexpr FMT_INLINE auto is_constant_evaluated() FMT_NOEXCEPT -> bool {
+// Suppress "unused variable" warnings with the method described in
+// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
+// (void)var does not work on many Intel compilers.
+template FMT_CONSTEXPR void ignore_unused(const T&...) {}
+
+constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false)
+ FMT_NOEXCEPT -> bool {
#ifdef __cpp_lib_is_constant_evaluated
+ ignore_unused(default_value);
return std::is_constant_evaluated();
#else
- return false;
+ return default_value;
#endif
}
// A function to suppress "conditional expression is constant" warnings.
-template constexpr auto const_check(T value) -> T { return value; }
+template constexpr FMT_INLINE auto const_check(T value) -> T {
+ return value;
+}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
@@ -361,7 +343,8 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
#ifndef FMT_ASSERT
# ifdef NDEBUG
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
-# define FMT_ASSERT(condition, message) ((void)0)
+# define FMT_ASSERT(condition, message) \
+ ::fmt::detail::ignore_unused((condition), (message))
# else
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
@@ -451,13 +434,12 @@ template class basic_string_view {
*/
FMT_CONSTEXPR_CHAR_TRAITS
FMT_INLINE
- basic_string_view(const Char* s) : data_(s) {
- if (detail::const_check(std::is_same::value &&
- !detail::is_constant_evaluated()))
- size_ = std::strlen(reinterpret_cast(s));
- else
- size_ = std::char_traits::length(s);
- }
+ basic_string_view(const Char* s)
+ : data_(s),
+ size_(detail::const_check(std::is_same::value &&
+ !detail::is_constant_evaluated(true))
+ ? std::strlen(reinterpret_cast(s))
+ : std::char_traits::length(s)) {}
/** Constructs a string reference from a ``std::basic_string`` object. */
template
@@ -526,39 +508,21 @@ using string_view = basic_string_view;
template struct is_char : std::false_type {};
template <> struct is_char : std::true_type {};
-/**
- \rst
- Returns a string view of `s`. In order to add custom string type support to
- {fmt} provide an overload of `to_string_view` for it in the same namespace as
- the type for the argument-dependent lookup to work.
-
- **Example**::
-
- namespace my_ns {
- inline string_view to_string_view(const my_string& s) {
- return {s.data(), s.length()};
- }
- }
- std::string message = fmt::format(my_string("The answer is {}"), 42);
- \endrst
- */
+// Returns a string view of `s`.
template ::value)>
FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view {
return s;
}
-
template
inline auto to_string_view(const std::basic_string& s)
-> basic_string_view {
return s;
}
-
template
constexpr auto to_string_view(basic_string_view s)
-> basic_string_view {
return s;
}
-
template >::value)>
inline auto to_string_view(detail::std_string_view s)
@@ -609,12 +573,14 @@ FMT_INLINE void check_format_string(const S&) {
template ::value)>
void check_format_string(S);
+FMT_NORETURN FMT_API void throw_format_error(const char* message);
+
struct error_handler {
constexpr error_handler() = default;
constexpr error_handler(const error_handler&) = default;
// This function is intentionally not constexpr to give a compile-time error.
- FMT_NORETURN FMT_API void on_error(const char* message);
+ void on_error(const char* message) { throw_format_error(message); }
};
FMT_END_DETAIL_NAMESPACE
@@ -723,6 +689,22 @@ class appender;
FMT_BEGIN_DETAIL_NAMESPACE
+template
+constexpr auto has_const_formatter_impl(T*)
+ -> decltype(typename Context::template formatter_type().format(
+ std::declval(), std::declval()),
+ true) {
+ return true;
+}
+template
+constexpr auto has_const_formatter_impl(...) -> bool {
+ return false;
+}
+template
+constexpr auto has_const_formatter() -> bool {
+ return has_const_formatter_impl(static_cast(nullptr));
+}
+
// Extracts a reference to the container from back_insert_iterator.
template
inline auto get_container(std::back_insert_iterator it)
@@ -742,13 +724,13 @@ FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
return out;
}
-template ::value)>
-FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out)
- -> Char* {
- if (is_constant_evaluated())
- return copy_str(begin, end, out);
+template , U>::value&& is_char::value)>
+FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
+ if (is_constant_evaluated()) return copy_str(begin, end, out);
auto size = to_unsigned(end - begin);
- memcpy(out, begin, size);
+ memcpy(out, begin, size * sizeof(U));
return out + size;
}
@@ -769,22 +751,22 @@ template class buffer {
FMT_MSC_WARNING(suppress : 26495)
buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
- buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT
- : ptr_(p),
- size_(sz),
- capacity_(cap) {}
+ FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0,
+ size_t cap = 0) FMT_NOEXCEPT : ptr_(p),
+ size_(sz),
+ capacity_(cap) {}
- ~buffer() = default;
+ FMT_CONSTEXPR20 ~buffer() = default;
buffer(buffer&&) = default;
/** Sets the buffer data and capacity. */
- void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
+ FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
ptr_ = buf_data;
capacity_ = buf_capacity;
}
/** Increases the buffer capacity to hold at least *capacity* elements. */
- virtual void grow(size_t capacity) = 0;
+ virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
public:
using value_type = T;
@@ -800,23 +782,23 @@ template class buffer {
auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; }
/** Returns the size of this buffer. */
- auto size() const FMT_NOEXCEPT -> size_t { return size_; }
+ constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; }
/** Returns the capacity of this buffer. */
- auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; }
+ constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; }
/** Returns a pointer to the buffer data. */
- auto data() FMT_NOEXCEPT -> T* { return ptr_; }
+ FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; }
/** Returns a pointer to the buffer data. */
- auto data() const FMT_NOEXCEPT -> const T* { return ptr_; }
+ FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; }
/** Clears this buffer. */
void clear() { size_ = 0; }
// Tries resizing the buffer to contain *count* elements. If T is a POD type
// the new elements may not be initialized.
- void try_resize(size_t count) {
+ FMT_CONSTEXPR20 void try_resize(size_t count) {
try_reserve(count);
size_ = count <= capacity_ ? count : capacity_;
}
@@ -825,11 +807,11 @@ template class buffer {
// capacity by a smaller amount than requested but guarantees there is space
// for at least one additional element either by increasing the capacity or by
// flushing the buffer if it is full.
- void try_reserve(size_t new_capacity) {
+ FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
if (new_capacity > capacity_) grow(new_capacity);
}
- void push_back(const T& value) {
+ FMT_CONSTEXPR20 void push_back(const T& value) {
try_reserve(size_ + 1);
ptr_[size_++] = value;
}
@@ -837,8 +819,11 @@ template class buffer {
/** Appends data to the end of the buffer. */
template void append(const U* begin, const U* end);
- template auto operator[](I index) -> T& { return ptr_[index]; }
- template auto operator[](I index) const -> const T& {
+ template FMT_CONSTEXPR auto operator[](I index) -> T& {
+ return ptr_[index];
+ }
+ template
+ FMT_CONSTEXPR auto operator[](I index) const -> const T& {
return ptr_[index];
}
};
@@ -873,7 +858,7 @@ class iterator_buffer final : public Traits, public buffer {
T data_[buffer_size];
protected:
- void grow(size_t) final FMT_OVERRIDE {
+ void grow(size_t) override {
if (this->size() == buffer_size) flush();
}
@@ -897,9 +882,55 @@ class iterator_buffer final : public Traits, public buffer {
auto count() const -> size_t { return Traits::count() + this->size(); }
};
+template
+class iterator_buffer final
+ : public fixed_buffer_traits,
+ public buffer {
+ private:
+ T* out_;
+ enum { buffer_size = 256 };
+ T data_[buffer_size];
+
+ protected:
+ void grow(size_t) override {
+ if (this->size() == this->capacity()) flush();
+ }
+
+ void flush() {
+ size_t n = this->limit(this->size());
+ if (this->data() == out_) {
+ out_ += n;
+ this->set(data_, buffer_size);
+ }
+ this->clear();
+ }
+
+ public:
+ explicit iterator_buffer(T* out, size_t n = buffer_size)
+ : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {}
+ iterator_buffer(iterator_buffer&& other)
+ : fixed_buffer_traits(other),
+ buffer(std::move(other)),
+ out_(other.out_) {
+ if (this->data() != out_) {
+ this->set(data_, buffer_size);
+ this->clear();
+ }
+ }
+ ~iterator_buffer() { flush(); }
+
+ auto out() -> T* {
+ flush();
+ return out_;
+ }
+ auto count() const -> size_t {
+ return fixed_buffer_traits::count() + this->size();
+ }
+};
+
template class iterator_buffer final : public buffer {
protected:
- void grow(size_t) final FMT_OVERRIDE {}
+ void grow(size_t) override {}
public:
explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {}
@@ -917,7 +948,7 @@ class iterator_buffer,
Container& container_;
protected:
- void grow(size_t capacity) final FMT_OVERRIDE {
+ void grow(size_t capacity) override {
container_.resize(capacity);
this->set(&container_[0], capacity);
}
@@ -940,7 +971,7 @@ template class counting_buffer final : public buffer {
size_t count_ = 0;
protected:
- void grow(size_t) final FMT_OVERRIDE {
+ void grow(size_t) override {
if (this->size() != buffer_size) return;
count_ += this->size();
this->clear();
@@ -1112,6 +1143,11 @@ constexpr bool is_arithmetic_type(type t) {
return t > type::none_type && t <= type::last_numeric_type;
}
+struct unformattable {};
+struct unformattable_char : unformattable {};
+struct unformattable_const : unformattable {};
+struct unformattable_pointer : unformattable {};
+
template struct string_value {
const Char* data;
size_t size;
@@ -1124,8 +1160,8 @@ template struct named_arg_value {
template struct custom_value {
using parse_context = typename Context::parse_context_type;
- const void* value;
- void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
+ void* value;
+ void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
};
// A formatting argument value.
@@ -1159,8 +1195,8 @@ template class value {
constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
FMT_INLINE value(int128_t val) : int128_value(val) {}
FMT_INLINE value(uint128_t val) : uint128_value(val) {}
- FMT_INLINE value(float val) : float_value(val) {}
- FMT_INLINE value(double val) : double_value(val) {}
+ constexpr FMT_INLINE value(float val) : float_value(val) {}
+ constexpr FMT_INLINE value(double val) : double_value(val) {}
FMT_INLINE value(long double val) : long_double_value(val) {}
constexpr FMT_INLINE value(bool val) : bool_value(val) {}
constexpr FMT_INLINE value(char_type val) : char_value(val) {}
@@ -1176,26 +1212,34 @@ template class value {
FMT_INLINE value(const named_arg_info* args, size_t size)
: named_args{args, size} {}
- template FMT_CONSTEXPR FMT_INLINE value(const T& val) {
- custom.value = &val;
+ template FMT_CONSTEXPR FMT_INLINE value(T& val) {
+ using value_type = remove_cvref_t;
+ custom.value = const_cast(&val);
// Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter` for `format` and
// `printf_formatter` for `printf`.
custom.format = format_custom_arg<
- T, conditional_t::value,
- typename Context::template formatter_type,
- fallback_formatter>>;
+ value_type,
+ conditional_t::value,
+ typename Context::template formatter_type,
+ fallback_formatter>>;
}
+ value(unformattable);
+ value(unformattable_char);
+ value(unformattable_const);
+ value(unformattable_pointer);
private:
// Formats an argument of a custom type, such as a user-defined class.
template
- static void format_custom_arg(const void* arg,
+ static void format_custom_arg(void* arg,
typename Context::parse_context_type& parse_ctx,
Context& ctx) {
- Formatter f;
+ auto f = Formatter();
parse_ctx.advance_to(f.parse(parse_ctx));
- ctx.advance_to(f.format(*static_cast(arg), ctx));
+ using qualified_type =
+ conditional_t(), const T, T>;
+ ctx.advance_to(f.format(*static_cast(arg), ctx));
}
};
@@ -1208,9 +1252,9 @@ enum { long_short = sizeof(long) == sizeof(int) };
using long_type = conditional_t;
using ulong_type = conditional_t;
-struct unformattable {};
-
// Maps formatting arguments to core types.
+// arg_mapper reports errors by returning unformattable instead of using
+// static_assert because it's used in the is_formattable trait.
template struct arg_mapper {
using char_type = typename Context::char_type;
@@ -1237,13 +1281,22 @@ template struct arg_mapper {
FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
- template ::value)>
+ template ::value ||
+ std::is_same::value)>
FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
- static_assert(
- std::is_same::value || std::is_same::value,
- "mixing character types is disallowed");
return val;
}
+ template ::value ||
+#ifdef __cpp_char8_t
+ std::is_same::value ||
+#endif
+ std::is_same::value ||
+ std::is_same::value) &&
+ !std::is_same::value,
+ int> = 0>
+ FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
+ return {};
+ }
FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
@@ -1257,13 +1310,19 @@ template struct arg_mapper {
FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
return val;
}
- template ::value)>
+ template ::value && !std::is_pointer::value &&
+ std::is_same>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-> basic_string_view {
- static_assert(std::is_same>::value,
- "mixing character types is disallowed");
return to_string_view(val);
}
+ template ::value && !std::is_pointer::value &&
+ !std::is_same>::value)>
+ FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
+ return {};
+ }
template , T>::value &&
@@ -1284,21 +1343,21 @@ template struct arg_mapper {
-> basic_string_view {
return std_string_view(val);
}
- FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) -> const char* {
- static_assert(std::is_same::value, "invalid string type");
- return reinterpret_cast(val);
+ FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val)
+ -> decltype(this->map("")) {
+ return map(reinterpret_cast(val));
}
- FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) -> const char* {
- static_assert(std::is_same::value, "invalid string type");
- return reinterpret_cast(val);
+ FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val)
+ -> decltype(this->map("")) {
+ return map(reinterpret_cast(val));
}
- FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) -> const char* {
- const auto* const_val = val;
- return map(const_val);
+ FMT_CONSTEXPR FMT_INLINE auto map(signed char* val)
+ -> decltype(this->map("")) {
+ return map(reinterpret_cast(val));
}
- FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) -> const char* {
- const auto* const_val = val;
- return map(const_val);
+ FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val)
+ -> decltype(this->map("")) {
+ return map(reinterpret_cast(val));
}
FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
@@ -1311,17 +1370,16 @@ template struct arg_mapper {
// We use SFINAE instead of a const T* parameter to avoid conflicting with
// the C array overload.
- template
- FMT_CONSTEXPR auto map(T) -> enable_if_t::value, int> {
- // Formatting of arbitrary pointers is disallowed. If you want to output
- // a pointer cast it to "void *" or "const void *". In particular, this
- // forbids formatting of "[const] volatile char *" which is printed as bool
- // by iostreams.
- static_assert(!sizeof(T), "formatting of non-void pointers is disallowed");
- return 0;
+ template <
+ typename T,
+ FMT_ENABLE_IF(std::is_convertible::value &&
+ !std::is_convertible::value)>
+ FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
+ return {};
}
- template
+ template ::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
return values;
}
@@ -1335,13 +1393,38 @@ template struct arg_mapper {
static_cast::type>(val))) {
return map(static_cast::type>(val));
}
- template ::value && !is_char::value &&
- (has_formatter::value ||
- has_fallback_formatter::value))>
- FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> const T& {
+
+ template >
+ struct formattable
+ : bool_constant() ||
+ !std::is_const>::value ||
+ has_fallback_formatter::value> {};
+
+#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910
+ // Workaround a bug in MSVC.
+ template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
+ return val;
+ }
+#else
+ template ::value)>
+ FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
return val;
}
+ template ::value)>
+ FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const {
+ return {};
+ }
+#endif
+
+ template ,
+ FMT_ENABLE_IF(!is_string::value && !is_char::value &&
+ !std::is_array::value &&
+ (has_formatter::value ||
+ has_fallback_formatter::value))>
+ FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
+ -> decltype(this->do_map(std::forward(val))) {
+ return do_map(std::forward(val));
+ }
template ::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
@@ -1574,10 +1657,30 @@ FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg {
// another (not recommended).
template
-FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value {
- const auto& arg = arg_mapper().map(val);
+FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value {
+ const auto& arg = arg_mapper().map(std::forward(val));
+
+ constexpr bool formattable_char =
+ !std::is_same::value;
+ static_assert(formattable_char, "Mixing character types is disallowed.");
+
+ constexpr bool formattable_const =
+ !std::is_same::value;
+ static_assert(formattable_const, "Cannot format a const argument.");
+
+ // Formatting of arbitrary pointers is disallowed. If you want to output
+ // a pointer cast it to "void *" or "const void *". In particular, this
+ // forbids formatting of "[const] volatile char *" which is printed as bool
+ // by iostreams.
+ constexpr bool formattable_pointer =
+ !std::is_same::value;
+ static_assert(formattable_pointer,
+ "Formatting of non-void pointers is disallowed.");
+
+ constexpr bool formattable =
+ !std::is_same::value;
static_assert(
- !std::is_same::value,
+ formattable,
"Cannot format an argument. To make type T formattable provide a "
"formatter specialization: https://fmt.dev/latest/api.html#udt");
return {arg};
@@ -1655,9 +1758,9 @@ using format_context = buffer_context;
template
using is_formattable = bool_constant<
- !std::is_same>().map(
- std::declval())),
- detail::unformattable>::value &&
+ !std::is_base_of>().map(
+ std::declval()))>::value &&
!detail::has_fallback_formatter::value>;
/**
@@ -1696,14 +1799,16 @@ class format_arg_store
: 0);
public:
- FMT_CONSTEXPR FMT_INLINE format_arg_store(const Args&... args)
+ template
+ FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args(*this),
#endif
data_{detail::make_arg<
is_packed, Context,
- detail::mapped_type_constant::value>(args)...} {
+ detail::mapped_type_constant, Context>::value>(
+ std::forward(args))...} {
detail::init_named_args(data_.named_args(), 0, 0, args...);
}
};
@@ -1717,9 +1822,9 @@ class format_arg_store
\endrst
*/
template
-constexpr auto make_format_args(const Args&... args)
- -> format_arg_store {
- return {args...};
+constexpr auto make_format_args(Args&&... args)
+ -> format_arg_store...> {
+ return {std::forward(args)...};
}
/**
@@ -1878,8 +1983,6 @@ using sign_t = sign::type;
FMT_BEGIN_DETAIL_NAMESPACE
-void throw_format_error(const char* message);
-
// Workaround an array initialization issue in gcc 4.8.
template struct fill_t {
private:
@@ -1905,11 +2008,33 @@ template struct fill_t {
};
FMT_END_DETAIL_NAMESPACE
+enum class presentation_type : unsigned char {
+ none,
+ // Integer types should go first,
+ dec, // 'd'
+ oct, // 'o'
+ hex_lower, // 'x'
+ hex_upper, // 'X'
+ bin_lower, // 'b'
+ bin_upper, // 'B'
+ hexfloat_lower, // 'a'
+ hexfloat_upper, // 'A'
+ exp_lower, // 'e'
+ exp_upper, // 'E'
+ fixed_lower, // 'f'
+ fixed_upper, // 'F'
+ general_lower, // 'g'
+ general_upper, // 'G'
+ chr, // 'c'
+ string, // 's'
+ pointer // 'p'
+};
+
// Format specifiers for built-in and string types.
template struct basic_format_specs {
int width;
int precision;
- char type;
+ presentation_type type;
align_t align : 4;
sign_t sign : 3;
bool alt : 1; // Alternate form ('#').
@@ -1919,7 +2044,7 @@ template struct basic_format_specs {
constexpr basic_format_specs()
: width(0),
precision(-1),
- type(0),
+ type(presentation_type::none),
align(align::none),
sign(sign::none),
alt(false),
@@ -1999,9 +2124,7 @@ template class specs_setter {
}
FMT_CONSTEXPR void end_precision() {}
- FMT_CONSTEXPR void on_type(Char type) {
- specs_.type = static_cast(type);
- }
+ FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
};
// Format spec handler that saves references to arguments representing dynamic
@@ -2075,8 +2198,8 @@ constexpr auto to_ascii(Char value) ->
template
FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
if (const_check(sizeof(Char) != 1)) return 1;
- constexpr char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
+ auto lengths =
+ "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4";
int len = lengths[static_cast(*begin) >> 3];
// Compute the pointer to the next character early so that the next
@@ -2283,6 +2406,48 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
return begin;
}
+template
+FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type {
+ switch (to_ascii(type)) {
+ case 'd':
+ return presentation_type::dec;
+ case 'o':
+ return presentation_type::oct;
+ case 'x':
+ return presentation_type::hex_lower;
+ case 'X':
+ return presentation_type::hex_upper;
+ case 'b':
+ return presentation_type::bin_lower;
+ case 'B':
+ return presentation_type::bin_upper;
+ case 'a':
+ return presentation_type::hexfloat_lower;
+ case 'A':
+ return presentation_type::hexfloat_upper;
+ case 'e':
+ return presentation_type::exp_lower;
+ case 'E':
+ return presentation_type::exp_upper;
+ case 'f':
+ return presentation_type::fixed_lower;
+ case 'F':
+ return presentation_type::fixed_upper;
+ case 'g':
+ return presentation_type::general_lower;
+ case 'G':
+ return presentation_type::general_upper;
+ case 'c':
+ return presentation_type::chr;
+ case 's':
+ return presentation_type::string;
+ case 'p':
+ return presentation_type::pointer;
+ default:
+ return presentation_type::none;
+ }
+}
+
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
template
@@ -2292,7 +2457,10 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin,
-> const Char* {
if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) &&
*begin != 'L') {
- handler.on_type(*begin++);
+ presentation_type type = parse_presentation_type(*begin++);
+ if (type == presentation_type::none)
+ handler.on_error("invalid type specifier");
+ handler.on_type(type);
return begin;
}
@@ -2346,7 +2514,12 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin,
}
// Parse type.
- if (begin != end && *begin != '}') handler.on_type(*begin++);
+ if (begin != end && *begin != '}') {
+ presentation_type type = parse_presentation_type(*begin++);
+ if (type == presentation_type::none)
+ handler.on_error("invalid type specifier");
+ handler.on_type(type);
+ }
return begin;
}
@@ -2393,7 +2566,7 @@ FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
template
FMT_CONSTEXPR FMT_INLINE void parse_format_string(
basic_string_view format_str, Handler&& handler) {
- // this is most likely a name-lookup defect in msvc's modules implementation
+ // Workaround a name-lookup bug in MSVC's modules implementation.
using detail::find;
auto begin = format_str.data();
@@ -2421,7 +2594,7 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string(
if (pbegin == pend) return;
for (;;) {
const Char* p = nullptr;
- if (!find(pbegin, pend, '}', p))
+ if (!find(pbegin, pend, Char('}'), p))
return handler_.on_text(pbegin, pend);
++p;
if (p == pend || *p != '}')
@@ -2436,7 +2609,7 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string(
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const Char* p = begin;
- if (*begin != '{' && !find(begin + 1, end, '{', p))
+ if (*begin != '{' && !find(begin + 1, end, Char('{'), p))
return write(begin, end);
write(begin, p);
begin = parse_replacement_field(p, end, handler);
@@ -2488,28 +2661,18 @@ class compile_parse_context
};
template
-FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) {
- switch (spec) {
- case 0:
- case 'd':
- case 'x':
- case 'X':
- case 'b':
- case 'B':
- case 'o':
- case 'c':
- break;
- default:
+FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
+ ErrorHandler&& eh) {
+ if (type > presentation_type::bin_upper && type != presentation_type::chr)
eh.on_error("invalid type specifier");
- break;
- }
}
// Checks char specs and returns true if the type spec is char (and not int).
template
FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs,
ErrorHandler&& eh = {}) -> bool {
- if (specs.type && specs.type != 'c') {
+ if (specs.type != presentation_type::none &&
+ specs.type != presentation_type::chr) {
check_int_type_spec(specs.type, eh);
return false;
}
@@ -2533,7 +2696,7 @@ struct float_specs {
bool upper : 1;
bool locale : 1;
bool binary32 : 1;
- bool use_grisu : 1;
+ bool fallback : 1;
bool showpoint : 1;
};
@@ -2545,33 +2708,33 @@ FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs,
result.showpoint = specs.alt;
result.locale = specs.localized;
switch (specs.type) {
- case 0:
+ case presentation_type::none:
result.format = float_format::general;
break;
- case 'G':
+ case presentation_type::general_upper:
result.upper = true;
FMT_FALLTHROUGH;
- case 'g':
+ case presentation_type::general_lower:
result.format = float_format::general;
break;
- case 'E':
+ case presentation_type::exp_upper:
result.upper = true;
FMT_FALLTHROUGH;
- case 'e':
+ case presentation_type::exp_lower:
result.format = float_format::exp;
result.showpoint |= specs.precision != 0;
break;
- case 'F':
+ case presentation_type::fixed_upper:
result.upper = true;
FMT_FALLTHROUGH;
- case 'f':
+ case presentation_type::fixed_lower:
result.format = float_format::fixed;
result.showpoint |= specs.precision != 0;
break;
- case 'A':
+ case presentation_type::hexfloat_upper:
result.upper = true;
FMT_FALLTHROUGH;
- case 'a':
+ case presentation_type::hexfloat_lower:
result.format = float_format::hex;
break;
default:
@@ -2581,22 +2744,27 @@ FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs