mirror of
https://github.com/Jous99/F4MP.git
synced 2026-01-12 14:30:54 +01:00
subida de archivos
archivos originales del proyecto F4MP
This commit is contained in:
parent
d8f75c4612
commit
e52b7b446a
290 changed files with 138049 additions and 0 deletions
38
f4mp_originalcode/F4MPClient/.gitignore
vendored
Normal file
38
f4mp_originalcode/F4MPClient/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#Cmake
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
out/
|
||||||
|
cmake-build-debug/
|
||||||
|
cmake-build-release/
|
||||||
|
build/
|
||||||
|
.idea/
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
48
f4mp_originalcode/F4MPClient/CMakeLists.txt
Normal file
48
f4mp_originalcode/F4MPClient/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
cmake_minimum_required(VERSION 3.7)
|
||||||
|
project(F4MPClient)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} CMakeModules/)
|
||||||
|
|
||||||
|
set(EXECUTABLE F4MPClient)
|
||||||
|
set(SRC_DIR ./src)
|
||||||
|
set(INCLUDE_DIR ./include)
|
||||||
|
|
||||||
|
# Setting resource path
|
||||||
|
add_definitions(-D_RESOURCES="${CMAKE_CURRENT_SOURCE_DIR}/resources/")
|
||||||
|
message(${CMAKE_CURRENT_SOURCE_DIR}/resources)
|
||||||
|
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAD ON)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
include_directories(${EXECUTABLE}
|
||||||
|
${INCLUDE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
file(GLOB SourceFiles
|
||||||
|
${SRC_DIR}/*
|
||||||
|
${SRC_DIR}/Utils/*
|
||||||
|
${SRC_DIR}/ThirdParty/*
|
||||||
|
${SRC_DIR}/Hooks/*
|
||||||
|
${SRC_DIR}/imgui/*
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(Common)
|
||||||
|
|
||||||
|
add_library(${EXECUTABLE} SHARED ${SourceFiles})
|
||||||
|
|
||||||
|
set_target_properties(${EXECUTABLE} PROPERTIES
|
||||||
|
CXX_STANDARD 17
|
||||||
|
CXX_EXTENSIONS ON
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${EXECUTABLE}
|
||||||
|
${CMAKE_DL_LIBS}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/lib/steam_api64.lib
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/lib/detours.lib
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/lib/spdlogd.lib
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/lib/GameNetworkingSockets_s.lib
|
||||||
|
Common
|
||||||
|
d3d11.lib
|
||||||
|
dxgi.dll
|
||||||
|
)
|
||||||
28
f4mp_originalcode/F4MPClient/CMakeSettings.json
Normal file
28
f4mp_originalcode/F4MPClient/CMakeSettings.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "x64-Debug",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"configurationType": "Debug",
|
||||||
|
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||||
|
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||||
|
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||||
|
"cmakeCommandArgs": "",
|
||||||
|
"buildCommandArgs": "-v",
|
||||||
|
"ctestCommandArgs": "",
|
||||||
|
"variables": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x64-Release",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"configurationType": "RelWithDebInfo",
|
||||||
|
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||||
|
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||||
|
"cmakeCommandArgs": "",
|
||||||
|
"buildCommandArgs": "-v",
|
||||||
|
"ctestCommandArgs": "",
|
||||||
|
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||||
|
"variables": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
674
f4mp_originalcode/F4MPClient/LICENSE
Normal file
674
f4mp_originalcode/F4MPClient/LICENSE
Normal file
|
|
@ -0,0 +1,674 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
1
f4mp_originalcode/F4MPClient/README.md
Normal file
1
f4mp_originalcode/F4MPClient/README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# client
|
||||||
14
f4mp_originalcode/F4MPClient/common/CMakeLists.txt
Normal file
14
f4mp_originalcode/F4MPClient/common/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
set(PROJ Common)
|
||||||
|
|
||||||
|
set(SRC_DIR ./src)
|
||||||
|
set(INCLUDE_DIR ./include)
|
||||||
|
|
||||||
|
file(GLOB SOURCE_FILES
|
||||||
|
${SRC_DIR}/*
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library (${PROJ} STATIC ${SOURCE_FILES})
|
||||||
|
|
||||||
|
include_directories(${PROJ} ${INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
target_include_directories (${PROJ} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/*)
|
||||||
126
f4mp_originalcode/F4MPClient/common/include/Exceptions.h
Normal file
126
f4mp_originalcode/F4MPClient/common/include/Exceptions.h
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
#ifndef COMMON_EXCEPTIONS_H
|
||||||
|
#define COMMON_EXCEPTIONS_H
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace Exceptions {
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
namespace Exceptions {
|
||||||
|
class RuntimeException : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit RuntimeException(const std::string &msg) :
|
||||||
|
std::runtime_error(msg.c_str())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit RuntimeException(const char *msg) :
|
||||||
|
std::runtime_error(msg)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class DetourException : public RuntimeException
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DetourException(const std::string &msg) :
|
||||||
|
RuntimeException(msg.c_str())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit DetourException(const char *msg) :
|
||||||
|
RuntimeException(msg)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class GenericWinAPIException : public std::runtime_error
|
||||||
|
{
|
||||||
|
DWORD last_error_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GenericWinAPIException(const std::string &msg) :
|
||||||
|
std::runtime_error(msg.c_str()), last_error_(GetLastError())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit GenericWinAPIException(const char *msg) :
|
||||||
|
std::runtime_error(msg), last_error_(GetLastError())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual DWORD get_last_error() const
|
||||||
|
{
|
||||||
|
return last_error_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModuleNotFoundException : public GenericWinAPIException
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ModuleNotFoundException(const std::string &msg) :
|
||||||
|
GenericWinAPIException(msg)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit ModuleNotFoundException(const char *msg) :
|
||||||
|
GenericWinAPIException(msg)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProcAddressNotFoundException : public GenericWinAPIException
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ProcAddressNotFoundException(const std::string &msg) :
|
||||||
|
GenericWinAPIException(msg)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit ProcAddressNotFoundException(const char *msg) :
|
||||||
|
GenericWinAPIException(msg)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class COMInterfaceException : public RuntimeException
|
||||||
|
{
|
||||||
|
HRESULT hresult_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit COMInterfaceException(const std::string &msg, HRESULT result) :
|
||||||
|
RuntimeException(msg), hresult_(result)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit COMInterfaceException(const char *msg, HRESULT result) :
|
||||||
|
RuntimeException(msg), hresult_(result)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
HRESULT hresult() const
|
||||||
|
{
|
||||||
|
return hresult_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DXAPIException : public COMInterfaceException
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DXAPIException(const std::string &msg, HRESULT result) :
|
||||||
|
COMInterfaceException(msg, result)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit DXAPIException(const char *msg, HRESULT result) :
|
||||||
|
COMInterfaceException(msg, result)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ARCException : public COMInterfaceException
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ARCException(const std::string &msg, HRESULT result) :
|
||||||
|
COMInterfaceException(msg, result)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
explicit ARCException(const char *msg, HRESULT result) :
|
||||||
|
COMInterfaceException(msg, result)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
#endif
|
||||||
81
f4mp_originalcode/F4MPClient/common/include/GamePtr.h
Normal file
81
f4mp_originalcode/F4MPClient/common/include/GamePtr.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#ifndef COMMON_GAMEPTR_H
|
||||||
|
#define COMMON_GAMEPTR_H
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <utility>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Memory {
|
||||||
|
|
||||||
|
class GamePtr_Manager {
|
||||||
|
public:
|
||||||
|
GamePtr_Manager();
|
||||||
|
|
||||||
|
static uintptr_t s_baseAddress;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class GamePtr {
|
||||||
|
public :
|
||||||
|
GamePtr(uintptr_t offset) : offset(offset + Memory::GamePtr_Manager::s_baseAddress){}
|
||||||
|
|
||||||
|
T * GetPtr() const {
|
||||||
|
return reinterpret_cast <T *>(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
T * operator->() const
|
||||||
|
{
|
||||||
|
return GetPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T *() const
|
||||||
|
{
|
||||||
|
return GetPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!() const {
|
||||||
|
return !GetPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
const T * GetConstPtr() const {
|
||||||
|
return reinterpret_cast <T *>(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t GetUIntPtr() const
|
||||||
|
{
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
uintptr_t offset;
|
||||||
|
|
||||||
|
GamePtr();
|
||||||
|
GamePtr(GamePtr & rhs);
|
||||||
|
GamePtr & operator=(GamePtr & rhs);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class GameAddr{
|
||||||
|
public:
|
||||||
|
GameAddr(uintptr_t offset) : offset(reinterpret_cast<ConversionType *>(offset + Memory::GamePtr_Manager::s_baseAddress)){}
|
||||||
|
|
||||||
|
operator T() {
|
||||||
|
return reinterpret_cast<T>(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t GetUIntPtr(){
|
||||||
|
return reinterpret_cast<uintptr_t >(offset);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
struct ConversionType {};
|
||||||
|
|
||||||
|
ConversionType * offset;
|
||||||
|
|
||||||
|
GameAddr();
|
||||||
|
GameAddr(GameAddr & rhs);
|
||||||
|
GameAddr & operator=(GameAddr & rhs);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
257
f4mp_originalcode/F4MPClient/common/include/Hook.h
Normal file
257
f4mp_originalcode/F4MPClient/common/include/Hook.h
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
#ifndef COMMON_HOOK_H
|
||||||
|
#define COMMON_HOOK_H
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "detours.h"
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include "Exceptions.h"
|
||||||
|
|
||||||
|
namespace Hooks {
|
||||||
|
|
||||||
|
enum class CallConvention
|
||||||
|
{
|
||||||
|
stdcall_t,
|
||||||
|
cdecl_t
|
||||||
|
};
|
||||||
|
|
||||||
|
template <CallConvention cc, typename retn, typename convention, typename ...args>
|
||||||
|
struct convention;
|
||||||
|
|
||||||
|
template <typename retn, typename ...args>
|
||||||
|
struct convention<CallConvention::stdcall_t, retn, args...>
|
||||||
|
{
|
||||||
|
typedef retn (__stdcall *type)(args ...);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename retn, typename ...args>
|
||||||
|
struct convention<CallConvention::cdecl_t, retn, args...>
|
||||||
|
{
|
||||||
|
typedef retn (__cdecl *type)(args ...);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <CallConvention cc, typename retn, typename ...args>
|
||||||
|
class Hook
|
||||||
|
{
|
||||||
|
typedef typename convention<cc, retn, args...>::type type;
|
||||||
|
|
||||||
|
size_t orig_;
|
||||||
|
type detour_;
|
||||||
|
|
||||||
|
bool is_applied_;
|
||||||
|
bool has_open_transaction_;
|
||||||
|
|
||||||
|
void transaction_begin()
|
||||||
|
{
|
||||||
|
const auto result = DetourTransactionBegin();
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
if (result == ERROR_INVALID_OPERATION)
|
||||||
|
{
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"A pending transaction already exists"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException("Unknown error");
|
||||||
|
}
|
||||||
|
|
||||||
|
has_open_transaction_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transaction_commit()
|
||||||
|
{
|
||||||
|
const auto result = DetourTransactionCommit();
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case ERROR_INVALID_DATA:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"Target function was changed by third party between steps of the transaction"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_INVALID_OPERATION:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"No pending transaction exists"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_INVALID_BLOCK:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"The function referenced is too small to be detoured"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_INVALID_HANDLE:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"The ppPointer parameter is null or points to a null pointer"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_NOT_ENOUGH_MEMORY:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"Not enough memory exists to complete the operation"
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"Unknown error"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
has_open_transaction_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_thread(HANDLE hThread)
|
||||||
|
{
|
||||||
|
const auto result = DetourUpdateThread(hThread);
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
if (result == ERROR_NOT_ENOUGH_MEMORY)
|
||||||
|
{
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"Not enough memory to record identity of thread"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Hook() : orig_(0), detour_(0), is_applied_(false), has_open_transaction_(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~Hook() noexcept(false)
|
||||||
|
{
|
||||||
|
if (has_open_transaction_)
|
||||||
|
{
|
||||||
|
const auto result = DetourTransactionAbort();
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
if (result == ERROR_INVALID_OPERATION)
|
||||||
|
{
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"No pending transaction exists"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void apply(T pFunc, type detour)
|
||||||
|
{
|
||||||
|
detour_ = detour;
|
||||||
|
orig_ = static_cast<size_t>(pFunc);
|
||||||
|
|
||||||
|
transaction_begin();
|
||||||
|
update_thread(GetCurrentThread());
|
||||||
|
const auto result = DetourAttach(reinterpret_cast<void **>(&orig_), reinterpret_cast<void *>(detour_));
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case ERROR_INVALID_BLOCK:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"The function referenced is too small to be detoured"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_INVALID_HANDLE:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"The ppPointer parameter is null or points to a null pointer"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_INVALID_OPERATION:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"No pending transaction exists"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_NOT_ENOUGH_MEMORY:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"Not enough memory exists to complete the operation"
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction_commit();
|
||||||
|
|
||||||
|
is_applied_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove()
|
||||||
|
{
|
||||||
|
if (!is_applied_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
is_applied_ = false;
|
||||||
|
|
||||||
|
transaction_begin();
|
||||||
|
update_thread(GetCurrentThread());
|
||||||
|
const auto result = DetourDetach(reinterpret_cast<void **>(&orig_), reinterpret_cast<void *>(detour_));
|
||||||
|
|
||||||
|
if (result != NO_ERROR)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case ERROR_INVALID_BLOCK:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"The function to be detached was too small to be detoured"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_INVALID_HANDLE:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"The ppPointer parameter is null or points to a null pointer"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_INVALID_OPERATION:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"No pending transaction exists"
|
||||||
|
);
|
||||||
|
|
||||||
|
case ERROR_NOT_ENOUGH_MEMORY:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"Not enough memory exists to complete the operation"
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Exceptions::Core::Exceptions::DetourException(
|
||||||
|
"Unknown error"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction_commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_orig(){
|
||||||
|
return orig_;
|
||||||
|
}
|
||||||
|
|
||||||
|
retn call_orig(args ... p)
|
||||||
|
{
|
||||||
|
return type(orig_)(p...);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool is_applied() const
|
||||||
|
{
|
||||||
|
return is_applied_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
20
f4mp_originalcode/F4MPClient/common/include/Types.h
Normal file
20
f4mp_originalcode/F4MPClient/common/include/Types.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
#ifndef COMMON_TYPES_H
|
||||||
|
#define COMMON_TYPES_H
|
||||||
|
|
||||||
|
namespace Types {
|
||||||
|
typedef unsigned char UInt8; //!< An unsigned 8-bit integer value
|
||||||
|
typedef unsigned short UInt16; //!< An unsigned 16-bit integer value
|
||||||
|
typedef unsigned long UInt32; //!< An unsigned 32-bit integer value
|
||||||
|
typedef unsigned long long UInt64; //!< An unsigned 64-bit integer value
|
||||||
|
typedef signed char SInt8; //!< A signed 8-bit integer value
|
||||||
|
typedef signed short SInt16; //!< A signed 16-bit integer value
|
||||||
|
typedef signed long SInt32; //!< A signed 32-bit integer value
|
||||||
|
typedef signed long long SInt64; //!< A signed 64-bit integer value
|
||||||
|
typedef float Float32; //!< A 32-bit floating point value
|
||||||
|
typedef double Float64; //!< A 64-bit floating point value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //F4MPREVERSE_TYPES_H
|
||||||
215
f4mp_originalcode/F4MPClient/common/include/Utilities.h
Normal file
215
f4mp_originalcode/F4MPClient/common/include/Utilities.h
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
#ifndef COMMON_UTILITIES_H
|
||||||
|
#define COMMON_UTILITIES_H
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "Types.h"
|
||||||
|
#include "GamePtr.h"
|
||||||
|
|
||||||
|
namespace Utilities {
|
||||||
|
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable : 4200)
|
||||||
|
struct RTTIType
|
||||||
|
{
|
||||||
|
void * typeInfo;
|
||||||
|
Types::UInt64 data;
|
||||||
|
char name[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RTTILocator
|
||||||
|
{
|
||||||
|
Types::UInt32 sig, offset, cdOffset;
|
||||||
|
Types::UInt32 typeDesc;
|
||||||
|
Types::UInt32 classDesc;
|
||||||
|
};
|
||||||
|
#pragma warning (pop)
|
||||||
|
|
||||||
|
const char * GetObjClassName(void * objBase);
|
||||||
|
void DumpClass(void * theClassPtr, Types::UInt64 nIntsToDump);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
uintptr_t GetFnAddr(T src)
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uintptr_t u;
|
||||||
|
T t;
|
||||||
|
} data;
|
||||||
|
|
||||||
|
data.t = src;
|
||||||
|
|
||||||
|
return data.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
class IAT {
|
||||||
|
public:
|
||||||
|
IAT(void * module,const char * DllName,const char * ImportName) : module(module), DllName(DllName), ImportName(ImportName) {
|
||||||
|
base = (Types::UInt8 *)module;
|
||||||
|
dosHeader = (IMAGE_DOS_HEADER *)base;
|
||||||
|
ntHeader = (IMAGE_NT_HEADERS *)(base + dosHeader->e_lfanew);
|
||||||
|
importTable = (IMAGE_IMPORT_DESCRIPTOR *)(base + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||||
|
};
|
||||||
|
|
||||||
|
void * Get();
|
||||||
|
private:
|
||||||
|
void * module;
|
||||||
|
const char * DllName;
|
||||||
|
const char * ImportName;
|
||||||
|
|
||||||
|
Types::UInt8 * base;
|
||||||
|
IMAGE_DOS_HEADER * dosHeader;
|
||||||
|
IMAGE_NT_HEADERS * ntHeader;
|
||||||
|
IMAGE_IMPORT_DESCRIPTOR * importTable;
|
||||||
|
};
|
||||||
|
|
||||||
|
//+++THANKS TO F4SE TEAM
|
||||||
|
#define MEMBER_FN_PREFIX(className) \
|
||||||
|
typedef className _MEMBER_FN_BASE_TYPE
|
||||||
|
|
||||||
|
#define DEFINE_MEMBER_FN_LONG(className, functionName, retnType, address, ...) \
|
||||||
|
typedef retnType (className::* _##functionName##_type)(__VA_ARGS__); \
|
||||||
|
\
|
||||||
|
inline _##functionName##_type * _##functionName##_GetPtr(void) \
|
||||||
|
{ \
|
||||||
|
static uintptr_t _address; \
|
||||||
|
_address = address + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
return (_##functionName##_type *)&_address; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_MEMBER_FN(functionName, retnType, address, ...) \
|
||||||
|
DEFINE_MEMBER_FN_LONG(_MEMBER_FN_BASE_TYPE, functionName, retnType, address, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define DEFINE_STATIC_HEAP(staticAllocate, staticFree) \
|
||||||
|
static void * operator new(std::size_t size) \
|
||||||
|
{ \
|
||||||
|
return staticAllocate(size); \
|
||||||
|
} \
|
||||||
|
static void * operator new(std::size_t size, const std::nothrow_t &) \
|
||||||
|
{ \
|
||||||
|
return staticAllocate(size); \
|
||||||
|
} \
|
||||||
|
static void * operator new(std::size_t size, void * ptr) \
|
||||||
|
{ \
|
||||||
|
return ptr; \
|
||||||
|
} \
|
||||||
|
static void operator delete(void * ptr) \
|
||||||
|
{ \
|
||||||
|
staticFree(ptr); \
|
||||||
|
} \
|
||||||
|
static void operator delete(void * ptr, const std::nothrow_t &) \
|
||||||
|
{ \
|
||||||
|
staticFree(ptr); \
|
||||||
|
} \
|
||||||
|
static void operator delete(void *, void *) \
|
||||||
|
{ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CALL_MEMBER_FN(obj, fn) \
|
||||||
|
((*(obj)).*(*((obj)->_##fn##_GetPtr())))
|
||||||
|
|
||||||
|
|
||||||
|
// Using the original implementation does very broken things in a Release build
|
||||||
|
// For classes like BSGFxShaderFXTarget and GameMenuBase
|
||||||
|
#define FORCE_INLINE __forceinline
|
||||||
|
#define DEFINE_MEMBER_FN_0(fnName, retnType, addr) \
|
||||||
|
FORCE_INLINE retnType fnName() { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_1(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_2(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_3(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2, typename T3> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2, t3); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_4(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2, typename T3, typename T4> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2, t3, t4); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_5(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2, typename T3, typename T4, typename T5> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2, t3, t4, t5); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_6(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2, t3, t4, t5, t6); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_7(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6, T7 && t7) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2, t3, t4, t5, t6, t7); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_8(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6, T7 && t7, T8 && t8) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2, t3, t4, t5, t6, t7, t8); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_9(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6, T7 && t7, T8 && t8, T9 && t9) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2, t3, t4, t5, t6, t7, t8, t9); \
|
||||||
|
}
|
||||||
|
#define DEFINE_MEMBER_FN_10(fnName, retnType, addr, ...) \
|
||||||
|
template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10> \
|
||||||
|
FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6, T7 && t7, T8 && t8, T9 && t9, T10 && t10) { \
|
||||||
|
struct empty_struct {}; \
|
||||||
|
typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \
|
||||||
|
const static uintptr_t address = addr + Memory::GamePtr_Manager::s_baseAddress; \
|
||||||
|
_##fnName##_type fn = *(_##fnName##_type*)&address; \
|
||||||
|
return (reinterpret_cast<empty_struct*>(this)->*fn)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); \
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
1059
f4mp_originalcode/F4MPClient/common/include/detours.h
Normal file
1059
f4mp_originalcode/F4MPClient/common/include/detours.h
Normal file
File diff suppressed because it is too large
Load diff
1
f4mp_originalcode/F4MPClient/common/include/main.h
Normal file
1
f4mp_originalcode/F4MPClient/common/include/main.h
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
#pragma once
|
||||||
12
f4mp_originalcode/F4MPClient/common/src/GamePtr.cpp
Normal file
12
f4mp_originalcode/F4MPClient/common/src/GamePtr.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include <include/GamePtr.h>
|
||||||
|
|
||||||
|
#pragma warning(disable: 4073)
|
||||||
|
#pragma init_seg(lib)
|
||||||
|
|
||||||
|
static Memory::GamePtr_Manager sRelocMgr;
|
||||||
|
|
||||||
|
uintptr_t Memory::GamePtr_Manager::s_baseAddress = 0;
|
||||||
|
|
||||||
|
Memory::GamePtr_Manager::GamePtr_Manager(){
|
||||||
|
Memory::GamePtr_Manager::s_baseAddress = reinterpret_cast<uintptr_t>(GetModuleHandle(NULL));
|
||||||
|
}
|
||||||
93
f4mp_originalcode/F4MPClient/include/spdlog/async.h
Normal file
93
f4mp_originalcode/F4MPClient/include/spdlog/async.h
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//
|
||||||
|
// Async logging using global thread pool
|
||||||
|
// All loggers created here share same global thread pool.
|
||||||
|
// Each log message is pushed to a queue along with a shared pointer to the
|
||||||
|
// logger.
|
||||||
|
// If a logger deleted while having pending messages in the queue, it's actual
|
||||||
|
// destruction will defer
|
||||||
|
// until all its messages are processed by the thread pool.
|
||||||
|
// This is because each message in the queue holds a shared_ptr to the
|
||||||
|
// originating logger.
|
||||||
|
|
||||||
|
#include <spdlog/async_logger.h>
|
||||||
|
#include <spdlog/details/registry.h>
|
||||||
|
#include <spdlog/details/thread_pool.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
static const size_t default_async_q_size = 8192;
|
||||||
|
}
|
||||||
|
|
||||||
|
// async logger factory - creates async loggers backed with thread pool.
|
||||||
|
// if a global thread pool doesn't already exist, create it with default queue
|
||||||
|
// size of 8192 items and single thread.
|
||||||
|
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
||||||
|
struct async_factory_impl
|
||||||
|
{
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
||||||
|
{
|
||||||
|
auto ®istry_inst = details::registry::instance();
|
||||||
|
|
||||||
|
// create global thread pool if not already exists..
|
||||||
|
|
||||||
|
auto &mutex = registry_inst.tp_mutex();
|
||||||
|
std::lock_guard<std::recursive_mutex> tp_lock(mutex);
|
||||||
|
auto tp = registry_inst.get_tp();
|
||||||
|
if (tp == nullptr)
|
||||||
|
{
|
||||||
|
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1);
|
||||||
|
registry_inst.set_tp(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||||
|
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
||||||
|
registry_inst.initialize_logger(new_logger);
|
||||||
|
return new_logger;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using async_factory = async_factory_impl<async_overflow_policy::block>;
|
||||||
|
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||||
|
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
|
{
|
||||||
|
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
|
{
|
||||||
|
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set global thread pool.
|
||||||
|
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
|
||||||
|
{
|
||||||
|
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start);
|
||||||
|
details::registry::instance().set_tp(std::move(tp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// set global thread pool.
|
||||||
|
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
||||||
|
{
|
||||||
|
init_thread_pool(q_size, thread_count, [] {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the global thread pool.
|
||||||
|
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
|
||||||
|
{
|
||||||
|
return details::registry::instance().get_tp();
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/async_logger.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/sinks/sink.h>
|
||||||
|
#include <spdlog/details/thread_pool.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
||||||
|
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
|
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
||||||
|
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
|
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// send the log message to the thread pool
|
||||||
|
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
if (auto pool_ptr = thread_pool_.lock())
|
||||||
|
{
|
||||||
|
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send flush request to the thread pool
|
||||||
|
SPDLOG_INLINE void spdlog::async_logger::flush_()
|
||||||
|
{
|
||||||
|
if (auto pool_ptr = thread_pool_.lock())
|
||||||
|
{
|
||||||
|
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// backend functions - called from the thread pool to do the actual job
|
||||||
|
//
|
||||||
|
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
if (sink->should_log(msg.level))
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
sink->log(msg);
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_flush_(msg))
|
||||||
|
{
|
||||||
|
backend_flush_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
|
||||||
|
{
|
||||||
|
auto cloned = std::make_shared<spdlog::async_logger>(*this);
|
||||||
|
cloned->name_ = std::move(new_name);
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
68
f4mp_originalcode/F4MPClient/include/spdlog/async_logger.h
Normal file
68
f4mp_originalcode/F4MPClient/include/spdlog/async_logger.h
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Fast asynchronous logger.
|
||||||
|
// Uses pre allocated queue.
|
||||||
|
// Creates a single back thread to pop messages from the queue and log them.
|
||||||
|
//
|
||||||
|
// Upon each log write the logger:
|
||||||
|
// 1. Checks if its log level is enough to log the message
|
||||||
|
// 2. Push a new copy of the message to a queue (or block the caller until
|
||||||
|
// space is available in the queue)
|
||||||
|
// Upon destruction, logs all remaining messages in the queue before
|
||||||
|
// destructing..
|
||||||
|
|
||||||
|
#include <spdlog/logger.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
// Async overflow policy - block by default.
|
||||||
|
enum class async_overflow_policy
|
||||||
|
{
|
||||||
|
block, // Block until message can be enqueued
|
||||||
|
overrun_oldest // Discard oldest message in the queue if full when trying to
|
||||||
|
// add new item.
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
class thread_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
||||||
|
{
|
||||||
|
friend class details::thread_pool;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename It>
|
||||||
|
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
|
||||||
|
async_overflow_policy overflow_policy = async_overflow_policy::block)
|
||||||
|
: logger(std::move(logger_name), begin, end)
|
||||||
|
, thread_pool_(std::move(tp))
|
||||||
|
, overflow_policy_(overflow_policy)
|
||||||
|
{}
|
||||||
|
|
||||||
|
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
|
||||||
|
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||||
|
|
||||||
|
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
|
||||||
|
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||||
|
|
||||||
|
std::shared_ptr<logger> clone(std::string new_name) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override;
|
||||||
|
void flush_() override;
|
||||||
|
void backend_sink_it_(const details::log_msg &incoming_log_msg);
|
||||||
|
void backend_flush_();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::weak_ptr<details::thread_pool> thread_pool_;
|
||||||
|
async_overflow_policy overflow_policy_;
|
||||||
|
};
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "async_logger-inl.h"
|
||||||
|
#endif
|
||||||
45
f4mp_originalcode/F4MPClient/include/spdlog/cfg/argv.h
Normal file
45
f4mp_originalcode/F4MPClient/include/spdlog/cfg/argv.h
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <spdlog/cfg/helpers.h>
|
||||||
|
#include <spdlog/details/registry.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
|
||||||
|
//
|
||||||
|
// set all loggers to debug level:
|
||||||
|
// example.exe "SPDLOG_LEVEL=debug"
|
||||||
|
|
||||||
|
// set logger1 to trace level
|
||||||
|
// example.exe "SPDLOG_LEVEL=logger1=trace"
|
||||||
|
|
||||||
|
// turn off all logging except for logger1 and logger2:
|
||||||
|
// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace cfg {
|
||||||
|
|
||||||
|
// search for SPDLOG_LEVEL= in the args and use it to init the levels
|
||||||
|
void load_argv_levels(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
|
||||||
|
for (int i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
std::string arg = argv[i];
|
||||||
|
if (arg.find(spdlog_level_prefix) == 0)
|
||||||
|
{
|
||||||
|
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
||||||
|
auto levels = helpers::extract_levels(levels_string);
|
||||||
|
details::registry::instance().update_levels(std::move(levels));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_argv_levels(int argc, char **argv)
|
||||||
|
{
|
||||||
|
load_argv_levels(argc, const_cast<const char **>(argv));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cfg
|
||||||
|
} // namespace spdlog
|
||||||
36
f4mp_originalcode/F4MPClient/include/spdlog/cfg/env.h
Normal file
36
f4mp_originalcode/F4MPClient/include/spdlog/cfg/env.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <spdlog/cfg/helpers.h>
|
||||||
|
#include <spdlog/details/registry.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Init levels and patterns from env variables SPDLOG_LEVEL
|
||||||
|
// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
|
||||||
|
// Note - fallback to "info" level on unrecognized levels
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// set global level to debug:
|
||||||
|
// export SPDLOG_LEVEL=debug
|
||||||
|
//
|
||||||
|
// turn off all logging except for logger1:
|
||||||
|
// export SPDLOG_LEVEL="off,logger1=debug"
|
||||||
|
//
|
||||||
|
|
||||||
|
// turn off all logging except for logger1 and logger2:
|
||||||
|
// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace cfg {
|
||||||
|
void load_env_levels()
|
||||||
|
{
|
||||||
|
auto env_val = details::os::getenv("SPDLOG_LEVEL");
|
||||||
|
auto levels = helpers::extract_levels(env_val);
|
||||||
|
details::registry::instance().update_levels(std::move(levels));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cfg
|
||||||
|
} // namespace spdlog
|
||||||
103
f4mp_originalcode/F4MPClient/include/spdlog/cfg/helpers-inl.h
Normal file
103
f4mp_originalcode/F4MPClient/include/spdlog/cfg/helpers-inl.h
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/cfg/helpers.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
#include <spdlog/details/registry.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace cfg {
|
||||||
|
namespace helpers {
|
||||||
|
|
||||||
|
// inplace convert to lowercase
|
||||||
|
inline std::string &to_lower_(std::string &str)
|
||||||
|
{
|
||||||
|
std::transform(
|
||||||
|
str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inplace trim spaces
|
||||||
|
inline std::string &trim_(std::string &str)
|
||||||
|
{
|
||||||
|
const char *spaces = " \n\r\t";
|
||||||
|
str.erase(str.find_last_not_of(spaces) + 1);
|
||||||
|
str.erase(0, str.find_first_not_of(spaces));
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return (name,value) trimmed pair from given "name=value" string.
|
||||||
|
// return empty string on missing parts
|
||||||
|
// "key=val" => ("key", "val")
|
||||||
|
// " key = val " => ("key", "val")
|
||||||
|
// "key=" => ("key", "")
|
||||||
|
// "val" => ("", "val")
|
||||||
|
|
||||||
|
inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
|
||||||
|
{
|
||||||
|
auto n = str.find(sep);
|
||||||
|
std::string k, v;
|
||||||
|
if (n == std::string::npos)
|
||||||
|
{
|
||||||
|
v = str;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
k = str.substr(0, n);
|
||||||
|
v = str.substr(n + 1);
|
||||||
|
}
|
||||||
|
return std::make_pair(trim_(k), trim_(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
|
||||||
|
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
|
||||||
|
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
std::istringstream token_stream(str);
|
||||||
|
std::unordered_map<std::string, std::string> rv{};
|
||||||
|
while (std::getline(token_stream, token, ','))
|
||||||
|
{
|
||||||
|
if (token.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto kv = extract_kv_('=', token);
|
||||||
|
rv[kv.first] = kv.second;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_levels extract_levels(const std::string &input)
|
||||||
|
{
|
||||||
|
auto key_vals = extract_key_vals_(input);
|
||||||
|
log_levels rv;
|
||||||
|
|
||||||
|
for (auto &name_level : key_vals)
|
||||||
|
{
|
||||||
|
auto &logger_name = name_level.first;
|
||||||
|
auto level_name = to_lower_(name_level.second);
|
||||||
|
auto level = level::from_str(level_name);
|
||||||
|
// fallback to "info" if unrecognized level name
|
||||||
|
if (level == level::off && level_name != "off")
|
||||||
|
{
|
||||||
|
level = level::info;
|
||||||
|
}
|
||||||
|
rv.set(logger_name, level);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace helpers
|
||||||
|
} // namespace cfg
|
||||||
|
} // namespace spdlog
|
||||||
28
f4mp_originalcode/F4MPClient/include/spdlog/cfg/helpers.h
Normal file
28
f4mp_originalcode/F4MPClient/include/spdlog/cfg/helpers.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/cfg/log_levels.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace cfg {
|
||||||
|
namespace helpers {
|
||||||
|
//
|
||||||
|
// Init levels from given string
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// set global level to debug: "debug"
|
||||||
|
// turn off all logging except for logger1: "off,logger1=debug"
|
||||||
|
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
||||||
|
//
|
||||||
|
SPDLOG_API log_levels extract_levels(const std::string &txt);
|
||||||
|
} // namespace helpers
|
||||||
|
|
||||||
|
} // namespace cfg
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "helpers-inl.h"
|
||||||
|
#endif // SPDLOG_HEADER_ONLY
|
||||||
47
f4mp_originalcode/F4MPClient/include/spdlog/cfg/log_levels.h
Normal file
47
f4mp_originalcode/F4MPClient/include/spdlog/cfg/log_levels.h
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace cfg {
|
||||||
|
class log_levels
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, spdlog::level::level_enum> levels_;
|
||||||
|
spdlog::level::level_enum default_level_ = level::info;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set(const std::string &logger_name, level::level_enum lvl)
|
||||||
|
{
|
||||||
|
if (logger_name.empty())
|
||||||
|
{
|
||||||
|
default_level_ = lvl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
levels_[logger_name] = lvl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_default(level::level_enum lvl)
|
||||||
|
{
|
||||||
|
default_level_ = lvl;
|
||||||
|
}
|
||||||
|
|
||||||
|
level::level_enum get(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
auto it = levels_.find(logger_name);
|
||||||
|
return it != levels_.end() ? it->second : default_level_;
|
||||||
|
}
|
||||||
|
|
||||||
|
level::level_enum default_level()
|
||||||
|
{
|
||||||
|
return default_level_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace cfg
|
||||||
|
} // namespace spdlog
|
||||||
76
f4mp_originalcode/F4MPClient/include/spdlog/common-inl.h
Normal file
76
f4mp_originalcode/F4MPClient/include/spdlog/common-inl.h
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace level {
|
||||||
|
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
||||||
|
|
||||||
|
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
||||||
|
|
||||||
|
SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return level_string_views[l];
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return short_level_names[l];
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
int level = 0;
|
||||||
|
for (const auto &level_str : level_string_views)
|
||||||
|
{
|
||||||
|
if (level_str == name)
|
||||||
|
{
|
||||||
|
return static_cast<level::level_enum>(level);
|
||||||
|
}
|
||||||
|
level++;
|
||||||
|
}
|
||||||
|
// check also for "warn" and "err" before giving up..
|
||||||
|
if (name == "warn")
|
||||||
|
{
|
||||||
|
return level::warn;
|
||||||
|
}
|
||||||
|
if (name == "err")
|
||||||
|
{
|
||||||
|
return level::err;
|
||||||
|
}
|
||||||
|
return level::off;
|
||||||
|
}
|
||||||
|
} // namespace level
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
|
||||||
|
: msg_(std::move(msg))
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
|
||||||
|
{
|
||||||
|
memory_buf_t outbuf;
|
||||||
|
fmt::format_system_error(outbuf, last_errno, msg);
|
||||||
|
msg_ = fmt::to_string(outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return msg_.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex(msg, last_errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex(std::move(msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
246
f4mp_originalcode/F4MPClient/include/spdlog/common.h
Normal file
246
f4mp_originalcode/F4MPClient/include/spdlog/common.h
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/tweakme.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <memory>
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#ifdef SPDLOG_COMPILED_LIB
|
||||||
|
#undef SPDLOG_HEADER_ONLY
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
|
||||||
|
#ifdef spdlog_EXPORTS
|
||||||
|
#define SPDLOG_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_API __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB)
|
||||||
|
#define SPDLOG_API
|
||||||
|
#endif
|
||||||
|
#define SPDLOG_INLINE
|
||||||
|
#else // !defined(SPDLOG_COMPILED_LIB)
|
||||||
|
#define SPDLOG_API
|
||||||
|
#define SPDLOG_HEADER_ONLY
|
||||||
|
#define SPDLOG_INLINE inline
|
||||||
|
#endif // #ifdef SPDLOG_COMPILED_LIB
|
||||||
|
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
|
||||||
|
// visual studio upto 2013 does not support noexcept nor constexpr
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||||
|
#define SPDLOG_NOEXCEPT _NOEXCEPT
|
||||||
|
#define SPDLOG_CONSTEXPR
|
||||||
|
#else
|
||||||
|
#define SPDLOG_NOEXCEPT noexcept
|
||||||
|
#define SPDLOG_CONSTEXPR constexpr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define SPDLOG_DEPRECATED __declspec(deprecated)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// disable thread local on msvc 2013
|
||||||
|
#ifndef SPDLOG_NO_TLS
|
||||||
|
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
||||||
|
#define SPDLOG_NO_TLS 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPDLOG_FUNCTION
|
||||||
|
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPDLOG_NO_EXCEPTIONS
|
||||||
|
#define SPDLOG_TRY
|
||||||
|
#define SPDLOG_THROW(ex) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
printf("spdlog fatal error: %s\n", ex.what()); \
|
||||||
|
std::abort(); \
|
||||||
|
} while (0)
|
||||||
|
#define SPDLOG_CATCH_ALL()
|
||||||
|
#else
|
||||||
|
#define SPDLOG_TRY try
|
||||||
|
#define SPDLOG_THROW(ex) throw(ex)
|
||||||
|
#define SPDLOG_CATCH_ALL() catch (...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
class formatter;
|
||||||
|
|
||||||
|
namespace sinks {
|
||||||
|
class sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
using filename_t = std::wstring;
|
||||||
|
#define SPDLOG_FILENAME_T(s) L##s
|
||||||
|
#else
|
||||||
|
using filename_t = std::string;
|
||||||
|
#define SPDLOG_FILENAME_T(s) s
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using log_clock = std::chrono::system_clock;
|
||||||
|
using sink_ptr = std::shared_ptr<sinks::sink>;
|
||||||
|
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||||
|
using err_handler = std::function<void(const std::string &err_msg)>;
|
||||||
|
using string_view_t = fmt::basic_string_view<char>;
|
||||||
|
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
||||||
|
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
#ifndef _WIN32
|
||||||
|
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||||
|
#else
|
||||||
|
template<typename T>
|
||||||
|
struct is_convertible_to_wstring_view : std::is_convertible<T, wstring_view_t>
|
||||||
|
{};
|
||||||
|
#endif // _WIN32
|
||||||
|
#else
|
||||||
|
template<typename>
|
||||||
|
struct is_convertible_to_wstring_view : std::false_type
|
||||||
|
{};
|
||||||
|
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
|
||||||
|
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||||
|
using level_t = details::null_atomic_int;
|
||||||
|
#else
|
||||||
|
using level_t = std::atomic<int>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SPDLOG_LEVEL_TRACE 0
|
||||||
|
#define SPDLOG_LEVEL_DEBUG 1
|
||||||
|
#define SPDLOG_LEVEL_INFO 2
|
||||||
|
#define SPDLOG_LEVEL_WARN 3
|
||||||
|
#define SPDLOG_LEVEL_ERROR 4
|
||||||
|
#define SPDLOG_LEVEL_CRITICAL 5
|
||||||
|
#define SPDLOG_LEVEL_OFF 6
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
||||||
|
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Log level enum
|
||||||
|
namespace level {
|
||||||
|
enum level_enum
|
||||||
|
{
|
||||||
|
trace = SPDLOG_LEVEL_TRACE,
|
||||||
|
debug = SPDLOG_LEVEL_DEBUG,
|
||||||
|
info = SPDLOG_LEVEL_INFO,
|
||||||
|
warn = SPDLOG_LEVEL_WARN,
|
||||||
|
err = SPDLOG_LEVEL_ERROR,
|
||||||
|
critical = SPDLOG_LEVEL_CRITICAL,
|
||||||
|
off = SPDLOG_LEVEL_OFF,
|
||||||
|
n_levels
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||||
|
#define SPDLOG_LEVEL_NAMES \
|
||||||
|
{ \
|
||||||
|
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
|
||||||
|
|
||||||
|
#define SPDLOG_SHORT_LEVEL_NAMES \
|
||||||
|
{ \
|
||||||
|
"T", "D", "I", "W", "E", "C", "O" \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||||
|
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||||
|
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
using level_hasher = std::hash<int>;
|
||||||
|
} // namespace level
|
||||||
|
|
||||||
|
//
|
||||||
|
// Color mode used by sinks with color support.
|
||||||
|
//
|
||||||
|
enum class color_mode
|
||||||
|
{
|
||||||
|
always,
|
||||||
|
automatic,
|
||||||
|
never
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pattern time - specific time getting to use for pattern_formatter.
|
||||||
|
// local time by default
|
||||||
|
//
|
||||||
|
enum class pattern_time_type
|
||||||
|
{
|
||||||
|
local, // log localtime
|
||||||
|
utc // log utc
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Log exception
|
||||||
|
//
|
||||||
|
class SPDLOG_API spdlog_ex : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit spdlog_ex(std::string msg);
|
||||||
|
spdlog_ex(const std::string &msg, int last_errno);
|
||||||
|
const char *what() const SPDLOG_NOEXCEPT override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string msg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
||||||
|
SPDLOG_API void throw_spdlog_ex(std::string msg);
|
||||||
|
|
||||||
|
struct source_loc
|
||||||
|
{
|
||||||
|
SPDLOG_CONSTEXPR source_loc() = default;
|
||||||
|
SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
|
||||||
|
: filename{filename_in}
|
||||||
|
, line{line_in}
|
||||||
|
, funcname{funcname_in}
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return line == 0;
|
||||||
|
}
|
||||||
|
const char *filename{nullptr};
|
||||||
|
int line{0};
|
||||||
|
const char *funcname{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
// make_unique support for pre c++14
|
||||||
|
|
||||||
|
#if __cplusplus >= 201402L // C++14 and beyond
|
||||||
|
using std::make_unique;
|
||||||
|
#else
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
std::unique_ptr<T> make_unique(Args &&... args)
|
||||||
|
{
|
||||||
|
static_assert(!std::is_array<T>::value, "arrays not supported");
|
||||||
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "common-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/details/backtracer.h>
|
||||||
|
#endif
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||||
|
enabled_ = other.enabled();
|
||||||
|
messages_ = other.messages_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||||
|
enabled_ = other.enabled();
|
||||||
|
messages_ = std::move(other.messages_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
enabled_ = other.enabled();
|
||||||
|
messages_ = std::move(other.messages_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void backtracer::enable(size_t size)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
enabled_.store(true, std::memory_order_relaxed);
|
||||||
|
messages_ = circular_q<log_msg_buffer>{size};
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void backtracer::disable()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
enabled_.store(false, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE bool backtracer::enabled() const
|
||||||
|
{
|
||||||
|
return enabled_.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
messages_.push_back(log_msg_buffer{msg});
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop all items in the q and apply the given fun on each of them.
|
||||||
|
SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
while (!messages_.empty())
|
||||||
|
{
|
||||||
|
auto &front_msg = messages_.front();
|
||||||
|
fun(front_msg);
|
||||||
|
messages_.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/log_msg_buffer.h>
|
||||||
|
#include <spdlog/details/circular_q.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
// Store log messages in circular buffer.
|
||||||
|
// Useful for storing debug data in case of error/warning happens.
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
class SPDLOG_API backtracer
|
||||||
|
{
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
std::atomic<bool> enabled_{false};
|
||||||
|
circular_q<log_msg_buffer> messages_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
backtracer() = default;
|
||||||
|
backtracer(const backtracer &other);
|
||||||
|
|
||||||
|
backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
|
||||||
|
backtracer &operator=(backtracer other);
|
||||||
|
|
||||||
|
void enable(size_t size);
|
||||||
|
void disable();
|
||||||
|
bool enabled() const;
|
||||||
|
void push_back(const log_msg &msg);
|
||||||
|
|
||||||
|
// pop all items in the q and apply the given fun on each of them.
|
||||||
|
void foreach_pop(std::function<void(const details::log_msg &)> fun);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "backtracer-inl.h"
|
||||||
|
#endif
|
||||||
141
f4mp_originalcode/F4MPClient/include/spdlog/details/circular_q.h
Normal file
141
f4mp_originalcode/F4MPClient/include/spdlog/details/circular_q.h
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
// circular q view of std::vector.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
template<typename T>
|
||||||
|
class circular_q
|
||||||
|
{
|
||||||
|
size_t max_items_ = 0;
|
||||||
|
typename std::vector<T>::size_type head_ = 0;
|
||||||
|
typename std::vector<T>::size_type tail_ = 0;
|
||||||
|
size_t overrun_counter_ = 0;
|
||||||
|
std::vector<T> v_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
// empty ctor - create a disabled queue with no elements allocated at all
|
||||||
|
circular_q() = default;
|
||||||
|
|
||||||
|
explicit circular_q(size_t max_items)
|
||||||
|
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
||||||
|
, v_(max_items_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
circular_q(const circular_q &) = default;
|
||||||
|
circular_q &operator=(const circular_q &) = default;
|
||||||
|
|
||||||
|
// move cannot be default,
|
||||||
|
// since we need to reset head_, tail_, etc to zero in the moved object
|
||||||
|
circular_q(circular_q &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
copy_moveable(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
copy_moveable(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// push back, overrun (oldest) item if no room left
|
||||||
|
void push_back(T &&item)
|
||||||
|
{
|
||||||
|
if (max_items_ > 0)
|
||||||
|
{
|
||||||
|
v_[tail_] = std::move(item);
|
||||||
|
tail_ = (tail_ + 1) % max_items_;
|
||||||
|
|
||||||
|
if (tail_ == head_) // overrun last item if full
|
||||||
|
{
|
||||||
|
head_ = (head_ + 1) % max_items_;
|
||||||
|
++overrun_counter_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return reference to the front item.
|
||||||
|
// If there are no elements in the container, the behavior is undefined.
|
||||||
|
const T &front() const
|
||||||
|
{
|
||||||
|
return v_[head_];
|
||||||
|
}
|
||||||
|
|
||||||
|
T &front()
|
||||||
|
{
|
||||||
|
return v_[head_];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return number of elements actually stored
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
if (tail_ >= head_)
|
||||||
|
{
|
||||||
|
return tail_ - head_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return max_items_ - (head_ - tail_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return const reference to item by index.
|
||||||
|
// If index is out of range 0…size()-1, the behavior is undefined.
|
||||||
|
const T &at(size_t i) const
|
||||||
|
{
|
||||||
|
assert(i < size());
|
||||||
|
return v_[(head_ + i) % max_items_];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop item from front.
|
||||||
|
// If there are no elements in the container, the behavior is undefined.
|
||||||
|
void pop_front()
|
||||||
|
{
|
||||||
|
head_ = (head_ + 1) % max_items_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return tail_ == head_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool full() const
|
||||||
|
{
|
||||||
|
// head is ahead of the tail by 1
|
||||||
|
if (max_items_ > 0)
|
||||||
|
{
|
||||||
|
return ((tail_ + 1) % max_items_) == head_;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t overrun_counter() const
|
||||||
|
{
|
||||||
|
return overrun_counter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// copy from other&& and reset it to disabled state
|
||||||
|
void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
max_items_ = other.max_items_;
|
||||||
|
head_ = other.head_;
|
||||||
|
tail_ = other.tail_;
|
||||||
|
overrun_counter_ = other.overrun_counter_;
|
||||||
|
v_ = std::move(other.v_);
|
||||||
|
|
||||||
|
// put &&other in disabled, but valid state
|
||||||
|
other.max_items_ = 0;
|
||||||
|
other.head_ = other.tail_ = 0;
|
||||||
|
other.overrun_counter_ = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
struct console_mutex
|
||||||
|
{
|
||||||
|
using mutex_t = std::mutex;
|
||||||
|
static mutex_t &mutex()
|
||||||
|
{
|
||||||
|
static mutex_t s_mutex;
|
||||||
|
return s_mutex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct console_nullmutex
|
||||||
|
{
|
||||||
|
using mutex_t = null_mutex;
|
||||||
|
static mutex_t &mutex()
|
||||||
|
{
|
||||||
|
static mutex_t s_mutex;
|
||||||
|
return s_mutex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/details/file_helper.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE file_helper::~file_helper()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
filename_ = fname;
|
||||||
|
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
||||||
|
|
||||||
|
for (int tries = 0; tries < open_tries_; ++tries)
|
||||||
|
{
|
||||||
|
// create containing folder if not exists already.
|
||||||
|
os::create_dir(os::dir_name(fname));
|
||||||
|
if (!os::fopen_s(&fd_, fname, mode))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
details::os::sleep_for_millis(open_interval_);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::reopen(bool truncate)
|
||||||
|
{
|
||||||
|
if (filename_.empty())
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("Failed re opening file - was not opened before");
|
||||||
|
}
|
||||||
|
this->open(filename_, truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::flush()
|
||||||
|
{
|
||||||
|
std::fflush(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::close()
|
||||||
|
{
|
||||||
|
if (fd_ != nullptr)
|
||||||
|
{
|
||||||
|
std::fclose(fd_);
|
||||||
|
fd_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
|
||||||
|
{
|
||||||
|
size_t msg_size = buf.size();
|
||||||
|
auto data = buf.data();
|
||||||
|
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE size_t file_helper::size() const
|
||||||
|
{
|
||||||
|
if (fd_ == nullptr)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
|
||||||
|
}
|
||||||
|
return os::filesize(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE const filename_t &file_helper::filename() const
|
||||||
|
{
|
||||||
|
return filename_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// return file path and its extension:
|
||||||
|
//
|
||||||
|
// "mylog.txt" => ("mylog", ".txt")
|
||||||
|
// "mylog" => ("mylog", "")
|
||||||
|
// "mylog." => ("mylog.", "")
|
||||||
|
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||||
|
//
|
||||||
|
// the starting dot in filenames is ignored (hidden files):
|
||||||
|
//
|
||||||
|
// ".mylog" => (".mylog". "")
|
||||||
|
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||||
|
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||||
|
SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
|
||||||
|
{
|
||||||
|
auto ext_index = fname.rfind('.');
|
||||||
|
|
||||||
|
// no valid extension found - return whole path and empty string as
|
||||||
|
// extension
|
||||||
|
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
||||||
|
{
|
||||||
|
return std::make_tuple(fname, filename_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||||
|
auto folder_index = fname.rfind(details::os::folder_sep);
|
||||||
|
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
||||||
|
{
|
||||||
|
return std::make_tuple(fname, filename_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally - return a valid base and extension tuple
|
||||||
|
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
// Helper class for file sinks.
|
||||||
|
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
||||||
|
// Throw spdlog_ex exception on errors.
|
||||||
|
|
||||||
|
class SPDLOG_API file_helper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit file_helper() = default;
|
||||||
|
|
||||||
|
file_helper(const file_helper &) = delete;
|
||||||
|
file_helper &operator=(const file_helper &) = delete;
|
||||||
|
~file_helper();
|
||||||
|
|
||||||
|
void open(const filename_t &fname, bool truncate = false);
|
||||||
|
void reopen(bool truncate);
|
||||||
|
void flush();
|
||||||
|
void close();
|
||||||
|
void write(const memory_buf_t &buf);
|
||||||
|
size_t size() const;
|
||||||
|
const filename_t &filename() const;
|
||||||
|
|
||||||
|
//
|
||||||
|
// return file path and its extension:
|
||||||
|
//
|
||||||
|
// "mylog.txt" => ("mylog", ".txt")
|
||||||
|
// "mylog" => ("mylog", "")
|
||||||
|
// "mylog." => ("mylog.", "")
|
||||||
|
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||||
|
//
|
||||||
|
// the starting dot in filenames is ignored (hidden files):
|
||||||
|
//
|
||||||
|
// ".mylog" => (".mylog". "")
|
||||||
|
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||||
|
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||||
|
static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int open_tries_ = 5;
|
||||||
|
const int open_interval_ = 10;
|
||||||
|
std::FILE *fd_{nullptr};
|
||||||
|
filename_t filename_;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "file_helper-inl.h"
|
||||||
|
#endif
|
||||||
116
f4mp_originalcode/F4MPClient/include/spdlog/details/fmt_helper.h
Normal file
116
f4mp_originalcode/F4MPClient/include/spdlog/details/fmt_helper.h
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
|
||||||
|
// Some fmt helpers to efficiently format and pad ints and strings
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
namespace fmt_helper {
|
||||||
|
|
||||||
|
inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return spdlog::string_view_t{buf.data(), buf.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
|
||||||
|
{
|
||||||
|
auto *buf_ptr = view.data();
|
||||||
|
dest.append(buf_ptr, buf_ptr + view.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void append_int(T n, memory_buf_t &dest)
|
||||||
|
{
|
||||||
|
fmt::format_int i(n);
|
||||||
|
dest.append(i.data(), i.data() + i.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline unsigned int count_digits(T n)
|
||||||
|
{
|
||||||
|
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||||
|
return static_cast<unsigned int>(fmt::
|
||||||
|
// fmt 7.0.0 renamed the internal namespace to detail.
|
||||||
|
// See: https://github.com/fmtlib/fmt/issues/1538
|
||||||
|
#if FMT_VERSION < 70000
|
||||||
|
internal
|
||||||
|
#else
|
||||||
|
detail
|
||||||
|
#endif
|
||||||
|
::count_digits(static_cast<count_type>(n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void pad2(int n, memory_buf_t &dest)
|
||||||
|
{
|
||||||
|
if (n >= 0 && n < 100) // 0-99
|
||||||
|
{
|
||||||
|
dest.push_back(static_cast<char>('0' + n / 10));
|
||||||
|
dest.push_back(static_cast<char>('0' + n % 10));
|
||||||
|
}
|
||||||
|
else // unlikely, but just in case, let fmt deal with it
|
||||||
|
{
|
||||||
|
fmt::format_to(dest, "{:02}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
|
||||||
|
{
|
||||||
|
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
||||||
|
for (auto digits = count_digits(n); digits < width; digits++)
|
||||||
|
{
|
||||||
|
dest.push_back('0');
|
||||||
|
}
|
||||||
|
append_int(n, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void pad3(T n, memory_buf_t &dest)
|
||||||
|
{
|
||||||
|
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
|
||||||
|
if (n < 1000)
|
||||||
|
{
|
||||||
|
dest.push_back(static_cast<char>(n / 100 + '0'));
|
||||||
|
n = n % 100;
|
||||||
|
dest.push_back(static_cast<char>((n / 10) + '0'));
|
||||||
|
dest.push_back(static_cast<char>((n % 10) + '0'));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
append_int(n, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void pad6(T n, memory_buf_t &dest)
|
||||||
|
{
|
||||||
|
pad_uint(n, 6, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void pad9(T n, memory_buf_t &dest)
|
||||||
|
{
|
||||||
|
pad_uint(n, 9, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return fraction of a second of the given time_point.
|
||||||
|
// e.g.
|
||||||
|
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
||||||
|
template<typename ToDuration>
|
||||||
|
inline ToDuration time_fraction(log_clock::time_point tp)
|
||||||
|
{
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
using std::chrono::seconds;
|
||||||
|
auto duration = tp.time_since_epoch();
|
||||||
|
auto secs = duration_cast<seconds>(duration);
|
||||||
|
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fmt_helper
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, spdlog::source_loc loc, string_view_t a_logger_name,
|
||||||
|
spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
||||||
|
: logger_name(a_logger_name)
|
||||||
|
, level(lvl)
|
||||||
|
, time(log_time)
|
||||||
|
#ifndef SPDLOG_NO_THREAD_ID
|
||||||
|
, thread_id(os::thread_id())
|
||||||
|
#endif
|
||||||
|
, source(loc)
|
||||||
|
, payload(msg)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg::log_msg(
|
||||||
|
spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
||||||
|
: log_msg(os::now(), loc, a_logger_name, lvl, msg)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
||||||
|
: log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
|
||||||
|
{}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
struct SPDLOG_API log_msg
|
||||||
|
{
|
||||||
|
log_msg() = default;
|
||||||
|
log_msg(log_clock::time_point log_time, source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||||
|
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||||
|
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||||
|
log_msg(const log_msg &other) = default;
|
||||||
|
|
||||||
|
string_view_t logger_name;
|
||||||
|
level::level_enum level{level::off};
|
||||||
|
log_clock::time_point time;
|
||||||
|
size_t thread_id{0};
|
||||||
|
|
||||||
|
// wrapping the formatted text with color (updated by pattern_formatter).
|
||||||
|
mutable size_t color_range_start{0};
|
||||||
|
mutable size_t color_range_end{0};
|
||||||
|
|
||||||
|
source_loc source;
|
||||||
|
string_view_t payload;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "log_msg-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/details/log_msg_buffer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
|
||||||
|
: log_msg{orig_msg}
|
||||||
|
{
|
||||||
|
buffer.append(logger_name.begin(), logger_name.end());
|
||||||
|
buffer.append(payload.begin(), payload.end());
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
|
||||||
|
: log_msg{other}
|
||||||
|
{
|
||||||
|
buffer.append(logger_name.begin(), logger_name.end());
|
||||||
|
buffer.append(payload.begin(), payload.end());
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)}
|
||||||
|
{
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
|
||||||
|
{
|
||||||
|
log_msg::operator=(other);
|
||||||
|
buffer.clear();
|
||||||
|
buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
|
||||||
|
update_string_views();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
log_msg::operator=(other);
|
||||||
|
buffer = std::move(other.buffer);
|
||||||
|
update_string_views();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void log_msg_buffer::update_string_views()
|
||||||
|
{
|
||||||
|
logger_name = string_view_t{buffer.data(), logger_name.size()};
|
||||||
|
payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
// Extend log_msg with internal buffer to store its payload.
|
||||||
|
// This is needed since log_msg holds string_views that points to stack data.
|
||||||
|
|
||||||
|
class SPDLOG_API log_msg_buffer : public log_msg
|
||||||
|
{
|
||||||
|
memory_buf_t buffer;
|
||||||
|
void update_string_views();
|
||||||
|
|
||||||
|
public:
|
||||||
|
log_msg_buffer() = default;
|
||||||
|
explicit log_msg_buffer(const log_msg &orig_msg);
|
||||||
|
log_msg_buffer(const log_msg_buffer &other);
|
||||||
|
log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
||||||
|
log_msg_buffer &operator=(const log_msg_buffer &other);
|
||||||
|
log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "log_msg_buffer-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// multi producer-multi consumer blocking queue.
|
||||||
|
// enqueue(..) - will block until room found to put the new message.
|
||||||
|
// enqueue_nowait(..) - will return immediately with false if no room left in
|
||||||
|
// the queue.
|
||||||
|
// dequeue_for(..) - will block until the queue is not empty or timeout have
|
||||||
|
// passed.
|
||||||
|
|
||||||
|
#include <spdlog/details/circular_q.h>
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class mpmc_blocking_queue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item_type = T;
|
||||||
|
explicit mpmc_blocking_queue(size_t max_items)
|
||||||
|
: q_(max_items)
|
||||||
|
{}
|
||||||
|
|
||||||
|
#ifndef __MINGW32__
|
||||||
|
// try to enqueue and block if no room left
|
||||||
|
void enqueue(T &&item)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||||
|
q_.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
push_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enqueue immediately. overrun oldest message in the queue if no room left.
|
||||||
|
void enqueue_nowait(T &&item)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
q_.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
push_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to dequeue item. if no item found. wait upto timeout and try again
|
||||||
|
// Return true, if succeeded dequeue item, false otherwise
|
||||||
|
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
popped_item = std::move(q_.front());
|
||||||
|
q_.pop_front();
|
||||||
|
}
|
||||||
|
pop_cv_.notify_one();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
||||||
|
// so release the mutex at the very end each function.
|
||||||
|
|
||||||
|
// try to enqueue and block if no room left
|
||||||
|
void enqueue(T &&item)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||||
|
q_.push_back(std::move(item));
|
||||||
|
push_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enqueue immediately. overrun oldest message in the queue if no room left.
|
||||||
|
void enqueue_nowait(T &&item)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
q_.push_back(std::move(item));
|
||||||
|
push_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to dequeue item. if no item found. wait upto timeout and try again
|
||||||
|
// Return true, if succeeded dequeue item, false otherwise
|
||||||
|
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
popped_item = std::move(q_.front());
|
||||||
|
q_.pop_front();
|
||||||
|
pop_cv_.notify_one();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t overrun_counter()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
return q_.overrun_counter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex queue_mutex_;
|
||||||
|
std::condition_variable push_cv_;
|
||||||
|
std::condition_variable pop_cv_;
|
||||||
|
spdlog::details::circular_q<T> q_;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <utility>
|
||||||
|
// null, no cost dummy "mutex" and dummy "atomic" int
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
struct null_mutex
|
||||||
|
{
|
||||||
|
void lock() const {}
|
||||||
|
void unlock() const {}
|
||||||
|
bool try_lock() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct null_atomic_int
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
null_atomic_int() = default;
|
||||||
|
|
||||||
|
explicit null_atomic_int(int new_value)
|
||||||
|
: value(new_value)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int load(std::memory_order = std::memory_order_relaxed) const
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void store(int new_value, std::memory_order = std::memory_order_relaxed)
|
||||||
|
{
|
||||||
|
value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
|
||||||
|
{
|
||||||
|
std::swap(new_value, value);
|
||||||
|
return new_value; // return value before the call
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
554
f4mp_originalcode/F4MPClient/include/spdlog/details/os-inl.h
Normal file
554
f4mp_originalcode/F4MPClient/include/spdlog/details/os-inl.h
Normal file
|
|
@ -0,0 +1,554 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <ctime>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <array>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <io.h> // _get_osfhandle and _isatty support
|
||||||
|
#include <process.h> // _get_pid support
|
||||||
|
#include <spdlog/details/windows_include.h>
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#include <share.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
#include <limits>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <direct.h> // for _mkdir/_wmkdir
|
||||||
|
|
||||||
|
#else // unix
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||||
|
|
||||||
|
#elif defined(_AIX)
|
||||||
|
#include <pthread.h> // for pthread_getthreadid_np
|
||||||
|
|
||||||
|
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
#include <pthread_np.h> // for pthread_getthreadid_np
|
||||||
|
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
#include <lwp.h> // for _lwp_self
|
||||||
|
|
||||||
|
#elif defined(__sun)
|
||||||
|
#include <thread.h> // for thr_self
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // unix
|
||||||
|
|
||||||
|
#ifndef __has_feature // Clang - feature checking macros.
|
||||||
|
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
namespace os {
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
||||||
|
timespec ts;
|
||||||
|
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
||||||
|
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
||||||
|
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
||||||
|
|
||||||
|
#else
|
||||||
|
return log_clock::now();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::tm tm;
|
||||||
|
::localtime_s(&tm, &time_tt);
|
||||||
|
#else
|
||||||
|
std::tm tm;
|
||||||
|
::localtime_r(&time_tt, &tm);
|
||||||
|
#endif
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::time_t now_t = ::time(nullptr);
|
||||||
|
return localtime(now_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::tm tm;
|
||||||
|
::gmtime_s(&tm, &time_tt);
|
||||||
|
#else
|
||||||
|
std::tm tm;
|
||||||
|
::gmtime_r(&time_tt, &tm);
|
||||||
|
#endif
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::time_t now_t = ::time(nullptr);
|
||||||
|
return gmtime(now_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fopen_s on non windows for writing
|
||||||
|
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||||
|
#else
|
||||||
|
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||||
|
#endif
|
||||||
|
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||||
|
if (*fp != nullptr)
|
||||||
|
{
|
||||||
|
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
|
||||||
|
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||||
|
{
|
||||||
|
::fclose(*fp);
|
||||||
|
*fp = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#else // unix
|
||||||
|
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||||
|
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
|
||||||
|
const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*fp = ::fdopen(fd, mode.c_str());
|
||||||
|
if (*fp == nullptr)
|
||||||
|
{
|
||||||
|
::close(fd);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
*fp = ::fopen((filename.c_str()), mode.c_str());
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return *fp == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
return ::_wremove(filename.c_str());
|
||||||
|
#else
|
||||||
|
return std::remove(filename.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return path_exists(filename) ? remove(filename) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
return ::_wrename(filename1.c_str(), filename2.c_str());
|
||||||
|
#else
|
||||||
|
return std::rename(filename1.c_str(), filename2.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if path exists (file or directory)
|
||||||
|
SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
auto attribs = ::GetFileAttributesW(filename.c_str());
|
||||||
|
#else
|
||||||
|
auto attribs = ::GetFileAttributesA(filename.c_str());
|
||||||
|
#endif
|
||||||
|
return attribs != INVALID_FILE_ATTRIBUTES;
|
||||||
|
#else // common linux/unix all have the stat system call
|
||||||
|
struct stat buffer;
|
||||||
|
return (::stat(filename.c_str(), &buffer) == 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return file size according to open FILE* object
|
||||||
|
SPDLOG_INLINE size_t filesize(FILE *f)
|
||||||
|
{
|
||||||
|
if (f == nullptr)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("Failed getting file size. fd is null");
|
||||||
|
}
|
||||||
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
|
int fd = ::_fileno(f);
|
||||||
|
#if _WIN64 // 64 bits
|
||||||
|
__int64 ret = ::_filelengthi64(fd);
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // windows 32 bits
|
||||||
|
long ret = ::_filelength(fd);
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else // unix
|
||||||
|
// OpenBSD doesn't compile with :: before the fileno(..)
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
int fd = fileno(f);
|
||||||
|
#else
|
||||||
|
int fd = ::fileno(f);
|
||||||
|
#endif
|
||||||
|
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
||||||
|
#if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
|
||||||
|
struct stat64 st;
|
||||||
|
if (::fstat64(fd, &st) == 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(st.st_size);
|
||||||
|
}
|
||||||
|
#else // other unix or linux 32 bits or cygwin
|
||||||
|
struct stat st;
|
||||||
|
if (::fstat(fd, &st) == 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(st.st_size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
throw_spdlog_ex("Failed getting file size from fd", errno);
|
||||||
|
return 0; // will not be reached.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||||
|
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
||||||
|
TIME_ZONE_INFORMATION tzinfo;
|
||||||
|
auto rv = ::GetTimeZoneInformation(&tzinfo);
|
||||||
|
#else
|
||||||
|
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
||||||
|
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
|
||||||
|
#endif
|
||||||
|
if (rv == TIME_ZONE_ID_INVALID)
|
||||||
|
throw_spdlog_ex("Failed getting timezone info. ", errno);
|
||||||
|
|
||||||
|
int offset = -tzinfo.Bias;
|
||||||
|
if (tm.tm_isdst)
|
||||||
|
{
|
||||||
|
offset -= tzinfo.DaylightBias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset -= tzinfo.StandardBias;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
|
||||||
|
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
||||||
|
struct helper
|
||||||
|
{
|
||||||
|
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
|
||||||
|
{
|
||||||
|
int local_year = localtm.tm_year + (1900 - 1);
|
||||||
|
int gmt_year = gmtm.tm_year + (1900 - 1);
|
||||||
|
|
||||||
|
long int days = (
|
||||||
|
// difference in day of year
|
||||||
|
localtm.tm_yday -
|
||||||
|
gmtm.tm_yday
|
||||||
|
|
||||||
|
// + intervening leap days
|
||||||
|
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
|
||||||
|
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
||||||
|
|
||||||
|
// + difference in years * 365 */
|
||||||
|
+ (long int)(local_year - gmt_year) * 365);
|
||||||
|
|
||||||
|
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
||||||
|
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
||||||
|
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
||||||
|
|
||||||
|
return secs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
||||||
|
#else
|
||||||
|
auto offset_seconds = tm.tm_gmtoff;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return static_cast<int>(offset_seconds / 60);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return current thread id as size_t
|
||||||
|
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||||
|
// under VS 2013)
|
||||||
|
SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return static_cast<size_t>(::GetCurrentThreadId());
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||||
|
#define SYS_gettid __NR_gettid
|
||||||
|
#endif
|
||||||
|
return static_cast<size_t>(::syscall(SYS_gettid));
|
||||||
|
#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
return static_cast<size_t>(::pthread_getthreadid_np());
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
return static_cast<size_t>(::_lwp_self());
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
return static_cast<size_t>(::getthrid());
|
||||||
|
#elif defined(__sun)
|
||||||
|
return static_cast<size_t>(::thr_self());
|
||||||
|
#elif __APPLE__
|
||||||
|
uint64_t tid;
|
||||||
|
pthread_threadid_np(nullptr, &tid);
|
||||||
|
return static_cast<size_t>(tid);
|
||||||
|
#else // Default to standard C++11 (other Unix)
|
||||||
|
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return current thread id as size_t (from thread local storage)
|
||||||
|
SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(SPDLOG_NO_TLS)
|
||||||
|
return _thread_id();
|
||||||
|
#else // cache thread id in tls
|
||||||
|
static thread_local const size_t tid = _thread_id();
|
||||||
|
return tid;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||||
|
// See https://github.com/gabime/spdlog/issues/609
|
||||||
|
SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
::Sleep(milliseconds);
|
||||||
|
#else
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
||||||
|
{
|
||||||
|
memory_buf_t buf;
|
||||||
|
wstr_to_utf8buf(filename, buf);
|
||||||
|
return fmt::to_string(buf);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
return static_cast<int>(::GetCurrentProcessId());
|
||||||
|
#else
|
||||||
|
return static_cast<int>(::getpid());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the terminal supports colors
|
||||||
|
// Based on: https://github.com/agauniyal/rang/
|
||||||
|
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
static constexpr std::array<const char *, 14> terms = {
|
||||||
|
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
|
||||||
|
|
||||||
|
const char *env_p = std::getenv("TERM");
|
||||||
|
if (env_p == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const bool result =
|
||||||
|
std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the terminal attached
|
||||||
|
// Source: https://github.com/agauniyal/rang/
|
||||||
|
SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
return ::_isatty(_fileno(file)) != 0;
|
||||||
|
#else
|
||||||
|
return ::isatty(fileno(file)) != 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
|
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||||
|
{
|
||||||
|
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()))
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
int wstr_size = static_cast<int>(wstr.size());
|
||||||
|
if (wstr_size == 0)
|
||||||
|
{
|
||||||
|
target.resize(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result_size = static_cast<int>(target.capacity());
|
||||||
|
if ((wstr_size + 1) * 2 > result_size)
|
||||||
|
{
|
||||||
|
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result_size > 0)
|
||||||
|
{
|
||||||
|
target.resize(result_size);
|
||||||
|
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
|
||||||
|
|
||||||
|
if (result_size > 0)
|
||||||
|
{
|
||||||
|
target.resize(result_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
||||||
|
}
|
||||||
|
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
|
|
||||||
|
// return true on success
|
||||||
|
static SPDLOG_INLINE bool mkdir_(const filename_t &path)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
return ::_wmkdir(path.c_str()) == 0;
|
||||||
|
#else
|
||||||
|
return ::_mkdir(path.c_str()) == 0;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
return ::mkdir(path.c_str(), mode_t(0755)) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the given directory - and all directories leading to it
|
||||||
|
// return true on success or if the directory already exists
|
||||||
|
SPDLOG_INLINE bool create_dir(filename_t path)
|
||||||
|
{
|
||||||
|
if (path_exists(path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// support forward slash in windows
|
||||||
|
std::replace(path.begin(), path.end(), '/', folder_sep);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t search_offset = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
auto token_pos = path.find(folder_sep, search_offset);
|
||||||
|
// treat the entire path as a folder if no folder separator not found
|
||||||
|
if (token_pos == filename_t::npos)
|
||||||
|
{
|
||||||
|
token_pos = path.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto subdir = path.substr(0, token_pos);
|
||||||
|
|
||||||
|
if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir))
|
||||||
|
{
|
||||||
|
return false; // return error if failed creating dir
|
||||||
|
}
|
||||||
|
search_offset = token_pos + 1;
|
||||||
|
} while (search_offset < path.size());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return directory name from given path or empty string
|
||||||
|
// "abc/file" => "abc"
|
||||||
|
// "abc/" => "abc"
|
||||||
|
// "abc" => ""
|
||||||
|
// "abc///" => "abc//"
|
||||||
|
SPDLOG_INLINE filename_t dir_name(filename_t path)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
// support forward slash in windows
|
||||||
|
std::replace(path.begin(), path.end(), '/', folder_sep);
|
||||||
|
#endif
|
||||||
|
auto pos = path.find_last_of(folder_sep);
|
||||||
|
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SPDLOG_INLINE getenv(const char *field)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#if defined(__cplusplus_winrt)
|
||||||
|
return std::string{}; // not supported under uwp
|
||||||
|
#else
|
||||||
|
size_t len = 0;
|
||||||
|
char buf[128];
|
||||||
|
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
|
||||||
|
return ok ? buf : std::string{};
|
||||||
|
#endif
|
||||||
|
#else // revert to getenv
|
||||||
|
char *buf = ::getenv(field);
|
||||||
|
return buf ? buf : std::string{};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace os
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
111
f4mp_originalcode/F4MPClient/include/spdlog/details/os.h
Normal file
111
f4mp_originalcode/F4MPClient/include/spdlog/details/os.h
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <ctime> // std::time_t
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
namespace os {
|
||||||
|
|
||||||
|
SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// eol definition
|
||||||
|
#if !defined(SPDLOG_EOL)
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define SPDLOG_EOL "\r\n"
|
||||||
|
#else
|
||||||
|
#define SPDLOG_EOL "\n"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
||||||
|
|
||||||
|
// folder separator
|
||||||
|
#ifdef _WIN32
|
||||||
|
static const char folder_sep = '\\';
|
||||||
|
#else
|
||||||
|
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// fopen_s on non windows for writing
|
||||||
|
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||||
|
|
||||||
|
// Remove filename. return 0 on success
|
||||||
|
SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// Remove file if exists. return 0 on success
|
||||||
|
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
|
||||||
|
SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// Return if file exists.
|
||||||
|
SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// Return file size according to open FILE* object
|
||||||
|
SPDLOG_API size_t filesize(FILE *f);
|
||||||
|
|
||||||
|
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||||
|
SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
|
||||||
|
|
||||||
|
// Return current thread id as size_t
|
||||||
|
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||||
|
// under VS 2013)
|
||||||
|
SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// Return current thread id as size_t (from thread local storage)
|
||||||
|
SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||||
|
// See https://github.com/gabime/spdlog/issues/609
|
||||||
|
SPDLOG_API void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
SPDLOG_API std::string filename_to_str(const filename_t &filename);
|
||||||
|
|
||||||
|
SPDLOG_API int pid() SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// Determine if the terminal supports colors
|
||||||
|
// Source: https://github.com/agauniyal/rang/
|
||||||
|
SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// Determine if the terminal attached
|
||||||
|
// Source: https://github.com/agauniyal/rang/
|
||||||
|
SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
|
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Return directory name from given path or empty string
|
||||||
|
// "abc/file" => "abc"
|
||||||
|
// "abc/" => "abc"
|
||||||
|
// "abc" => ""
|
||||||
|
// "abc///" => "abc//"
|
||||||
|
SPDLOG_API filename_t dir_name(filename_t path);
|
||||||
|
|
||||||
|
// Create a dir from the given path.
|
||||||
|
// Return true if succeeded or if this dir already exists.
|
||||||
|
SPDLOG_API bool create_dir(filename_t path);
|
||||||
|
|
||||||
|
// non thread safe, cross platform getenv/getenv_s
|
||||||
|
// return empty string if field not found
|
||||||
|
SPDLOG_API std::string getenv(const char *field);
|
||||||
|
|
||||||
|
} // namespace os
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "os-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/details/periodic_worker.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE periodic_worker::periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval)
|
||||||
|
{
|
||||||
|
active_ = (interval > std::chrono::seconds::zero());
|
||||||
|
if (!active_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(this->mutex_);
|
||||||
|
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
|
||||||
|
{
|
||||||
|
return; // active_ == false, so exit this thread
|
||||||
|
}
|
||||||
|
callback_fun();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop the worker thread and join it
|
||||||
|
SPDLOG_INLINE periodic_worker::~periodic_worker()
|
||||||
|
{
|
||||||
|
if (worker_thread_.joinable())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
active_ = false;
|
||||||
|
}
|
||||||
|
cv_.notify_one();
|
||||||
|
worker_thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// periodic worker thread - periodically executes the given callback function.
|
||||||
|
//
|
||||||
|
// RAII over the owned thread:
|
||||||
|
// creates the thread on construction.
|
||||||
|
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
class SPDLOG_API periodic_worker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval);
|
||||||
|
periodic_worker(const periodic_worker &) = delete;
|
||||||
|
periodic_worker &operator=(const periodic_worker &) = delete;
|
||||||
|
// stop the worker thread and join it
|
||||||
|
~periodic_worker();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool active_;
|
||||||
|
std::thread worker_thread_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable cv_;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "periodic_worker-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,299 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/details/registry.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/periodic_worker.h>
|
||||||
|
#include <spdlog/logger.h>
|
||||||
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
|
||||||
|
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
// support for the default stdout color logger
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <spdlog/sinks/wincolor_sink.h>
|
||||||
|
#else
|
||||||
|
#include <spdlog/sinks/ansicolor_sink.h>
|
||||||
|
#endif
|
||||||
|
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE registry::registry()
|
||||||
|
: formatter_(new pattern_formatter())
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
||||||
|
#else
|
||||||
|
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *default_logger_name = "";
|
||||||
|
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
||||||
|
loggers_[default_logger_name] = default_logger_;
|
||||||
|
|
||||||
|
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE registry::~registry() = default;
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
register_logger_(std::move(new_logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
new_logger->set_formatter(formatter_->clone());
|
||||||
|
|
||||||
|
if (err_handler_)
|
||||||
|
{
|
||||||
|
new_logger->set_error_handler(err_handler_);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_logger->set_level(levels_.get(new_logger->name()));
|
||||||
|
new_logger->flush_on(flush_level_);
|
||||||
|
|
||||||
|
if (backtrace_n_messages_ > 0)
|
||||||
|
{
|
||||||
|
new_logger->enable_backtrace(backtrace_n_messages_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (automatic_registration_)
|
||||||
|
{
|
||||||
|
register_logger_(std::move(new_logger));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
auto found = loggers_.find(logger_name);
|
||||||
|
return found == loggers_.end() ? nullptr : found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
return default_logger_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return raw ptr to the default logger.
|
||||||
|
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||||
|
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||||
|
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
|
SPDLOG_INLINE logger *registry::get_default_raw()
|
||||||
|
{
|
||||||
|
return default_logger_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default logger.
|
||||||
|
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||||
|
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
// remove previous default logger from the map
|
||||||
|
if (default_logger_ != nullptr)
|
||||||
|
{
|
||||||
|
loggers_.erase(default_logger_->name());
|
||||||
|
}
|
||||||
|
if (new_default_logger != nullptr)
|
||||||
|
{
|
||||||
|
loggers_[new_default_logger->name()] = new_default_logger;
|
||||||
|
}
|
||||||
|
default_logger_ = std::move(new_default_logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
tp_ = std::move(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
return tp_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||||
|
SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
formatter_ = std::move(formatter);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_formatter(formatter_->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
backtrace_n_messages_ = n_messages;
|
||||||
|
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->enable_backtrace(n_messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::disable_backtrace()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
backtrace_n_messages_ = 0;
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->disable_backtrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_level(log_level);
|
||||||
|
}
|
||||||
|
levels_.set_default(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->flush_on(log_level);
|
||||||
|
}
|
||||||
|
flush_level_ = log_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||||
|
auto clbk = [this]() { this->flush_all(); };
|
||||||
|
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::set_error_handler(void (*handler)(const std::string &msg))
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_error_handler(handler);
|
||||||
|
}
|
||||||
|
err_handler_ = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
fun(l.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::flush_all()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::drop(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
loggers_.erase(logger_name);
|
||||||
|
if (default_logger_ && default_logger_->name() == logger_name)
|
||||||
|
{
|
||||||
|
default_logger_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::drop_all()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
loggers_.clear();
|
||||||
|
default_logger_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean all resources and threads started by the registry
|
||||||
|
SPDLOG_INLINE void registry::shutdown()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||||
|
periodic_flusher_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_all();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
tp_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex()
|
||||||
|
{
|
||||||
|
return tp_mutex_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
automatic_registration_ = automatic_registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
levels_ = std::move(levels);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
auto &logger = l.second;
|
||||||
|
logger->set_level(levels_.get(logger->name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE registry ®istry::instance()
|
||||||
|
{
|
||||||
|
static registry s_instance;
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
if (loggers_.find(logger_name) != loggers_.end())
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
auto logger_name = new_logger->name();
|
||||||
|
throw_if_exists_(logger_name);
|
||||||
|
loggers_[logger_name] = std::move(new_logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
112
f4mp_originalcode/F4MPClient/include/spdlog/details/registry.h
Normal file
112
f4mp_originalcode/F4MPClient/include/spdlog/details/registry.h
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Loggers registry of unique name->logger pointer
|
||||||
|
// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
|
||||||
|
// If user requests a non existing logger, nullptr will be returned
|
||||||
|
// This class is thread safe
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/cfg/log_levels.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
class logger;
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
class thread_pool;
|
||||||
|
class periodic_worker;
|
||||||
|
|
||||||
|
class SPDLOG_API registry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
registry(const registry &) = delete;
|
||||||
|
registry &operator=(const registry &) = delete;
|
||||||
|
|
||||||
|
void register_logger(std::shared_ptr<logger> new_logger);
|
||||||
|
void initialize_logger(std::shared_ptr<logger> new_logger);
|
||||||
|
std::shared_ptr<logger> get(const std::string &logger_name);
|
||||||
|
std::shared_ptr<logger> default_logger();
|
||||||
|
|
||||||
|
// Return raw ptr to the default logger.
|
||||||
|
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||||
|
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||||
|
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
|
logger *get_default_raw();
|
||||||
|
|
||||||
|
// set default logger.
|
||||||
|
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||||
|
void set_default_logger(std::shared_ptr<logger> new_default_logger);
|
||||||
|
|
||||||
|
void set_tp(std::shared_ptr<thread_pool> tp);
|
||||||
|
|
||||||
|
std::shared_ptr<thread_pool> get_tp();
|
||||||
|
|
||||||
|
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||||
|
void set_formatter(std::unique_ptr<formatter> formatter);
|
||||||
|
|
||||||
|
void enable_backtrace(size_t n_messages);
|
||||||
|
|
||||||
|
void disable_backtrace();
|
||||||
|
|
||||||
|
void set_level(level::level_enum log_level);
|
||||||
|
|
||||||
|
void flush_on(level::level_enum log_level);
|
||||||
|
|
||||||
|
void flush_every(std::chrono::seconds interval);
|
||||||
|
|
||||||
|
void set_error_handler(void (*handler)(const std::string &msg));
|
||||||
|
|
||||||
|
void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
|
||||||
|
|
||||||
|
void flush_all();
|
||||||
|
|
||||||
|
void drop(const std::string &logger_name);
|
||||||
|
|
||||||
|
void drop_all();
|
||||||
|
|
||||||
|
// clean all resources and threads started by the registry
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
std::recursive_mutex &tp_mutex();
|
||||||
|
|
||||||
|
void set_automatic_registration(bool automatic_registration);
|
||||||
|
|
||||||
|
void update_levels(cfg::log_levels levels);
|
||||||
|
|
||||||
|
static registry &instance();
|
||||||
|
|
||||||
|
private:
|
||||||
|
registry();
|
||||||
|
~registry();
|
||||||
|
|
||||||
|
void throw_if_exists_(const std::string &logger_name);
|
||||||
|
void register_logger_(std::shared_ptr<logger> new_logger);
|
||||||
|
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||||
|
std::recursive_mutex tp_mutex_;
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||||
|
cfg::log_levels levels_;
|
||||||
|
std::unique_ptr<formatter> formatter_;
|
||||||
|
level::level_enum flush_level_ = level::off;
|
||||||
|
void (*err_handler_)(const std::string &msg);
|
||||||
|
std::shared_ptr<thread_pool> tp_;
|
||||||
|
std::unique_ptr<periodic_worker> periodic_flusher_;
|
||||||
|
std::shared_ptr<logger> default_logger_;
|
||||||
|
bool automatic_registration_ = true;
|
||||||
|
size_t backtrace_n_messages_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "registry-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "registry.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
// Default logger factory- creates synchronous loggers
|
||||||
|
class logger;
|
||||||
|
|
||||||
|
struct synchronous_factory
|
||||||
|
{
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
||||||
|
{
|
||||||
|
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||||
|
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
||||||
|
details::registry::instance().initialize_logger(new_logger);
|
||||||
|
return new_logger;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
// tcp client helper
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
#pragma comment(lib, "Mswsock.lib")
|
||||||
|
#pragma comment(lib, "AdvApi32.lib")
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
class tcp_client
|
||||||
|
{
|
||||||
|
SOCKET socket_ = INVALID_SOCKET;
|
||||||
|
|
||||||
|
static bool winsock_initialized_()
|
||||||
|
{
|
||||||
|
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (s == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
closesocket(s);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_winsock_()
|
||||||
|
{
|
||||||
|
WSADATA wsaData;
|
||||||
|
auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
|
if (rv != 0)
|
||||||
|
{
|
||||||
|
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void throw_winsock_error_(const std::string &msg, int last_error)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
|
||||||
|
|
||||||
|
throw_spdlog_ex(fmt::format("tcp_sink - {}: {}", msg, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool is_connected() const
|
||||||
|
{
|
||||||
|
return socket_ != INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
::closesocket(socket_);
|
||||||
|
socket_ = INVALID_SOCKET;
|
||||||
|
WSACleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKET fd() const
|
||||||
|
{
|
||||||
|
return socket_;
|
||||||
|
}
|
||||||
|
|
||||||
|
~tcp_client()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to connect or throw on failure
|
||||||
|
void connect(const std::string &host, int port)
|
||||||
|
{
|
||||||
|
// initialize winsock if needed
|
||||||
|
if (!winsock_initialized_())
|
||||||
|
{
|
||||||
|
init_winsock_();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_connected())
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
struct addrinfo hints
|
||||||
|
{};
|
||||||
|
ZeroMemory(&hints, sizeof(hints));
|
||||||
|
|
||||||
|
hints.ai_family = AF_INET; // IPv4
|
||||||
|
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||||
|
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||||
|
hints.ai_protocol = 0;
|
||||||
|
|
||||||
|
auto port_str = std::to_string(port);
|
||||||
|
struct addrinfo *addrinfo_result;
|
||||||
|
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||||
|
int last_error = 0;
|
||||||
|
if (rv != 0)
|
||||||
|
{
|
||||||
|
last_error = ::WSAGetLastError();
|
||||||
|
WSACleanup();
|
||||||
|
throw_winsock_error_("getaddrinfo failed", last_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try each address until we successfully connect(2).
|
||||||
|
|
||||||
|
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
|
||||||
|
{
|
||||||
|
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||||
|
if (socket_ == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
last_error = ::WSAGetLastError();
|
||||||
|
WSACleanup();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
last_error = ::WSAGetLastError();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
::freeaddrinfo(addrinfo_result);
|
||||||
|
if (socket_ == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
WSACleanup();
|
||||||
|
throw_winsock_error_("connect failed", last_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set TCP_NODELAY
|
||||||
|
int enable_flag = 1;
|
||||||
|
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send exactly n_bytes of the given data.
|
||||||
|
// On error close the connection and throw.
|
||||||
|
void send(const char *data, size_t n_bytes)
|
||||||
|
{
|
||||||
|
size_t bytes_sent = 0;
|
||||||
|
while (bytes_sent < n_bytes)
|
||||||
|
{
|
||||||
|
const int send_flags = 0;
|
||||||
|
auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
|
||||||
|
if (write_result == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
int last_error = ::WSAGetLastError();
|
||||||
|
close();
|
||||||
|
throw_winsock_error_("send failed", last_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_result == 0) // (probably should not happen but in any case..)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytes_sent += static_cast<size_t>(write_result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
145
f4mp_originalcode/F4MPClient/include/spdlog/details/tcp_client.h
Normal file
145
f4mp_originalcode/F4MPClient/include/spdlog/details/tcp_client.h
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#error include tcp_client-windows.h instead
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// tcp client helper
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
class tcp_client
|
||||||
|
{
|
||||||
|
int socket_ = -1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool is_connected() const
|
||||||
|
{
|
||||||
|
return socket_ != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
if (is_connected())
|
||||||
|
{
|
||||||
|
::close(socket_);
|
||||||
|
socket_ = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd() const
|
||||||
|
{
|
||||||
|
return socket_;
|
||||||
|
}
|
||||||
|
|
||||||
|
~tcp_client()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to connect or throw on failure
|
||||||
|
void connect(const std::string &host, int port)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
struct addrinfo hints
|
||||||
|
{};
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
hints.ai_family = AF_INET; // IPv4
|
||||||
|
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||||
|
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||||
|
hints.ai_protocol = 0;
|
||||||
|
|
||||||
|
auto port_str = std::to_string(port);
|
||||||
|
struct addrinfo *addrinfo_result;
|
||||||
|
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||||
|
if (rv != 0)
|
||||||
|
{
|
||||||
|
auto msg = fmt::format("::getaddrinfo failed: {}", gai_strerror(rv));
|
||||||
|
throw_spdlog_ex(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try each address until we successfully connect(2).
|
||||||
|
int last_errno = 0;
|
||||||
|
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
|
||||||
|
{
|
||||||
|
int const flags = SOCK_CLOEXEC;
|
||||||
|
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
|
||||||
|
if (socket_ == -1)
|
||||||
|
{
|
||||||
|
last_errno = errno;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
|
||||||
|
if (rv == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
last_errno = errno;
|
||||||
|
::close(socket_);
|
||||||
|
socket_ = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
::freeaddrinfo(addrinfo_result);
|
||||||
|
if (socket_ == -1)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("::connect failed", last_errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set TCP_NODELAY
|
||||||
|
int enable_flag = 1;
|
||||||
|
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
|
||||||
|
|
||||||
|
// prevent sigpipe on systems where MSG_NOSIGNAL is not available
|
||||||
|
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||||
|
::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, (char *)&enable_flag, sizeof(enable_flag));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||||
|
#error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send exactly n_bytes of the given data.
|
||||||
|
// On error close the connection and throw.
|
||||||
|
void send(const char *data, size_t n_bytes)
|
||||||
|
{
|
||||||
|
size_t bytes_sent = 0;
|
||||||
|
while (bytes_sent < n_bytes)
|
||||||
|
{
|
||||||
|
#if defined(MSG_NOSIGNAL)
|
||||||
|
const int send_flags = MSG_NOSIGNAL;
|
||||||
|
#else
|
||||||
|
const int send_flags = 0;
|
||||||
|
#endif
|
||||||
|
auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
|
||||||
|
if (write_result < 0)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
throw_spdlog_ex("write(2) failed", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_result == 0) // (probably should not happen but in any case..)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytes_sent += static_cast<size_t>(write_result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/details/thread_pool.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
|
||||||
|
: q_(q_max_items)
|
||||||
|
{
|
||||||
|
if (threads_n == 0 || threads_n > 1000)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
|
||||||
|
"range is 1-1000)");
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < threads_n; i++)
|
||||||
|
{
|
||||||
|
threads_.emplace_back([this, on_thread_start] {
|
||||||
|
on_thread_start();
|
||||||
|
this->thread_pool::worker_loop_();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
||||||
|
: thread_pool(q_max_items, threads_n, [] {})
|
||||||
|
{}
|
||||||
|
|
||||||
|
// message all threads to terminate gracefully join them
|
||||||
|
SPDLOG_INLINE thread_pool::~thread_pool()
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < threads_.size(); i++)
|
||||||
|
{
|
||||||
|
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads_)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_ALL() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
|
||||||
|
post_async_msg_(std::move(async_m), overflow_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SPDLOG_INLINE thread_pool::overrun_counter()
|
||||||
|
{
|
||||||
|
return q_.overrun_counter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
if (overflow_policy == async_overflow_policy::block)
|
||||||
|
{
|
||||||
|
q_.enqueue(std::move(new_msg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
q_.enqueue_nowait(std::move(new_msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDLOG_INLINE thread_pool::worker_loop_()
|
||||||
|
{
|
||||||
|
while (process_next_msg_()) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process next message in the queue
|
||||||
|
// return true if this thread should still be active (while no terminate msg
|
||||||
|
// was received)
|
||||||
|
bool SPDLOG_INLINE thread_pool::process_next_msg_()
|
||||||
|
{
|
||||||
|
async_msg incoming_async_msg;
|
||||||
|
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
|
||||||
|
if (!dequeued)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (incoming_async_msg.msg_type)
|
||||||
|
{
|
||||||
|
case async_msg_type::log: {
|
||||||
|
incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case async_msg_type::flush: {
|
||||||
|
incoming_async_msg.worker_ptr->backend_flush_();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case async_msg_type::terminate: {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/log_msg_buffer.h>
|
||||||
|
#include <spdlog/details/mpmc_blocking_q.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
class async_logger;
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
||||||
|
|
||||||
|
enum class async_msg_type
|
||||||
|
{
|
||||||
|
log,
|
||||||
|
flush,
|
||||||
|
terminate
|
||||||
|
};
|
||||||
|
|
||||||
|
#include <spdlog/details/log_msg_buffer.h>
|
||||||
|
// Async msg to move to/from the queue
|
||||||
|
// Movable only. should never be copied
|
||||||
|
struct async_msg : log_msg_buffer
|
||||||
|
{
|
||||||
|
async_msg_type msg_type{async_msg_type::log};
|
||||||
|
async_logger_ptr worker_ptr;
|
||||||
|
|
||||||
|
async_msg() = default;
|
||||||
|
~async_msg() = default;
|
||||||
|
|
||||||
|
// should only be moved in or out of the queue..
|
||||||
|
async_msg(const async_msg &) = delete;
|
||||||
|
|
||||||
|
// support for vs2013 move
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||||
|
async_msg(async_msg &&other)
|
||||||
|
: log_msg_buffer(std::move(other))
|
||||||
|
, msg_type(other.msg_type)
|
||||||
|
, worker_ptr(std::move(other.worker_ptr))
|
||||||
|
{}
|
||||||
|
|
||||||
|
async_msg &operator=(async_msg &&other)
|
||||||
|
{
|
||||||
|
*static_cast<log_msg_buffer *>(this) = std::move(other);
|
||||||
|
msg_type = other.msg_type;
|
||||||
|
worker_ptr = std::move(other.worker_ptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#else // (_MSC_VER) && _MSC_VER <= 1800
|
||||||
|
async_msg(async_msg &&) = default;
|
||||||
|
async_msg &operator=(async_msg &&) = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// construct from log_msg with given type
|
||||||
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
|
||||||
|
: log_msg_buffer{m}
|
||||||
|
, msg_type{the_type}
|
||||||
|
, worker_ptr{std::move(worker)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
||||||
|
: log_msg_buffer{}
|
||||||
|
, msg_type{the_type}
|
||||||
|
, worker_ptr{std::move(worker)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
explicit async_msg(async_msg_type the_type)
|
||||||
|
: async_msg{nullptr, the_type}
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SPDLOG_API thread_pool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item_type = async_msg;
|
||||||
|
using q_type = details::mpmc_blocking_queue<item_type>;
|
||||||
|
|
||||||
|
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
|
||||||
|
thread_pool(size_t q_max_items, size_t threads_n);
|
||||||
|
|
||||||
|
// message all threads to terminate gracefully join them
|
||||||
|
~thread_pool();
|
||||||
|
|
||||||
|
thread_pool(const thread_pool &) = delete;
|
||||||
|
thread_pool &operator=(thread_pool &&) = delete;
|
||||||
|
|
||||||
|
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
||||||
|
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
||||||
|
size_t overrun_counter();
|
||||||
|
|
||||||
|
private:
|
||||||
|
q_type q_;
|
||||||
|
|
||||||
|
std::vector<std::thread> threads_;
|
||||||
|
|
||||||
|
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
|
||||||
|
void worker_loop_();
|
||||||
|
|
||||||
|
// process next message in the queue
|
||||||
|
// return true if this thread should still be active (while no terminate msg
|
||||||
|
// was received)
|
||||||
|
bool process_next_msg_();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "thread_pool-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX // prevent windows redefining min/max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
216
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bin_to_hex.h
Normal file
216
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bin_to_hex.h
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Support for logging binary data as hex
|
||||||
|
// format flags:
|
||||||
|
// {:X} - print in uppercase.
|
||||||
|
// {:s} - don't separate each byte with space.
|
||||||
|
// {:p} - don't print the position on each line start.
|
||||||
|
// {:n} - don't split the output to lines.
|
||||||
|
// {:a} - show ASCII if :n is not set
|
||||||
|
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// std::vector<char> v(200, 0x0b);
|
||||||
|
// logger->info("Some buffer {}", spdlog::to_hex(v));
|
||||||
|
// char buf[128];
|
||||||
|
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
|
||||||
|
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
class dump_info
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
dump_info(It range_begin, It range_end, size_t size_per_line)
|
||||||
|
: begin_(range_begin)
|
||||||
|
, end_(range_end)
|
||||||
|
, size_per_line_(size_per_line)
|
||||||
|
{}
|
||||||
|
|
||||||
|
It begin() const
|
||||||
|
{
|
||||||
|
return begin_;
|
||||||
|
}
|
||||||
|
It end() const
|
||||||
|
{
|
||||||
|
return end_;
|
||||||
|
}
|
||||||
|
size_t size_per_line() const
|
||||||
|
{
|
||||||
|
return size_per_line_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
It begin_, end_;
|
||||||
|
size_t size_per_line_;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
// create a dump_info that wraps the given container
|
||||||
|
template<typename Container>
|
||||||
|
inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
||||||
|
using Iter = typename Container::const_iterator;
|
||||||
|
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create dump_info from ranges
|
||||||
|
template<typename It>
|
||||||
|
inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
|
||||||
|
{
|
||||||
|
return details::dump_info<It>(range_begin, range_end, size_per_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct formatter<spdlog::details::dump_info<T>>
|
||||||
|
{
|
||||||
|
const char delimiter = ' ';
|
||||||
|
bool put_newlines = true;
|
||||||
|
bool put_delimiters = true;
|
||||||
|
bool use_uppercase = false;
|
||||||
|
bool put_positions = true; // position on start of each line
|
||||||
|
bool show_ascii = false;
|
||||||
|
|
||||||
|
// parse the format string flags
|
||||||
|
template<typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||||
|
{
|
||||||
|
auto it = ctx.begin();
|
||||||
|
while (it != ctx.end() && *it != '}')
|
||||||
|
{
|
||||||
|
switch (*it)
|
||||||
|
{
|
||||||
|
case 'X':
|
||||||
|
use_uppercase = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
put_delimiters = false;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
put_positions = false;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
put_newlines = false;
|
||||||
|
show_ascii = false;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
if (put_newlines)
|
||||||
|
{
|
||||||
|
show_ascii = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// format the given bytes range as hex
|
||||||
|
template<typename FormatContext, typename Container>
|
||||||
|
auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
|
||||||
|
{
|
||||||
|
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
||||||
|
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
||||||
|
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
||||||
|
|
||||||
|
#if FMT_VERSION < 60000
|
||||||
|
auto inserter = ctx.begin();
|
||||||
|
#else
|
||||||
|
auto inserter = ctx.out();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int size_per_line = static_cast<int>(the_range.size_per_line());
|
||||||
|
auto start_of_line = the_range.begin();
|
||||||
|
for (auto i = the_range.begin(); i != the_range.end(); i++)
|
||||||
|
{
|
||||||
|
auto ch = static_cast<unsigned char>(*i);
|
||||||
|
|
||||||
|
if (put_newlines && (i == the_range.begin() || i - start_of_line >= size_per_line))
|
||||||
|
{
|
||||||
|
if (show_ascii && i != the_range.begin())
|
||||||
|
{
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
for (auto j = start_of_line; j < i; j++)
|
||||||
|
{
|
||||||
|
auto pc = static_cast<unsigned char>(*j);
|
||||||
|
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put_newline(inserter, static_cast<size_t>(i - the_range.begin()));
|
||||||
|
|
||||||
|
// put first byte without delimiter in front of it
|
||||||
|
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||||
|
*inserter++ = hex_chars[ch & 0x0f];
|
||||||
|
start_of_line = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (put_delimiters)
|
||||||
|
{
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||||
|
*inserter++ = hex_chars[ch & 0x0f];
|
||||||
|
}
|
||||||
|
if (show_ascii) // add ascii to last line
|
||||||
|
{
|
||||||
|
if (the_range.end() - the_range.begin() > size_per_line)
|
||||||
|
{
|
||||||
|
auto blank_num = size_per_line - (the_range.end() - start_of_line);
|
||||||
|
while (blank_num-- > 0)
|
||||||
|
{
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
if (put_delimiters)
|
||||||
|
{
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
for (auto j = start_of_line; j != the_range.end(); j++)
|
||||||
|
{
|
||||||
|
auto pc = static_cast<unsigned char>(*j);
|
||||||
|
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inserter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put newline(and position header)
|
||||||
|
template<typename It>
|
||||||
|
void put_newline(It inserter, std::size_t pos)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
*inserter++ = '\r';
|
||||||
|
#endif
|
||||||
|
*inserter++ = '\n';
|
||||||
|
|
||||||
|
if (put_positions)
|
||||||
|
{
|
||||||
|
fmt::format_to(inserter, "{:<04X}: ", pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace fmt
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
--- Optional exception to the license ---
|
||||||
|
|
||||||
|
As an exception, if, as a result of your compiling your source code, portions
|
||||||
|
of this Software are embedded into a machine-executable object form of such
|
||||||
|
source code, you may redistribute such embedded portions in such object form
|
||||||
|
without including the above copyright and permission notices.
|
||||||
1119
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/chrono.h
Normal file
1119
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/chrono.h
Normal file
File diff suppressed because it is too large
Load diff
568
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/color.h
Normal file
568
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/color.h
Normal file
|
|
@ -0,0 +1,568 @@
|
||||||
|
// Formatting library for C++ - color support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COLOR_H_
|
||||||
|
#define FMT_COLOR_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
enum class color : uint32_t {
|
||||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||||
|
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||||
|
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||||
|
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||||
|
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||||
|
black = 0x000000, // rgb(0,0,0)
|
||||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||||
|
blue = 0x0000FF, // rgb(0,0,255)
|
||||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||||
|
brown = 0xA52A2A, // rgb(165,42,42)
|
||||||
|
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||||
|
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||||
|
coral = 0xFF7F50, // rgb(255,127,80)
|
||||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||||
|
crimson = 0xDC143C, // rgb(220,20,60)
|
||||||
|
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||||
|
dark_green = 0x006400, // rgb(0,100,0)
|
||||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||||
|
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||||
|
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||||
|
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||||
|
dim_gray = 0x696969, // rgb(105,105,105)
|
||||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||||
|
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||||
|
forest_green = 0x228B22, // rgb(34,139,34)
|
||||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||||
|
gold = 0xFFD700, // rgb(255,215,0)
|
||||||
|
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||||
|
gray = 0x808080, // rgb(128,128,128)
|
||||||
|
green = 0x008000, // rgb(0,128,0)
|
||||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||||
|
indigo = 0x4B0082, // rgb(75,0,130)
|
||||||
|
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||||
|
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||||
|
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||||
|
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||||
|
light_coral = 0xF08080, // rgb(240,128,128)
|
||||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||||
|
light_green = 0x90EE90, // rgb(144,238,144)
|
||||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||||
|
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||||
|
lime = 0x00FF00, // rgb(0,255,0)
|
||||||
|
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||||
|
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||||
|
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
maroon = 0x800000, // rgb(128,0,0)
|
||||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||||
|
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||||
|
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||||
|
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||||
|
navy = 0x000080, // rgb(0,0,128)
|
||||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||||
|
olive = 0x808000, // rgb(128,128,0)
|
||||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||||
|
orange = 0xFFA500, // rgb(255,165,0)
|
||||||
|
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||||
|
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||||
|
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||||
|
peru = 0xCD853F, // rgb(205,133,63)
|
||||||
|
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||||
|
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||||
|
purple = 0x800080, // rgb(128,0,128)
|
||||||
|
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||||
|
red = 0xFF0000, // rgb(255,0,0)
|
||||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||||
|
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||||
|
salmon = 0xFA8072, // rgb(250,128,114)
|
||||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||||
|
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||||
|
sienna = 0xA0522D, // rgb(160,82,45)
|
||||||
|
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||||
|
slate_gray = 0x708090, // rgb(112,128,144)
|
||||||
|
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||||
|
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||||
|
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||||
|
tan = 0xD2B48C, // rgb(210,180,140)
|
||||||
|
teal = 0x008080, // rgb(0,128,128)
|
||||||
|
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||||
|
tomato = 0xFF6347, // rgb(255,99,71)
|
||||||
|
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||||
|
violet = 0xEE82EE, // rgb(238,130,238)
|
||||||
|
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||||
|
white = 0xFFFFFF, // rgb(255,255,255)
|
||||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||||
|
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||||
|
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||||
|
}; // enum class color
|
||||||
|
|
||||||
|
enum class terminal_color : uint8_t {
|
||||||
|
black = 30,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
yellow,
|
||||||
|
blue,
|
||||||
|
magenta,
|
||||||
|
cyan,
|
||||||
|
white,
|
||||||
|
bright_black = 90,
|
||||||
|
bright_red,
|
||||||
|
bright_green,
|
||||||
|
bright_yellow,
|
||||||
|
bright_blue,
|
||||||
|
bright_magenta,
|
||||||
|
bright_cyan,
|
||||||
|
bright_white
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class emphasis : uint8_t {
|
||||||
|
bold = 1,
|
||||||
|
italic = 1 << 1,
|
||||||
|
underline = 1 << 2,
|
||||||
|
strikethrough = 1 << 3
|
||||||
|
};
|
||||||
|
|
||||||
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||||
|
struct rgb {
|
||||||
|
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
||||||
|
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||||
|
FMT_CONSTEXPR rgb(uint32_t hex)
|
||||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||||
|
FMT_CONSTEXPR rgb(color hex)
|
||||||
|
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||||
|
g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// color is a struct of either a rgb color or a terminal color.
|
||||||
|
struct color_type {
|
||||||
|
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
|
||||||
|
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
|
||||||
|
value{} {
|
||||||
|
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
|
||||||
|
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||||
|
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
|
||||||
|
value{} {
|
||||||
|
value.term_color = static_cast<uint8_t>(term_color);
|
||||||
|
}
|
||||||
|
bool is_rgb;
|
||||||
|
union color_union {
|
||||||
|
uint8_t term_color;
|
||||||
|
uint32_t rgb_color;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Experimental text formatting support.
|
||||||
|
class text_style {
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(),
|
||||||
|
set_background_color(),
|
||||||
|
ems(em) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't OR a terminal color"));
|
||||||
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't OR a terminal color"));
|
||||||
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||||
|
const text_style& rhs) {
|
||||||
|
return lhs |= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't AND a terminal color"));
|
||||||
|
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't AND a terminal color"));
|
||||||
|
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
||||||
|
const text_style& rhs) {
|
||||||
|
return lhs &= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||||
|
return set_foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||||
|
return set_background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(ems) != 0;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||||
|
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||||
|
return foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
||||||
|
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||||
|
return background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||||
|
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||||
|
return ems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
|
internal::color_type text_color) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(),
|
||||||
|
set_background_color(),
|
||||||
|
ems() {
|
||||||
|
if (is_foreground) {
|
||||||
|
foreground_color = text_color;
|
||||||
|
set_foreground_color = true;
|
||||||
|
} else {
|
||||||
|
background_color = text_color;
|
||||||
|
set_background_color = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
internal::color_type foreground_color;
|
||||||
|
internal::color_type background_color;
|
||||||
|
bool set_foreground_color;
|
||||||
|
bool set_background_color;
|
||||||
|
emphasis ems;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/true, foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/false, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
||||||
|
return text_style(lhs) | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Char> struct ansi_color_escape {
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||||
|
const char* esc) FMT_NOEXCEPT {
|
||||||
|
// If we have a terminal color, we need to output another escape code
|
||||||
|
// sequence.
|
||||||
|
if (!text_color.is_rgb) {
|
||||||
|
bool is_background = esc == internal::data::background_color;
|
||||||
|
uint32_t value = text_color.value.term_color;
|
||||||
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
|
// 10 more.
|
||||||
|
if (is_background) value += 10u;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
|
||||||
|
if (value >= 100u) {
|
||||||
|
buffer[index++] = static_cast<Char>('1');
|
||||||
|
value %= 100u;
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||||
|
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
buffer[index++] = static_cast<Char>('\0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
|
}
|
||||||
|
rgb color(text_color.value.rgb_color);
|
||||||
|
to_esc(color.r, buffer + 7, ';');
|
||||||
|
to_esc(color.g, buffer + 11, ';');
|
||||||
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
|
buffer[19] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||||
|
uint8_t em_codes[4] = {};
|
||||||
|
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||||
|
em_codes[3] = 9;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (!em_codes[i]) continue;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
||||||
|
return buffer + std::char_traits<Char>::length(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Char buffer[7u + 3u * 4u + 1u];
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||||
|
char delimiter) FMT_NOEXCEPT {
|
||||||
|
out[0] = static_cast<Char>('0' + c / 100);
|
||||||
|
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||||
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
|
out[3] = static_cast<Char>(delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||||
|
internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||||
|
internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(background, internal::data::background_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(em);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
||||||
|
std::fputs(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||||
|
std::fputws(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::reset_color, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::wreset_color, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||||
|
const char* begin = data::reset_color;
|
||||||
|
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||||
|
buffer.append(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<Char>> args) {
|
||||||
|
bool has_style = false;
|
||||||
|
if (ts.has_emphasis()) {
|
||||||
|
has_style = true;
|
||||||
|
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
|
||||||
|
buf.append(emphasis.begin(), emphasis.end());
|
||||||
|
}
|
||||||
|
if (ts.has_foreground()) {
|
||||||
|
has_style = true;
|
||||||
|
auto foreground =
|
||||||
|
internal::make_foreground_color<Char>(ts.get_foreground());
|
||||||
|
buf.append(foreground.begin(), foreground.end());
|
||||||
|
}
|
||||||
|
if (ts.has_background()) {
|
||||||
|
has_style = true;
|
||||||
|
auto background =
|
||||||
|
internal::make_background_color<Char>(ts.get_background());
|
||||||
|
buf.append(background.begin(), background.end());
|
||||||
|
}
|
||||||
|
internal::vformat_to(buf, format_str, args);
|
||||||
|
if (has_style) internal::reset_color<Char>(buf);
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
|
basic_format_args<buffer_context<Char>> args) {
|
||||||
|
basic_memory_buffer<Char> buf;
|
||||||
|
internal::vformat_to(buf, ts, to_string_view(format), args);
|
||||||
|
buf.push_back(Char(0));
|
||||||
|
internal::fputs(buf.data(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||||
|
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
using context = buffer_context<char_t<S>>;
|
||||||
|
format_arg_store<context, Args...> as{args...};
|
||||||
|
vprint(f, ts, format_str, basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||||
|
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||||
|
return print(stdout, ts, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const text_style& ts, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
basic_memory_buffer<Char> buf;
|
||||||
|
internal::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||||
|
return fmt::to_string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments and returns the result as a string using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
#include <fmt/color.h>
|
||||||
|
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"The answer is {}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
|
return vformat(ts, to_string_view(format_str),
|
||||||
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COLOR_H_
|
||||||
|
|
@ -0,0 +1,595 @@
|
||||||
|
// Formatting library for C++ - experimental format string compilation
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COMPILE_H_
|
||||||
|
#define FMT_COMPILE_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Part of a compiled format string. It can be either literal text or a
|
||||||
|
// replacement field.
|
||||||
|
template <typename Char> struct format_part {
|
||||||
|
enum class kind { arg_index, arg_name, text, replacement };
|
||||||
|
|
||||||
|
struct replacement {
|
||||||
|
arg_ref<Char> arg_id;
|
||||||
|
dynamic_format_specs<Char> specs;
|
||||||
|
};
|
||||||
|
|
||||||
|
kind part_kind;
|
||||||
|
union value {
|
||||||
|
int arg_index;
|
||||||
|
basic_string_view<Char> str;
|
||||||
|
replacement repl;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
|
||||||
|
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
|
||||||
|
FMT_CONSTEXPR value(replacement r) : repl(r) {}
|
||||||
|
} val;
|
||||||
|
// Position past the end of the argument id.
|
||||||
|
const Char* arg_id_end = nullptr;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
|
||||||
|
: part_kind(k), val(v) {}
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR format_part make_arg_index(int index) {
|
||||||
|
return format_part(kind::arg_index, index);
|
||||||
|
}
|
||||||
|
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
|
||||||
|
return format_part(kind::arg_name, name);
|
||||||
|
}
|
||||||
|
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
|
||||||
|
return format_part(kind::text, text);
|
||||||
|
}
|
||||||
|
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
|
||||||
|
return format_part(kind::replacement, repl);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char> struct part_counter {
|
||||||
|
unsigned num_parts = 0;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||||
|
if (begin != end) ++num_parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
|
||||||
|
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
|
||||||
|
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
||||||
|
const Char* end) {
|
||||||
|
// Find the matching brace.
|
||||||
|
unsigned brace_counter = 0;
|
||||||
|
for (; begin != end; ++begin) {
|
||||||
|
if (*begin == '{') {
|
||||||
|
++brace_counter;
|
||||||
|
} else if (*begin == '}') {
|
||||||
|
if (brace_counter == 0u) break;
|
||||||
|
--brace_counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_error(const char*) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Counts the number of parts in a format string.
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
|
||||||
|
part_counter<Char> counter;
|
||||||
|
parse_format_string<true>(format_str, counter);
|
||||||
|
return counter.num_parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename PartHandler>
|
||||||
|
class format_string_compiler : public error_handler {
|
||||||
|
private:
|
||||||
|
using part = format_part<Char>;
|
||||||
|
|
||||||
|
PartHandler handler_;
|
||||||
|
part part_;
|
||||||
|
basic_string_view<Char> format_str_;
|
||||||
|
basic_format_parse_context<Char> parse_context_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
|
||||||
|
PartHandler handler)
|
||||||
|
: handler_(handler),
|
||||||
|
format_str_(format_str),
|
||||||
|
parse_context_(format_str) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||||
|
if (begin != end)
|
||||||
|
handler_(part::make_text({begin, to_unsigned(end - begin)}));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_arg_id() {
|
||||||
|
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_arg_id(int id) {
|
||||||
|
parse_context_.check_arg_id(id);
|
||||||
|
part_ = part::make_arg_index(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
|
||||||
|
part_ = part::make_arg_name(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
|
||||||
|
part_.arg_id_end = ptr;
|
||||||
|
handler_(part_);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
||||||
|
const Char* end) {
|
||||||
|
auto repl = typename part::replacement();
|
||||||
|
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
||||||
|
repl.specs, parse_context_);
|
||||||
|
auto it = parse_format_specs(begin, end, handler);
|
||||||
|
if (*it != '}') on_error("missing '}' in format string");
|
||||||
|
repl.arg_id = part_.part_kind == part::kind::arg_index
|
||||||
|
? arg_ref<Char>(part_.val.arg_index)
|
||||||
|
: arg_ref<Char>(part_.val.str);
|
||||||
|
auto part = part::make_replacement(repl);
|
||||||
|
part.arg_id_end = begin;
|
||||||
|
handler_(part);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compiles a format string and invokes handler(part) for each parsed part.
|
||||||
|
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
|
||||||
|
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
|
||||||
|
PartHandler handler) {
|
||||||
|
parse_format_string<IS_CONSTEXPR>(
|
||||||
|
format_str,
|
||||||
|
format_string_compiler<Char, PartHandler>(format_str, handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Range, typename Context, typename Id>
|
||||||
|
void format_arg(
|
||||||
|
basic_format_parse_context<typename Range::value_type>& parse_ctx,
|
||||||
|
Context& ctx, Id arg_id) {
|
||||||
|
ctx.advance_to(
|
||||||
|
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// vformat_to is defined in a subnamespace to prevent ADL.
|
||||||
|
namespace cf {
|
||||||
|
template <typename Context, typename Range, typename CompiledFormat>
|
||||||
|
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
||||||
|
-> typename Context::iterator {
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
basic_format_parse_context<char_type> parse_ctx(
|
||||||
|
to_string_view(cf.format_str_));
|
||||||
|
Context ctx(out.begin(), args);
|
||||||
|
|
||||||
|
const auto& parts = cf.parts();
|
||||||
|
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
||||||
|
++part_it) {
|
||||||
|
const auto& part = *part_it;
|
||||||
|
const auto& value = part.val;
|
||||||
|
|
||||||
|
using format_part_t = format_part<char_type>;
|
||||||
|
switch (part.part_kind) {
|
||||||
|
case format_part_t::kind::text: {
|
||||||
|
const auto text = value.str;
|
||||||
|
auto output = ctx.out();
|
||||||
|
auto&& it = reserve(output, text.size());
|
||||||
|
it = std::copy_n(text.begin(), text.size(), it);
|
||||||
|
ctx.advance_to(output);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case format_part_t::kind::arg_index:
|
||||||
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
|
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case format_part_t::kind::arg_name:
|
||||||
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
|
internal::format_arg<Range>(parse_ctx, ctx, value.str);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case format_part_t::kind::replacement: {
|
||||||
|
const auto& arg_id_value = value.repl.arg_id.val;
|
||||||
|
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
|
||||||
|
? ctx.arg(arg_id_value.index)
|
||||||
|
: ctx.arg(arg_id_value.name);
|
||||||
|
|
||||||
|
auto specs = value.repl.specs;
|
||||||
|
|
||||||
|
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
|
||||||
|
handle_dynamic_spec<precision_checker>(specs.precision,
|
||||||
|
specs.precision_ref, ctx);
|
||||||
|
|
||||||
|
error_handler h;
|
||||||
|
numeric_specs_checker<error_handler> checker(h, arg.type());
|
||||||
|
if (specs.align == align::numeric) checker.require_numeric_argument();
|
||||||
|
if (specs.sign != sign::none) checker.check_sign();
|
||||||
|
if (specs.alt) checker.require_numeric_argument();
|
||||||
|
if (specs.precision >= 0) checker.check_precision();
|
||||||
|
|
||||||
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
|
ctx.advance_to(
|
||||||
|
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
} // namespace cf
|
||||||
|
|
||||||
|
struct basic_compiled_format {};
|
||||||
|
|
||||||
|
template <typename S, typename = void>
|
||||||
|
struct compiled_format_base : basic_compiled_format {
|
||||||
|
using char_type = char_t<S>;
|
||||||
|
using parts_container = std::vector<internal::format_part<char_type>>;
|
||||||
|
|
||||||
|
parts_container compiled_parts;
|
||||||
|
|
||||||
|
explicit compiled_format_base(basic_string_view<char_type> format_str) {
|
||||||
|
compile_format_string<false>(format_str,
|
||||||
|
[this](const format_part<char_type>& part) {
|
||||||
|
compiled_parts.push_back(part);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts_container& parts() const { return compiled_parts; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, unsigned N> struct format_part_array {
|
||||||
|
format_part<Char> data[N] = {};
|
||||||
|
FMT_CONSTEXPR format_part_array() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, unsigned N>
|
||||||
|
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
|
||||||
|
basic_string_view<Char> format_str) {
|
||||||
|
format_part_array<Char, N> parts;
|
||||||
|
unsigned counter = 0;
|
||||||
|
// This is not a lambda for compatibility with older compilers.
|
||||||
|
struct {
|
||||||
|
format_part<Char>* parts;
|
||||||
|
unsigned* counter;
|
||||||
|
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
|
||||||
|
parts[(*counter)++] = part;
|
||||||
|
}
|
||||||
|
} collector{parts.data, &counter};
|
||||||
|
compile_format_string<true>(format_str, collector);
|
||||||
|
if (counter < N) {
|
||||||
|
parts.data[counter] =
|
||||||
|
format_part<Char>::make_text(basic_string_view<Char>());
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
|
||||||
|
return (a < b) ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
||||||
|
: basic_compiled_format {
|
||||||
|
using char_type = char_t<S>;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
||||||
|
|
||||||
|
// Workaround for old compilers. Format string compilation will not be
|
||||||
|
// performed there anyway.
|
||||||
|
#if FMT_USE_CONSTEXPR
|
||||||
|
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
|
||||||
|
constexpr_max(count_parts(to_string_view(S())), 1u);
|
||||||
|
#else
|
||||||
|
static const unsigned num_format_parts = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using parts_container = format_part<char_type>[num_format_parts];
|
||||||
|
|
||||||
|
const parts_container& parts() const {
|
||||||
|
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
||||||
|
compile_to_parts<char_type, num_format_parts>(
|
||||||
|
internal::to_string_view(S()));
|
||||||
|
return compiled_parts.data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
class compiled_format : private compiled_format_base<S> {
|
||||||
|
public:
|
||||||
|
using typename compiled_format_base<S>::char_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
basic_string_view<char_type> format_str_;
|
||||||
|
|
||||||
|
template <typename Context, typename Range, typename CompiledFormat>
|
||||||
|
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
|
||||||
|
basic_format_args<Context> args) ->
|
||||||
|
typename Context::iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
compiled_format() = delete;
|
||||||
|
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
|
||||||
|
: compiled_format_base<S>(format_str), format_str_(format_str) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cpp_if_constexpr
|
||||||
|
template <typename... Args> struct type_list {};
|
||||||
|
|
||||||
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
|
template <int N, typename T, typename... Args>
|
||||||
|
constexpr const auto& get(const T& first, const Args&... rest) {
|
||||||
|
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||||
|
if constexpr (N == 0)
|
||||||
|
return first;
|
||||||
|
else
|
||||||
|
return get<N - 1>(rest...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int N, typename> struct get_type_impl;
|
||||||
|
|
||||||
|
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||||
|
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int N, typename T>
|
||||||
|
using get_type = typename get_type_impl<N, T>::type;
|
||||||
|
|
||||||
|
template <typename T> struct is_compiled_format : std::false_type {};
|
||||||
|
|
||||||
|
template <typename Char> struct text {
|
||||||
|
basic_string_view<Char> data;
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
OutputIt format(OutputIt out, const Args&...) const {
|
||||||
|
// TODO: reserve
|
||||||
|
return copy_str<Char>(data.begin(), data.end(), out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||||
|
size_t size) {
|
||||||
|
return {{&s[pos], size}};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt, typename T,
|
||||||
|
std::enable_if_t<std::is_integral_v<T>, int> = 0>
|
||||||
|
OutputIt format_default(OutputIt out, T value) {
|
||||||
|
// TODO: reserve
|
||||||
|
format_int fi(value);
|
||||||
|
return std::copy(fi.data(), fi.data() + fi.size(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
OutputIt format_default(OutputIt out, double value) {
|
||||||
|
writer w(out);
|
||||||
|
w.write(value);
|
||||||
|
return w.out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
OutputIt format_default(OutputIt out, Char value) {
|
||||||
|
*out++ = value;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
OutputIt format_default(OutputIt out, const Char* value) {
|
||||||
|
auto length = std::char_traits<Char>::length(value);
|
||||||
|
return copy_str<Char>(value, value + length, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A replacement field that refers to argument N.
|
||||||
|
template <typename Char, typename T, int N> struct field {
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
|
// This ensures that the argument type is convertile to `const T&`.
|
||||||
|
const T& arg = get<N>(args...);
|
||||||
|
return format_default<Char>(out, arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename T, int N>
|
||||||
|
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename L, typename R> struct concat {
|
||||||
|
L lhs;
|
||||||
|
R rhs;
|
||||||
|
using char_type = typename L::char_type;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
|
out = lhs.format(out, args...);
|
||||||
|
return rhs.format(out, args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
||||||
|
return {lhs, rhs};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct unknown_format {};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
||||||
|
for (size_t size = str.size(); pos != size; ++pos) {
|
||||||
|
if (str[pos] == '{' || str[pos] == '}') break;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Args, size_t POS, int ID, typename S>
|
||||||
|
constexpr auto compile_format_string(S format_str);
|
||||||
|
|
||||||
|
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||||
|
constexpr auto parse_tail(T head, S format_str) {
|
||||||
|
if constexpr (POS != to_string_view(format_str).size()) {
|
||||||
|
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||||
|
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||||
|
unknown_format>())
|
||||||
|
return tail;
|
||||||
|
else
|
||||||
|
return make_concat(head, tail);
|
||||||
|
} else {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
|
// or unknown_format() on unrecognized input.
|
||||||
|
template <typename Args, size_t POS, int ID, typename S>
|
||||||
|
constexpr auto compile_format_string(S format_str) {
|
||||||
|
using char_type = typename S::char_type;
|
||||||
|
constexpr basic_string_view<char_type> str = format_str;
|
||||||
|
if constexpr (str[POS] == '{') {
|
||||||
|
if (POS + 1 == str.size())
|
||||||
|
throw format_error("unmatched '{' in format string");
|
||||||
|
if constexpr (str[POS + 1] == '{') {
|
||||||
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
|
} else if constexpr (str[POS + 1] == '}') {
|
||||||
|
using type = get_type<ID, Args>;
|
||||||
|
if constexpr (std::is_same<type, int>::value) {
|
||||||
|
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
||||||
|
format_str);
|
||||||
|
} else {
|
||||||
|
return unknown_format();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return unknown_format();
|
||||||
|
}
|
||||||
|
} else if constexpr (str[POS] == '}') {
|
||||||
|
if (POS + 1 == str.size())
|
||||||
|
throw format_error("unmatched '}' in format string");
|
||||||
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
|
} else {
|
||||||
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
|
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||||
|
format_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // __cpp_if_constexpr
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#if FMT_USE_CONSTEXPR
|
||||||
|
# ifdef __cpp_if_constexpr
|
||||||
|
template <typename... Args, typename S,
|
||||||
|
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
|
constexpr auto compile(S format_str) {
|
||||||
|
constexpr basic_string_view<typename S::char_type> str = format_str;
|
||||||
|
if constexpr (str.size() == 0) {
|
||||||
|
return internal::make_text(str, 0, 0);
|
||||||
|
} else {
|
||||||
|
constexpr auto result =
|
||||||
|
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
|
||||||
|
format_str);
|
||||||
|
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
||||||
|
internal::unknown_format>()) {
|
||||||
|
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CompiledFormat, typename... Args,
|
||||||
|
typename Char = typename CompiledFormat::char_type,
|
||||||
|
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
||||||
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
cf.format(std::back_inserter(buffer), args...);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
||||||
|
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
return cf.format(out, args...);
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
template <typename... Args, typename S,
|
||||||
|
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
|
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
|
||||||
|
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||||
|
}
|
||||||
|
# endif // __cpp_if_constexpr
|
||||||
|
#endif // FMT_USE_CONSTEXPR
|
||||||
|
|
||||||
|
// Compiles the format string which must be a string literal.
|
||||||
|
template <typename... Args, typename Char, size_t N>
|
||||||
|
auto compile(const Char (&format_str)[N])
|
||||||
|
-> internal::compiled_format<const Char*, Args...> {
|
||||||
|
return internal::compiled_format<const Char*, Args...>(
|
||||||
|
basic_string_view<Char>(format_str, N - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CompiledFormat, typename... Args,
|
||||||
|
typename Char = typename CompiledFormat::char_type,
|
||||||
|
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
||||||
|
CompiledFormat>::value)>
|
||||||
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
using range = buffer_range<Char>;
|
||||||
|
using context = buffer_context<Char>;
|
||||||
|
internal::cf::vformat_to<context>(range(buffer), cf,
|
||||||
|
make_format_args<context>(args...));
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
||||||
|
CompiledFormat>::value)>
|
||||||
|
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
using char_type = typename CompiledFormat::char_type;
|
||||||
|
using range = internal::output_range<OutputIt, char_type>;
|
||||||
|
using context = format_context_t<OutputIt, char_type>;
|
||||||
|
return internal::cf::vformat_to<context>(range(out), cf,
|
||||||
|
make_format_args<context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
|
||||||
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||||
|
const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
auto it =
|
||||||
|
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||||
|
return {it.base(), it.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CompiledFormat, typename... Args>
|
||||||
|
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||||
|
return format_to(internal::counting_iterator(), cf, args...).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COMPILE_H_
|
||||||
1796
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/core.h
Normal file
1796
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/core.h
Normal file
File diff suppressed because it is too large
Load diff
1403
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/format-inl.h
Normal file
1403
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/format-inl.h
Normal file
File diff suppressed because it is too large
Load diff
3648
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/format.h
Normal file
3648
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/format.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Formatting library for C++ - std::locale support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_LOCALE_H_
|
||||||
|
#define FMT_LOCALE_H_
|
||||||
|
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename Char>
|
||||||
|
typename buffer_context<Char>::iterator vformat_to(
|
||||||
|
const std::locale& loc, buffer<Char>& buf,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
using range = buffer_range<Char>;
|
||||||
|
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
||||||
|
internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
std::basic_string<Char> vformat(
|
||||||
|
const std::locale& loc, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(loc, buffer, format_str, args);
|
||||||
|
return fmt::to_string(buffer);
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const std::locale& loc, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
return internal::vformat(loc, to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> format(const std::locale& loc,
|
||||||
|
const S& format_str, Args&&... args) {
|
||||||
|
return internal::vformat(
|
||||||
|
loc, to_string_view(format_str),
|
||||||
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
|
typename Char = enable_if_t<
|
||||||
|
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||||
|
inline OutputIt vformat_to(
|
||||||
|
OutputIt out, const std::locale& loc, const S& format_str,
|
||||||
|
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
||||||
|
using range = internal::output_range<OutputIt, Char>;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
|
||||||
|
internal::is_string<S>::value)>
|
||||||
|
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||||
|
const S& format_str, Args&&... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
using context = format_context_t<OutputIt, char_t<S>>;
|
||||||
|
format_arg_store<context, Args...> as{args...};
|
||||||
|
return vformat_to(out, loc, to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_LOCALE_H_
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
// Formatting library for C++ - std::ostream support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_OSTREAM_H_
|
||||||
|
#define FMT_OSTREAM_H_
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
template <typename CHar> class basic_printf_parse_context;
|
||||||
|
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||||
|
private:
|
||||||
|
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||||
|
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
||||||
|
|
||||||
|
buffer<Char>& buffer_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The put-area is actually always empty. This makes the implementation
|
||||||
|
// simpler and has the advantage that the streambuf and the buffer are always
|
||||||
|
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||||
|
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||||
|
// to overflow. There is no disadvantage here for sputn since this always
|
||||||
|
// results in a call to xsputn.
|
||||||
|
|
||||||
|
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||||
|
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||||
|
buffer_.push_back(static_cast<Char>(ch));
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
||||||
|
buffer_.append(s, s + count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||||
|
private:
|
||||||
|
// Hide all operator<< from std::basic_ostream<Char>.
|
||||||
|
void_t<> operator<<(null<>);
|
||||||
|
void_t<> operator<<(const Char*);
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
||||||
|
!std::is_enum<T>::value)>
|
||||||
|
void_t<> operator<<(T);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||||
|
// std::ostream).
|
||||||
|
template <typename T, typename Char> class is_streamable {
|
||||||
|
private:
|
||||||
|
template <typename U>
|
||||||
|
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
||||||
|
<< std::declval<U>()),
|
||||||
|
void_t<>>::value>
|
||||||
|
test(int);
|
||||||
|
|
||||||
|
template <typename> static std::false_type test(...);
|
||||||
|
|
||||||
|
using result = decltype(test<T>(0));
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const bool value = result::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the content of buf to os.
|
||||||
|
template <typename Char>
|
||||||
|
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
|
const Char* buf_data = buf.data();
|
||||||
|
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||||
|
unsigned_streamsize size = buf.size();
|
||||||
|
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||||
|
do {
|
||||||
|
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||||
|
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||||
|
buf_data += n;
|
||||||
|
size -= n;
|
||||||
|
} while (size != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename T>
|
||||||
|
void format_value(buffer<Char>& buf, const T& value,
|
||||||
|
locale_ref loc = locale_ref()) {
|
||||||
|
formatbuf<Char> format_buf(buf);
|
||||||
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
if (loc) output.imbue(loc.get<std::locale>());
|
||||||
|
#endif
|
||||||
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
|
output << value;
|
||||||
|
buf.resize(buf.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
|
: private formatter<basic_string_view<Char>, Char> {
|
||||||
|
auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
||||||
|
}
|
||||||
|
template <typename ParseCtx,
|
||||||
|
FMT_ENABLE_IF(std::is_same<
|
||||||
|
ParseCtx, basic_printf_parse_context<Char>>::value)>
|
||||||
|
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||||
|
-> OutputIt {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
format_value(buffer, value, ctx.locale());
|
||||||
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
|
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||||
|
}
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||||
|
-> OutputIt {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
format_value(buffer, value, ctx.locale());
|
||||||
|
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||||
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
|
vprint(os, to_string_view(format_str),
|
||||||
|
internal::make_args_checked<Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_OSTREAM_H_
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
#include "os.h"
|
||||||
|
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
||||||
726
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/printf.h
Normal file
726
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/printf.h
Normal file
|
|
@ -0,0 +1,726 @@
|
||||||
|
// Formatting library for C++ - legacy printf implementation
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_PRINTF_H_
|
||||||
|
#define FMT_PRINTF_H_
|
||||||
|
|
||||||
|
#include <algorithm> // std::max
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
|
||||||
|
#include "ostream.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
|
// signed and unsigned integers.
|
||||||
|
template <bool IsSigned> struct int_checker {
|
||||||
|
template <typename T> static bool fits_in_int(T value) {
|
||||||
|
unsigned max = max_value<int>();
|
||||||
|
return value <= max;
|
||||||
|
}
|
||||||
|
static bool fits_in_int(bool) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct int_checker<true> {
|
||||||
|
template <typename T> static bool fits_in_int(T value) {
|
||||||
|
return value >= (std::numeric_limits<int>::min)() &&
|
||||||
|
value <= max_value<int>();
|
||||||
|
}
|
||||||
|
static bool fits_in_int(int) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class printf_precision_handler {
|
||||||
|
public:
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
int operator()(T value) {
|
||||||
|
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
return (std::max)(static_cast<int>(value), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
int operator()(T) {
|
||||||
|
FMT_THROW(format_error("precision is not integer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// An argument visitor that returns true iff arg is a zero integer.
|
||||||
|
class is_zero_int {
|
||||||
|
public:
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
bool operator()(T value) {
|
||||||
|
return value == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
bool operator()(T) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||||
|
|
||||||
|
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
||||||
|
|
||||||
|
template <typename T, typename Context> class arg_converter {
|
||||||
|
private:
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
|
basic_format_arg<Context>& arg_;
|
||||||
|
char_type type_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
||||||
|
: arg_(arg), type_(type) {}
|
||||||
|
|
||||||
|
void operator()(bool value) {
|
||||||
|
if (type_ != 's') operator()<bool>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
||||||
|
void operator()(U value) {
|
||||||
|
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||||
|
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
||||||
|
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||||
|
// Extra casts are used to silence warnings.
|
||||||
|
if (is_signed) {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<int>(static_cast<target_type>(value)));
|
||||||
|
} else {
|
||||||
|
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_signed) {
|
||||||
|
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||||
|
// std::printf("%lld", -42); // prints "4294967254"
|
||||||
|
// but we don't have to do the same because it's a UB.
|
||||||
|
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
|
||||||
|
} else {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
||||||
|
void operator()(U) {} // No conversion needed for non-integral types.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts an integer argument to T for printf, if T is an integral type.
|
||||||
|
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||||
|
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||||
|
// unsigned).
|
||||||
|
template <typename T, typename Context, typename Char>
|
||||||
|
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
||||||
|
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts an integer argument to char for printf.
|
||||||
|
template <typename Context> class char_converter {
|
||||||
|
private:
|
||||||
|
basic_format_arg<Context>& arg_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
void operator()(T value) {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<typename Context::char_type>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
void operator()(T) {} // No conversion needed for non-integral types.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if an argument is a valid printf width specifier and sets
|
||||||
|
// left alignment if it is negative.
|
||||||
|
template <typename Char> class printf_width_handler {
|
||||||
|
private:
|
||||||
|
using format_specs = basic_format_specs<Char>;
|
||||||
|
|
||||||
|
format_specs& specs_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
unsigned operator()(T value) {
|
||||||
|
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||||
|
if (internal::is_negative(value)) {
|
||||||
|
specs_.align = align::left;
|
||||||
|
width = 0 - width;
|
||||||
|
}
|
||||||
|
unsigned int_max = max_value<int>();
|
||||||
|
if (width > int_max) FMT_THROW(format_error("number is too big"));
|
||||||
|
return static_cast<unsigned>(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
unsigned operator()(T) {
|
||||||
|
FMT_THROW(format_error("width is not integer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Context>
|
||||||
|
void printf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
Context(std::back_inserter(buf), format, args).format();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename Context>
|
||||||
|
internal::truncating_iterator<OutputIt> printf(
|
||||||
|
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
return Context(it, format, args).format();
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
using internal::printf; // For printing into memory_buffer.
|
||||||
|
|
||||||
|
template <typename Range> class printf_arg_formatter;
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
||||||
|
using basic_format_parse_context<Char>::basic_format_parse_context;
|
||||||
|
};
|
||||||
|
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
The ``printf`` argument formatter.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Range>
|
||||||
|
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
||||||
|
public:
|
||||||
|
using iterator = typename Range::iterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using char_type = typename Range::value_type;
|
||||||
|
using base = internal::arg_formatter_base<Range>;
|
||||||
|
using context_type = basic_printf_context<iterator, char_type>;
|
||||||
|
|
||||||
|
context_type& context_;
|
||||||
|
|
||||||
|
void write_null_pointer(char) {
|
||||||
|
this->specs()->type = 0;
|
||||||
|
this->write("(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_null_pointer(wchar_t) {
|
||||||
|
this->specs()->type = 0;
|
||||||
|
this->write(L"(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using format_specs = typename base::format_specs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an argument formatter object.
|
||||||
|
*buffer* is a reference to the output buffer and *specs* contains format
|
||||||
|
specifier information for standard argument types.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
||||||
|
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
|
||||||
|
iterator operator()(T value) {
|
||||||
|
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
||||||
|
// use std::is_same instead.
|
||||||
|
if (std::is_same<T, bool>::value) {
|
||||||
|
format_specs& fmt_specs = *this->specs();
|
||||||
|
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
|
||||||
|
fmt_specs.type = 0;
|
||||||
|
this->write(value != 0);
|
||||||
|
} else if (std::is_same<T, char_type>::value) {
|
||||||
|
format_specs& fmt_specs = *this->specs();
|
||||||
|
if (fmt_specs.type && fmt_specs.type != 'c')
|
||||||
|
return (*this)(static_cast<int>(value));
|
||||||
|
fmt_specs.sign = sign::none;
|
||||||
|
fmt_specs.alt = false;
|
||||||
|
fmt_specs.align = align::right;
|
||||||
|
return base::operator()(value);
|
||||||
|
} else {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||||
|
iterator operator()(T value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated C string. */
|
||||||
|
iterator operator()(const char* value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->specs()->type == 'p')
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
else
|
||||||
|
this->write("(null)");
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated wide C string. */
|
||||||
|
iterator operator()(const wchar_t* value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->specs()->type == 'p')
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
else
|
||||||
|
this->write(L"(null)");
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator()(basic_string_view<char_type> value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator()(monostate value) { return base::operator()(value); }
|
||||||
|
|
||||||
|
/** Formats a pointer. */
|
||||||
|
iterator operator()(const void* value) {
|
||||||
|
if (value) return base::operator()(value);
|
||||||
|
this->specs()->type = 0;
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
|
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||||
|
handle.format(context_.parse_context(), context_);
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct printf_formatter {
|
||||||
|
printf_formatter() = delete;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
internal::format_value(internal::get_container(ctx.out()), value);
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This template formats data and writes the output to a writer. */
|
||||||
|
template <typename OutputIt, typename Char> class basic_printf_context {
|
||||||
|
public:
|
||||||
|
/** The character type for the output. */
|
||||||
|
using char_type = Char;
|
||||||
|
using iterator = OutputIt;
|
||||||
|
using format_arg = basic_format_arg<basic_printf_context>;
|
||||||
|
using parse_context_type = basic_printf_parse_context<Char>;
|
||||||
|
template <typename T> using formatter_type = printf_formatter<T>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using format_specs = basic_format_specs<char_type>;
|
||||||
|
|
||||||
|
OutputIt out_;
|
||||||
|
basic_format_args<basic_printf_context> args_;
|
||||||
|
parse_context_type parse_ctx_;
|
||||||
|
|
||||||
|
static void parse_flags(format_specs& specs, const Char*& it,
|
||||||
|
const Char* end);
|
||||||
|
|
||||||
|
// Returns the argument with specified index or, if arg_index is -1, the next
|
||||||
|
// argument.
|
||||||
|
format_arg get_arg(int arg_index = -1);
|
||||||
|
|
||||||
|
// Parses argument index, flags and width and returns the argument index.
|
||||||
|
int parse_header(const Char*& it, const Char* end, format_specs& specs);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a ``printf_context`` object. References to the arguments and
|
||||||
|
the writer are stored in the context object so make sure they have
|
||||||
|
appropriate lifetimes.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||||
|
basic_format_args<basic_printf_context> args)
|
||||||
|
: out_(out), args_(args), parse_ctx_(format_str) {}
|
||||||
|
|
||||||
|
OutputIt out() { return out_; }
|
||||||
|
void advance_to(OutputIt it) { out_ = it; }
|
||||||
|
|
||||||
|
internal::locale_ref locale() { return {}; }
|
||||||
|
|
||||||
|
format_arg arg(int id) const { return args_.get(id); }
|
||||||
|
|
||||||
|
parse_context_type& parse_context() { return parse_ctx_; }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
|
parse_ctx_.on_error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats stored arguments and writes the output to the range. */
|
||||||
|
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
|
||||||
|
OutputIt format();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char>
|
||||||
|
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
||||||
|
const Char*& it,
|
||||||
|
const Char* end) {
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
switch (*it) {
|
||||||
|
case '-':
|
||||||
|
specs.align = align::left;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
specs.sign = sign::plus;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
specs.fill[0] = '0';
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
specs.sign = sign::space;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
specs.alt = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char>
|
||||||
|
typename basic_printf_context<OutputIt, Char>::format_arg
|
||||||
|
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
|
||||||
|
if (arg_index < 0)
|
||||||
|
arg_index = parse_ctx_.next_arg_id();
|
||||||
|
else
|
||||||
|
parse_ctx_.check_arg_id(--arg_index);
|
||||||
|
return internal::get_arg(*this, arg_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char>
|
||||||
|
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
||||||
|
const Char* end,
|
||||||
|
format_specs& specs) {
|
||||||
|
int arg_index = -1;
|
||||||
|
char_type c = *it;
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
|
// preceded with '0' flag(s).
|
||||||
|
internal::error_handler eh;
|
||||||
|
int value = parse_nonnegative_int(it, end, eh);
|
||||||
|
if (it != end && *it == '$') { // value is an argument index
|
||||||
|
++it;
|
||||||
|
arg_index = value;
|
||||||
|
} else {
|
||||||
|
if (c == '0') specs.fill[0] = '0';
|
||||||
|
if (value != 0) {
|
||||||
|
// Nonzero value means that we parsed width and don't need to
|
||||||
|
// parse it or flags again, so return now.
|
||||||
|
specs.width = value;
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse_flags(specs, it, end);
|
||||||
|
// Parse width.
|
||||||
|
if (it != end) {
|
||||||
|
if (*it >= '0' && *it <= '9') {
|
||||||
|
internal::error_handler eh;
|
||||||
|
specs.width = parse_nonnegative_int(it, end, eh);
|
||||||
|
} else if (*it == '*') {
|
||||||
|
++it;
|
||||||
|
specs.width = static_cast<int>(visit_format_arg(
|
||||||
|
internal::printf_width_handler<char_type>(specs), get_arg()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char>
|
||||||
|
template <typename ArgFormatter>
|
||||||
|
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
|
auto out = this->out();
|
||||||
|
const Char* start = parse_ctx_.begin();
|
||||||
|
const Char* end = parse_ctx_.end();
|
||||||
|
auto it = start;
|
||||||
|
while (it != end) {
|
||||||
|
char_type c = *it++;
|
||||||
|
if (c != '%') continue;
|
||||||
|
if (it != end && *it == c) {
|
||||||
|
out = std::copy(start, it, out);
|
||||||
|
start = ++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out = std::copy(start, it - 1, out);
|
||||||
|
|
||||||
|
format_specs specs;
|
||||||
|
specs.align = align::right;
|
||||||
|
|
||||||
|
// Parse argument index, flags and width.
|
||||||
|
int arg_index = parse_header(it, end, specs);
|
||||||
|
if (arg_index == 0) on_error("argument index out of range");
|
||||||
|
|
||||||
|
// Parse precision.
|
||||||
|
if (it != end && *it == '.') {
|
||||||
|
++it;
|
||||||
|
c = it != end ? *it : 0;
|
||||||
|
if ('0' <= c && c <= '9') {
|
||||||
|
internal::error_handler eh;
|
||||||
|
specs.precision = parse_nonnegative_int(it, end, eh);
|
||||||
|
} else if (c == '*') {
|
||||||
|
++it;
|
||||||
|
specs.precision = static_cast<int>(
|
||||||
|
visit_format_arg(internal::printf_precision_handler(), get_arg()));
|
||||||
|
} else {
|
||||||
|
specs.precision = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format_arg arg = get_arg(arg_index);
|
||||||
|
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
|
||||||
|
specs.alt = false;
|
||||||
|
if (specs.fill[0] == '0') {
|
||||||
|
if (arg.is_arithmetic())
|
||||||
|
specs.align = align::numeric;
|
||||||
|
else
|
||||||
|
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse length and convert the argument to the required type.
|
||||||
|
c = it != end ? *it++ : 0;
|
||||||
|
char_type t = it != end ? *it : 0;
|
||||||
|
using internal::convert_arg;
|
||||||
|
switch (c) {
|
||||||
|
case 'h':
|
||||||
|
if (t == 'h') {
|
||||||
|
++it;
|
||||||
|
t = it != end ? *it : 0;
|
||||||
|
convert_arg<signed char>(arg, t);
|
||||||
|
} else {
|
||||||
|
convert_arg<short>(arg, t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (t == 'l') {
|
||||||
|
++it;
|
||||||
|
t = it != end ? *it : 0;
|
||||||
|
convert_arg<long long>(arg, t);
|
||||||
|
} else {
|
||||||
|
convert_arg<long>(arg, t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
convert_arg<intmax_t>(arg, t);
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
convert_arg<std::size_t>(arg, t);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
convert_arg<std::ptrdiff_t>(arg, t);
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
// printf produces garbage when 'L' is omitted for long double, no
|
||||||
|
// need to do the same.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--it;
|
||||||
|
convert_arg<void>(arg, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse type.
|
||||||
|
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||||
|
specs.type = static_cast<char>(*it++);
|
||||||
|
if (arg.is_integral()) {
|
||||||
|
// Normalize type.
|
||||||
|
switch (specs.type) {
|
||||||
|
case 'i':
|
||||||
|
case 'u':
|
||||||
|
specs.type = 'd';
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
|
||||||
|
arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = it;
|
||||||
|
|
||||||
|
// Format argument.
|
||||||
|
visit_format_arg(ArgFormatter(out, specs, *this), arg);
|
||||||
|
}
|
||||||
|
return std::copy(start, it, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
using basic_printf_context_t =
|
||||||
|
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
|
||||||
|
Char>;
|
||||||
|
|
||||||
|
using printf_context = basic_printf_context_t<char>;
|
||||||
|
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||||
|
|
||||||
|
using printf_args = basic_format_args<printf_context>;
|
||||||
|
using wprintf_args = basic_format_args<wprintf_context>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||||
|
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline format_arg_store<printf_context, Args...> make_printf_args(
|
||||||
|
const Args&... args) {
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||||
|
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
||||||
|
const Args&... args) {
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> vsprintf(
|
||||||
|
const S& format,
|
||||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(format), args);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments and returns the result as a string.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||||
|
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
||||||
|
using context = basic_printf_context_t<Char>;
|
||||||
|
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
inline int vfprintf(
|
||||||
|
std::FILE* f, const S& format,
|
||||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(format), args);
|
||||||
|
std::size_t size = buffer.size();
|
||||||
|
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||||
|
? -1
|
||||||
|
: static_cast<int>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the file *f*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||||
|
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
||||||
|
using context = basic_printf_context_t<Char>;
|
||||||
|
return vfprintf(f, to_string_view(format),
|
||||||
|
make_format_args<context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
inline int vprintf(
|
||||||
|
const S& format,
|
||||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
|
return vfprintf(stdout, to_string_view(format), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to ``stdout``.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||||
|
inline int printf(const S& format_str, const Args&... args) {
|
||||||
|
using context = basic_printf_context_t<char_t<S>>;
|
||||||
|
return vprintf(to_string_view(format_str),
|
||||||
|
make_format_args<context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
inline int vfprintf(
|
||||||
|
std::basic_ostream<Char>& os, const S& format,
|
||||||
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(format), args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
return static_cast<int>(buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats arguments and writes the output to the range. */
|
||||||
|
template <typename ArgFormatter, typename Char,
|
||||||
|
typename Context =
|
||||||
|
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
||||||
|
typename ArgFormatter::iterator vprintf(
|
||||||
|
internal::buffer<Char>& out, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<type_identity_t<Context>> args) {
|
||||||
|
typename ArgFormatter::iterator iter(out);
|
||||||
|
Context(iter, format_str, args).template format<ArgFormatter>();
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(cerr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
|
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
|
using context = basic_printf_context_t<Char>;
|
||||||
|
return vfprintf(os, to_string_view(format_str),
|
||||||
|
make_format_args<context>(args...));
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_PRINTF_H_
|
||||||
387
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/ranges.h
Normal file
387
f4mp_originalcode/F4MPClient/include/spdlog/fmt/bundled/ranges.h
Normal file
|
|
@ -0,0 +1,387 @@
|
||||||
|
// Formatting library for C++ - experimental range support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||||
|
// All Rights Reserved
|
||||||
|
// {fmt} support for ranges, containers and types tuple interface.
|
||||||
|
|
||||||
|
#ifndef FMT_RANGES_H_
|
||||||
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
// output only up to N items from the range.
|
||||||
|
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
||||||
|
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
template <typename Char> struct formatting_base {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Enable = void>
|
||||||
|
struct formatting_range : formatting_base<Char> {
|
||||||
|
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||||
|
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||||
|
// range.
|
||||||
|
Char prefix;
|
||||||
|
Char delimiter;
|
||||||
|
Char postfix;
|
||||||
|
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Enable = void>
|
||||||
|
struct formatting_tuple : formatting_base<Char> {
|
||||||
|
Char prefix;
|
||||||
|
Char delimiter;
|
||||||
|
Char postfix;
|
||||||
|
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename RangeT, typename OutputIterator>
|
||||||
|
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||||
|
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||||
|
*out++ = *it;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator>
|
||||||
|
OutputIterator copy(const char* str, OutputIterator out) {
|
||||||
|
while (*str) *out++ = *str++;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator>
|
||||||
|
OutputIterator copy(char ch, OutputIterator out) {
|
||||||
|
*out++ = ch;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true value if T has std::string interface, like std::string_view.
|
||||||
|
template <typename T> class is_like_std_string {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U* p)
|
||||||
|
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... Ts> struct conditional_helper {};
|
||||||
|
|
||||||
|
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||||
|
template <typename T>
|
||||||
|
struct is_range_<
|
||||||
|
T, conditional_t<false,
|
||||||
|
conditional_helper<decltype(std::declval<T>().begin()),
|
||||||
|
decltype(std::declval<T>().end())>,
|
||||||
|
void>> : std::true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// tuple_size and tuple_element check.
|
||||||
|
template <typename T> class is_tuple_like_ {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for integer_sequence
|
||||||
|
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||||
|
template <typename T, T... N>
|
||||||
|
using integer_sequence = std::integer_sequence<T, N...>;
|
||||||
|
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||||
|
template <std::size_t N>
|
||||||
|
using make_index_sequence = std::make_index_sequence<N>;
|
||||||
|
#else
|
||||||
|
template <typename T, T... N> struct integer_sequence {
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t... N>
|
||||||
|
using index_sequence = integer_sequence<std::size_t, N...>;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N, T... Ns>
|
||||||
|
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||||
|
template <typename T, T... Ns>
|
||||||
|
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class Tuple, class F, size_t... Is>
|
||||||
|
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
|
||||||
|
using std::get;
|
||||||
|
// using free function get<I>(T) now.
|
||||||
|
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||||
|
(void)_; // blocks warnings
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
||||||
|
T const&) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||||
|
const auto indexes = get_indexes(tup);
|
||||||
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||||
|
typename std::decay<Arg>::type>::value)>
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||||
|
return add_space ? " {}" : "{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
||||||
|
typename std::decay<Arg>::type>::value)>
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||||
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
||||||
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||||
|
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||||
|
return add_space ? " '{}'" : "'{}'";
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||||
|
return add_space ? L" '{}'" : L"'{}'";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename T> struct is_tuple_like {
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TupleT, typename Char>
|
||||||
|
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||||
|
private:
|
||||||
|
// C++11 generic lambda for format()
|
||||||
|
template <typename FormatContext> struct format_each {
|
||||||
|
template <typename T> void operator()(const T& v) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
out = internal::copy(formatting.delimiter, out);
|
||||||
|
}
|
||||||
|
out = format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), v),
|
||||||
|
v);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatting_tuple<Char>& formatting;
|
||||||
|
std::size_t& i;
|
||||||
|
typename std::add_lvalue_reference<decltype(
|
||||||
|
std::declval<FormatContext>().out())>::type out;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatting_tuple<Char> formatting;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext = format_context>
|
||||||
|
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
std::size_t i = 0;
|
||||||
|
internal::copy(formatting.prefix, out);
|
||||||
|
|
||||||
|
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.postfix, out);
|
||||||
|
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Char> struct is_range {
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
internal::is_range_<T>::value &&
|
||||||
|
!internal::is_like_std_string<T>::value &&
|
||||||
|
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||||
|
!std::is_constructible<internal::std_string_view<Char>, T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename RangeT, typename Char>
|
||||||
|
struct formatter<RangeT, Char,
|
||||||
|
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
||||||
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
typename FormatContext::iterator format(const RangeT& values,
|
||||||
|
FormatContext& ctx) {
|
||||||
|
auto out = internal::copy(formatting.prefix, ctx.out());
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||||
|
out = internal::copy(formatting.delimiter, out);
|
||||||
|
}
|
||||||
|
out = format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||||
|
*it);
|
||||||
|
if (++i > formatting.range_length_limit) {
|
||||||
|
out = format_to(out, " ... <other elements>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||||
|
return internal::copy(formatting.postfix, out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
|
||||||
|
const std::tuple<T...>& tuple;
|
||||||
|
basic_string_view<Char> sep;
|
||||||
|
|
||||||
|
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||||
|
: tuple{t}, sep{s} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename... T>
|
||||||
|
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
typename FormatContext::iterator format(
|
||||||
|
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
||||||
|
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename FormatContext, size_t... N>
|
||||||
|
typename FormatContext::iterator format(
|
||||||
|
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||||
|
internal::index_sequence<N...>) {
|
||||||
|
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
typename FormatContext::iterator format_args(
|
||||||
|
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
||||||
|
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||||
|
// can be replaced with a constexpr branch in the variadic overload.
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext, typename Arg, typename... Args>
|
||||||
|
typename FormatContext::iterator format_args(
|
||||||
|
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||||
|
const Arg& arg, const Args&... args) {
|
||||||
|
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||||
|
auto out = ctx.out();
|
||||||
|
out = base{}.format(arg, ctx);
|
||||||
|
if (sizeof...(Args) > 0) {
|
||||||
|
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||||
|
ctx.advance_to(out);
|
||||||
|
return format_args(value, ctx, args...);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::tuple<int, char> t = {1, 'a'};
|
||||||
|
fmt::print("{}", fmt::join(t, ", "));
|
||||||
|
// Output: "1, a"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... T>
|
||||||
|
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
||||||
|
string_view sep) {
|
||||||
|
return {tuple, sep};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||||
|
wstring_view sep) {
|
||||||
|
return {tuple, sep};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns an object that formats `initializer_list` with elements separated by
|
||||||
|
`sep`.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||||
|
// Output: "1, 2, 3"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
|
||||||
|
std::initializer_list<T> list, string_view sep) {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
|
||||||
|
std::initializer_list<T> list, wstring_view sep) {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_RANGES_H_
|
||||||
25
f4mp_originalcode/F4MPClient/include/spdlog/fmt/fmt.h
Normal file
25
f4mp_originalcode/F4MPClient/include/spdlog/fmt/fmt.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// Copyright(c) 2016-2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//
|
||||||
|
// Include a bundled header-only copy of fmtlib or an external one.
|
||||||
|
// By default spdlog include its own copy.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||||
|
#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
|
||||||
|
#define FMT_HEADER_ONLY
|
||||||
|
#endif
|
||||||
|
#ifndef FMT_USE_WINDOWS_H
|
||||||
|
#define FMT_USE_WINDOWS_H 0
|
||||||
|
#endif
|
||||||
|
#include <spdlog/fmt/bundled/core.h>
|
||||||
|
#include <spdlog/fmt/bundled/format.h>
|
||||||
|
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#endif
|
||||||
20
f4mp_originalcode/F4MPClient/include/spdlog/fmt/ostr.h
Normal file
20
f4mp_originalcode/F4MPClient/include/spdlog/fmt/ostr.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// Copyright(c) 2016 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
//
|
||||||
|
// include bundled or external copy of fmtlib's ostream support
|
||||||
|
//
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#ifndef FMT_HEADER_ONLY
|
||||||
|
#define FMT_HEADER_ONLY
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#include <spdlog/fmt/bundled/ostream.h>
|
||||||
|
#else
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
#endif
|
||||||
18
f4mp_originalcode/F4MPClient/include/spdlog/formatter.h
Normal file
18
f4mp_originalcode/F4MPClient/include/spdlog/formatter.h
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
class formatter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~formatter() = default;
|
||||||
|
virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0;
|
||||||
|
virtual std::unique_ptr<formatter> clone() const = 0;
|
||||||
|
};
|
||||||
|
} // namespace spdlog
|
||||||
14
f4mp_originalcode/F4MPClient/include/spdlog/fwd.h
Normal file
14
f4mp_originalcode/F4MPClient/include/spdlog/fwd.h
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
class logger;
|
||||||
|
class formatter;
|
||||||
|
|
||||||
|
namespace sinks {
|
||||||
|
class sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
253
f4mp_originalcode/F4MPClient/include/spdlog/logger-inl.h
Normal file
253
f4mp_originalcode/F4MPClient/include/spdlog/logger-inl.h
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/logger.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/sinks/sink.h>
|
||||||
|
#include <spdlog/details/backtracer.h>
|
||||||
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
// public methods
|
||||||
|
SPDLOG_INLINE logger::logger(const logger &other)
|
||||||
|
: name_(other.name_)
|
||||||
|
, sinks_(other.sinks_)
|
||||||
|
, level_(other.level_.load(std::memory_order_relaxed))
|
||||||
|
, flush_level_(other.flush_level_.load(std::memory_order_relaxed))
|
||||||
|
, custom_err_handler_(other.custom_err_handler_)
|
||||||
|
, tracer_(other.tracer_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT : name_(std::move(other.name_)),
|
||||||
|
sinks_(std::move(other.sinks_)),
|
||||||
|
level_(other.level_.load(std::memory_order_relaxed)),
|
||||||
|
flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
|
||||||
|
custom_err_handler_(std::move(other.custom_err_handler_)),
|
||||||
|
tracer_(std::move(other.tracer_))
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE logger &logger::operator=(logger other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
this->swap(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
name_.swap(other.name_);
|
||||||
|
sinks_.swap(other.sinks_);
|
||||||
|
|
||||||
|
// swap level_
|
||||||
|
auto other_level = other.level_.load();
|
||||||
|
auto my_level = level_.exchange(other_level);
|
||||||
|
other.level_.store(my_level);
|
||||||
|
|
||||||
|
// swap flush level_
|
||||||
|
other_level = other.flush_level_.load();
|
||||||
|
my_level = flush_level_.exchange(other_level);
|
||||||
|
other.flush_level_.store(my_level);
|
||||||
|
|
||||||
|
custom_err_handler_.swap(other.custom_err_handler_);
|
||||||
|
std::swap(tracer_, other.tracer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void swap(logger &a, logger &b)
|
||||||
|
{
|
||||||
|
a.swap(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::set_level(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
level_.store(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE level::level_enum logger::level() const
|
||||||
|
{
|
||||||
|
return static_cast<level::level_enum>(level_.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE const std::string &logger::name() const
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set formatting for the sinks in this logger.
|
||||||
|
// each sink will get a separate instance of the formatter object.
|
||||||
|
SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
|
||||||
|
{
|
||||||
|
for (auto it = sinks_.begin(); it != sinks_.end(); ++it)
|
||||||
|
{
|
||||||
|
if (std::next(it) == sinks_.end())
|
||||||
|
{
|
||||||
|
// last element - we can be move it.
|
||||||
|
(*it)->set_formatter(std::move(f));
|
||||||
|
break; // to prevent clang-tidy warning
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(*it)->set_formatter(f->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type)
|
||||||
|
{
|
||||||
|
auto new_formatter = details::make_unique<pattern_formatter>(std::move(pattern), time_type);
|
||||||
|
set_formatter(std::move(new_formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new backtrace sink and move to it all our child sinks
|
||||||
|
SPDLOG_INLINE void logger::enable_backtrace(size_t n_messages)
|
||||||
|
{
|
||||||
|
tracer_.enable(n_messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore orig sinks and level and delete the backtrace sink
|
||||||
|
SPDLOG_INLINE void logger::disable_backtrace()
|
||||||
|
{
|
||||||
|
tracer_.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::dump_backtrace()
|
||||||
|
{
|
||||||
|
dump_backtrace_();
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush functions
|
||||||
|
SPDLOG_INLINE void logger::flush()
|
||||||
|
{
|
||||||
|
flush_();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::flush_on(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
flush_level_.store(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE level::level_enum logger::flush_level() const
|
||||||
|
{
|
||||||
|
return static_cast<level::level_enum>(flush_level_.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
// sinks
|
||||||
|
SPDLOG_INLINE const std::vector<sink_ptr> &logger::sinks() const
|
||||||
|
{
|
||||||
|
return sinks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::vector<sink_ptr> &logger::sinks()
|
||||||
|
{
|
||||||
|
return sinks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
SPDLOG_INLINE void logger::set_error_handler(err_handler handler)
|
||||||
|
{
|
||||||
|
custom_err_handler_ = std::move(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new logger with same sinks and configuration.
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
|
||||||
|
{
|
||||||
|
auto cloned = std::make_shared<logger>(*this);
|
||||||
|
cloned->name_ = std::move(logger_name);
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
// protected methods
|
||||||
|
SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg, bool log_enabled, bool traceback_enabled)
|
||||||
|
{
|
||||||
|
if (log_enabled)
|
||||||
|
{
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
if (traceback_enabled)
|
||||||
|
{
|
||||||
|
tracer_.push_back(log_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
if (sink->should_log(msg.level))
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
sink->log(msg);
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_flush_(msg))
|
||||||
|
{
|
||||||
|
flush_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::flush_()
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::dump_backtrace_()
|
||||||
|
{
|
||||||
|
using details::log_msg;
|
||||||
|
if (tracer_.enabled())
|
||||||
|
{
|
||||||
|
sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"});
|
||||||
|
tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); });
|
||||||
|
sink_it_(log_msg{name(), level::info, "****************** Backtrace End ********************"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
||||||
|
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
|
||||||
|
{
|
||||||
|
if (custom_err_handler_)
|
||||||
|
{
|
||||||
|
custom_err_handler_(msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using std::chrono::system_clock;
|
||||||
|
static std::mutex mutex;
|
||||||
|
static std::chrono::system_clock::time_point last_report_time;
|
||||||
|
static size_t err_counter = 0;
|
||||||
|
std::lock_guard<std::mutex> lk{mutex};
|
||||||
|
auto now = system_clock::now();
|
||||||
|
err_counter++;
|
||||||
|
if (now - last_report_time < std::chrono::seconds(1))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_report_time = now;
|
||||||
|
auto tm_time = details::os::localtime(system_clock::to_time_t(now));
|
||||||
|
char date_buf[64];
|
||||||
|
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||||
|
fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
366
f4mp_originalcode/F4MPClient/include/spdlog/logger.h
Normal file
366
f4mp_originalcode/F4MPClient/include/spdlog/logger.h
Normal file
|
|
@ -0,0 +1,366 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Thread safe logger (except for set_error_handler())
|
||||||
|
// Has name, log level, vector of std::shared sink pointers and formatter
|
||||||
|
// Upon each log write the logger:
|
||||||
|
// 1. Checks if its log level is enough to log the message and if yes:
|
||||||
|
// 2. Call the underlying sinks to do the job.
|
||||||
|
// 3. Each sink use its own private copy of a formatter to format the message
|
||||||
|
// and send to its destination.
|
||||||
|
//
|
||||||
|
// The use of private formatter per sink provides the opportunity to cache some
|
||||||
|
// formatted data, and support for different format per sink.
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
#include <spdlog/details/backtracer.h>
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#ifndef SPDLOG_NO_EXCEPTIONS
|
||||||
|
#define SPDLOG_LOGGER_CATCH() \
|
||||||
|
catch (const std::exception &ex) \
|
||||||
|
{ \
|
||||||
|
err_handler_(ex.what()); \
|
||||||
|
} \
|
||||||
|
catch (...) \
|
||||||
|
{ \
|
||||||
|
err_handler_("Unknown exception in logger"); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_CATCH()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
class SPDLOG_API logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Empty logger
|
||||||
|
explicit logger(std::string name)
|
||||||
|
: name_(std::move(name))
|
||||||
|
, sinks_()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Logger with range on sinks
|
||||||
|
template<typename It>
|
||||||
|
logger(std::string name, It begin, It end)
|
||||||
|
: name_(std::move(name))
|
||||||
|
, sinks_(begin, end)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Logger with single sink
|
||||||
|
logger(std::string name, sink_ptr single_sink)
|
||||||
|
: logger(std::move(name), {std::move(single_sink)})
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Logger with sinks init list
|
||||||
|
logger(std::string name, sinks_init_list sinks)
|
||||||
|
: logger(std::move(name), sinks.begin(), sinks.end())
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~logger() = default;
|
||||||
|
|
||||||
|
logger(const logger &other);
|
||||||
|
logger(logger &&other) SPDLOG_NOEXCEPT;
|
||||||
|
logger &operator=(logger other) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
// FormatString is a type derived from fmt::compile_string
|
||||||
|
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log_(loc, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
|
||||||
|
template<typename... Args>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log_(loc, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
void log(level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
void trace(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::trace, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
void debug(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::debug, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
void info(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::info, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
void warn(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::warn, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
void error(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::err, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
void critical(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::critical, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// T can be statically converted to string_view and isn't a fmt::compile_string
|
||||||
|
template<class T, typename std::enable_if<
|
||||||
|
std::is_convertible<const T &, spdlog::string_view_t>::value && !fmt::is_compile_string<T>::value, int>::type = 0>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(loc, lvl, string_view_t{msg});
|
||||||
|
}
|
||||||
|
|
||||||
|
void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg)
|
||||||
|
{
|
||||||
|
bool log_enabled = should_log(lvl);
|
||||||
|
bool traceback_enabled = tracer_.enabled();
|
||||||
|
if (!log_enabled && !traceback_enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
details::log_msg log_msg(log_time, loc, name_, lvl, msg);
|
||||||
|
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log(source_loc loc, level::level_enum lvl, string_view_t msg)
|
||||||
|
{
|
||||||
|
bool log_enabled = should_log(lvl);
|
||||||
|
bool traceback_enabled = tracer_.enabled();
|
||||||
|
if (!log_enabled && !traceback_enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
details::log_msg log_msg(loc, name_, lvl, msg);
|
||||||
|
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log(level::level_enum lvl, string_view_t msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// T cannot be statically converted to string_view or wstring_view
|
||||||
|
template<class T, typename std::enable_if<!std::is_convertible<const T &, spdlog::string_view_t>::value &&
|
||||||
|
!is_convertible_to_wstring_view<const T &>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(loc, lvl, "{}", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void trace(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::trace, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void debug(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::debug, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void info(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::info, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void warn(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::warn, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void error(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::err, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void critical(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::critical, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
#ifndef _WIN32
|
||||||
|
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||||
|
#else
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
bool log_enabled = should_log(lvl);
|
||||||
|
bool traceback_enabled = tracer_.enabled();
|
||||||
|
if (!log_enabled && !traceback_enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
// format to wmemory_buffer and convert to utf8
|
||||||
|
fmt::wmemory_buffer wbuf;
|
||||||
|
fmt::format_to(wbuf, fmt, args...);
|
||||||
|
|
||||||
|
memory_buf_t buf;
|
||||||
|
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
|
||||||
|
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||||
|
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
|
||||||
|
// T can be statically converted to wstring_view
|
||||||
|
template<class T, typename std::enable_if<is_convertible_to_wstring_view<const T &>::value, int>::type = 0>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
bool log_enabled = should_log(lvl);
|
||||||
|
bool traceback_enabled = tracer_.enabled();
|
||||||
|
if (!log_enabled && !traceback_enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
memory_buf_t buf;
|
||||||
|
details::os::wstr_to_utf8buf(msg, buf);
|
||||||
|
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||||
|
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
|
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
|
||||||
|
// return true logging is enabled for the given level.
|
||||||
|
bool should_log(level::level_enum msg_level) const
|
||||||
|
{
|
||||||
|
return msg_level >= level_.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return true if backtrace logging is enabled.
|
||||||
|
bool should_backtrace() const
|
||||||
|
{
|
||||||
|
return tracer_.enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_level(level::level_enum log_level);
|
||||||
|
|
||||||
|
level::level_enum level() const;
|
||||||
|
|
||||||
|
const std::string &name() const;
|
||||||
|
|
||||||
|
// set formatting for the sinks in this logger.
|
||||||
|
// each sink will get a separate instance of the formatter object.
|
||||||
|
void set_formatter(std::unique_ptr<formatter> f);
|
||||||
|
|
||||||
|
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
|
||||||
|
|
||||||
|
// backtrace support.
|
||||||
|
// efficiently store all debug/trace messages in a circular buffer until needed for debugging.
|
||||||
|
void enable_backtrace(size_t n_messages);
|
||||||
|
void disable_backtrace();
|
||||||
|
void dump_backtrace();
|
||||||
|
|
||||||
|
// flush functions
|
||||||
|
void flush();
|
||||||
|
void flush_on(level::level_enum log_level);
|
||||||
|
level::level_enum flush_level() const;
|
||||||
|
|
||||||
|
// sinks
|
||||||
|
const std::vector<sink_ptr> &sinks() const;
|
||||||
|
|
||||||
|
std::vector<sink_ptr> &sinks();
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
void set_error_handler(err_handler);
|
||||||
|
|
||||||
|
// create new logger with same sinks and configuration.
|
||||||
|
virtual std::shared_ptr<logger> clone(std::string logger_name);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string name_;
|
||||||
|
std::vector<sink_ptr> sinks_;
|
||||||
|
spdlog::level_t level_{level::info};
|
||||||
|
spdlog::level_t flush_level_{level::off};
|
||||||
|
err_handler custom_err_handler_{nullptr};
|
||||||
|
details::backtracer tracer_;
|
||||||
|
|
||||||
|
// common implementation for after templated public api has been resolved
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
bool log_enabled = should_log(lvl);
|
||||||
|
bool traceback_enabled = tracer_.enabled();
|
||||||
|
if (!log_enabled && !traceback_enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
memory_buf_t buf;
|
||||||
|
fmt::format_to(buf, fmt, args...);
|
||||||
|
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||||
|
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
|
||||||
|
// log the given message (if the given log level is high enough),
|
||||||
|
// and save backtrace (if backtrace is enabled).
|
||||||
|
void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
|
||||||
|
virtual void sink_it_(const details::log_msg &msg);
|
||||||
|
virtual void flush_();
|
||||||
|
void dump_backtrace_();
|
||||||
|
bool should_flush_(const details::log_msg &msg);
|
||||||
|
|
||||||
|
// handle errors during logging.
|
||||||
|
// default handler prints the error to stderr at max rate of 1 message/sec.
|
||||||
|
void err_handler_(const std::string &msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
void swap(logger &a, logger &b);
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "logger-inl.h"
|
||||||
|
#endif
|
||||||
1358
f4mp_originalcode/F4MPClient/include/spdlog/pattern_formatter-inl.h
Normal file
1358
f4mp_originalcode/F4MPClient/include/spdlog/pattern_formatter-inl.h
Normal file
File diff suppressed because it is too large
Load diff
126
f4mp_originalcode/F4MPClient/include/spdlog/pattern_formatter.h
Normal file
126
f4mp_originalcode/F4MPClient/include/spdlog/pattern_formatter.h
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
#include <spdlog/formatter.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
// padding information.
|
||||||
|
struct padding_info
|
||||||
|
{
|
||||||
|
enum pad_side
|
||||||
|
{
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
center
|
||||||
|
};
|
||||||
|
|
||||||
|
padding_info() = default;
|
||||||
|
padding_info(size_t width, padding_info::pad_side side, bool truncate)
|
||||||
|
: width_(width)
|
||||||
|
, side_(side)
|
||||||
|
, truncate_(truncate)
|
||||||
|
, enabled_(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool enabled() const
|
||||||
|
{
|
||||||
|
return enabled_;
|
||||||
|
}
|
||||||
|
size_t width_ = 0;
|
||||||
|
pad_side side_ = left;
|
||||||
|
bool truncate_ = false;
|
||||||
|
bool enabled_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SPDLOG_API flag_formatter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit flag_formatter(padding_info padinfo)
|
||||||
|
: padinfo_(padinfo)
|
||||||
|
{}
|
||||||
|
flag_formatter() = default;
|
||||||
|
virtual ~flag_formatter() = default;
|
||||||
|
virtual void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
padding_info padinfo_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
class SPDLOG_API custom_flag_formatter : public details::flag_formatter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
|
||||||
|
|
||||||
|
void set_padding_info(details::padding_info padding)
|
||||||
|
{
|
||||||
|
flag_formatter::padinfo_ = padding;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SPDLOG_API pattern_formatter final : public formatter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
|
||||||
|
|
||||||
|
explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local,
|
||||||
|
std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags());
|
||||||
|
|
||||||
|
// use default pattern is not given
|
||||||
|
explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
|
||||||
|
|
||||||
|
pattern_formatter(const pattern_formatter &other) = delete;
|
||||||
|
pattern_formatter &operator=(const pattern_formatter &other) = delete;
|
||||||
|
|
||||||
|
std::unique_ptr<formatter> clone() const override;
|
||||||
|
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
pattern_formatter &add_flag(char flag, const Args &... args)
|
||||||
|
{
|
||||||
|
custom_handlers_[flag] = details::make_unique<T>(args...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
void set_pattern(std::string pattern);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string pattern_;
|
||||||
|
std::string eol_;
|
||||||
|
pattern_time_type pattern_time_type_;
|
||||||
|
std::tm cached_tm_;
|
||||||
|
std::chrono::seconds last_log_secs_;
|
||||||
|
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
|
||||||
|
custom_flags custom_handlers_;
|
||||||
|
|
||||||
|
std::tm get_time_(const details::log_msg &msg);
|
||||||
|
template<typename Padder>
|
||||||
|
void handle_flag_(char flag, details::padding_info padding);
|
||||||
|
|
||||||
|
// Extract given pad spec (e.g. %8X)
|
||||||
|
// Advance the given it pass the end of the padding spec found (if any)
|
||||||
|
// Return padding.
|
||||||
|
static details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
|
||||||
|
|
||||||
|
void compile_pattern_(const std::string &pattern);
|
||||||
|
};
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "pattern_formatter-inl.h"
|
||||||
|
#endif
|
||||||
119
f4mp_originalcode/F4MPClient/include/spdlog/sinks/android_sink.h
Normal file
119
f4mp_originalcode/F4MPClient/include/spdlog/sinks/android_sink.h
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
|
||||||
|
#include <spdlog/details/fmt_helper.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_ANDROID_RETRIES)
|
||||||
|
#define SPDLOG_ANDROID_RETRIES 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Android sink (logging using __android_log_write)
|
||||||
|
*/
|
||||||
|
template<typename Mutex>
|
||||||
|
class android_sink final : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false)
|
||||||
|
: tag_(std::move(tag))
|
||||||
|
, use_raw_msg_(use_raw_msg)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
const android_LogPriority priority = convert_to_android_(msg.level);
|
||||||
|
memory_buf_t formatted;
|
||||||
|
if (use_raw_msg_)
|
||||||
|
{
|
||||||
|
details::fmt_helper::append_string_view(msg.payload, formatted);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
}
|
||||||
|
formatted.push_back('\0');
|
||||||
|
const char *msg_output = formatted.data();
|
||||||
|
|
||||||
|
// See system/core/liblog/logger_write.c for explanation of return value
|
||||||
|
int ret = __android_log_write(priority, tag_.c_str(), msg_output);
|
||||||
|
int retry_count = 0;
|
||||||
|
while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
|
||||||
|
{
|
||||||
|
details::os::sleep_for_millis(5);
|
||||||
|
ret = __android_log_write(priority, tag_.c_str(), msg_output);
|
||||||
|
retry_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("__android_log_write() failed", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
|
||||||
|
{
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case spdlog::level::trace:
|
||||||
|
return ANDROID_LOG_VERBOSE;
|
||||||
|
case spdlog::level::debug:
|
||||||
|
return ANDROID_LOG_DEBUG;
|
||||||
|
case spdlog::level::info:
|
||||||
|
return ANDROID_LOG_INFO;
|
||||||
|
case spdlog::level::warn:
|
||||||
|
return ANDROID_LOG_WARN;
|
||||||
|
case spdlog::level::err:
|
||||||
|
return ANDROID_LOG_ERROR;
|
||||||
|
case spdlog::level::critical:
|
||||||
|
return ANDROID_LOG_FATAL;
|
||||||
|
default:
|
||||||
|
return ANDROID_LOG_DEFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string tag_;
|
||||||
|
bool use_raw_msg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using android_sink_mt = android_sink<std::mutex>;
|
||||||
|
using android_sink_st = android_sink<details::null_mutex>;
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
// Create and register android syslog logger
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog")
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::android_sink_mt>(logger_name, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog")
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::android_sink_st>(logger_name, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#endif // __ANDROID__
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/sinks/ansicolor_sink.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, color_mode mode)
|
||||||
|
: target_file_(target_file)
|
||||||
|
, mutex_(ConsoleMutex::mutex())
|
||||||
|
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
||||||
|
|
||||||
|
{
|
||||||
|
set_color_mode(mode);
|
||||||
|
colors_[level::trace] = to_string_(white);
|
||||||
|
colors_[level::debug] = to_string_(cyan);
|
||||||
|
colors_[level::info] = to_string_(green);
|
||||||
|
colors_[level::warn] = to_string_(yellow_bold);
|
||||||
|
colors_[level::err] = to_string_(red_bold);
|
||||||
|
colors_[level::critical] = to_string_(bold_on_red);
|
||||||
|
colors_[level::off] = to_string_(reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
colors_[color_level] = to_string_(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
// Wrap the originally formatted message in color codes.
|
||||||
|
// If color is not supported in the terminal, log as is instead.
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
msg.color_range_start = 0;
|
||||||
|
msg.color_range_end = 0;
|
||||||
|
memory_buf_t formatted;
|
||||||
|
formatter_->format(msg, formatted);
|
||||||
|
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
||||||
|
{
|
||||||
|
// before color range
|
||||||
|
print_range_(formatted, 0, msg.color_range_start);
|
||||||
|
// in color range
|
||||||
|
print_ccode_(colors_[msg.level]);
|
||||||
|
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
||||||
|
print_ccode_(reset);
|
||||||
|
// after color range
|
||||||
|
print_range_(formatted, msg.color_range_end, formatted.size());
|
||||||
|
}
|
||||||
|
else // no color
|
||||||
|
{
|
||||||
|
print_range_(formatted, 0, formatted.size());
|
||||||
|
}
|
||||||
|
fflush(target_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::flush()
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
fflush(target_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
formatter_ = std::move(sink_formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE bool ansicolor_sink<ConsoleMutex>::should_color()
|
||||||
|
{
|
||||||
|
return should_do_colors_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case color_mode::always:
|
||||||
|
should_do_colors_ = true;
|
||||||
|
return;
|
||||||
|
case color_mode::automatic:
|
||||||
|
should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
|
||||||
|
return;
|
||||||
|
case color_mode::never:
|
||||||
|
should_do_colors_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
|
||||||
|
{
|
||||||
|
fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
|
||||||
|
{
|
||||||
|
fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE std::string ansicolor_sink<ConsoleMutex>::to_string_(const string_view_t &sv)
|
||||||
|
{
|
||||||
|
return std::string(sv.data(), sv.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ansicolor_stdout_sink
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE ansicolor_stdout_sink<ConsoleMutex>::ansicolor_stdout_sink(color_mode mode)
|
||||||
|
: ansicolor_sink<ConsoleMutex>(stdout, mode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// ansicolor_stderr_sink
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE ansicolor_stderr_sink<ConsoleMutex>::ansicolor_stderr_sink(color_mode mode)
|
||||||
|
: ansicolor_sink<ConsoleMutex>(stderr, mode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/console_globals.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/sinks/sink.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sink prefixes the output with an ANSI escape sequence color code
|
||||||
|
* depending on the severity
|
||||||
|
* of the message.
|
||||||
|
* If no color terminal detected, omit the escape codes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class ansicolor_sink : public sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using mutex_t = typename ConsoleMutex::mutex_t;
|
||||||
|
ansicolor_sink(FILE *target_file, color_mode mode);
|
||||||
|
~ansicolor_sink() override = default;
|
||||||
|
|
||||||
|
ansicolor_sink(const ansicolor_sink &other) = delete;
|
||||||
|
ansicolor_sink(ansicolor_sink &&other) = delete;
|
||||||
|
|
||||||
|
ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
|
||||||
|
ansicolor_sink &operator=(ansicolor_sink &&other) = delete;
|
||||||
|
|
||||||
|
void set_color(level::level_enum color_level, string_view_t color);
|
||||||
|
void set_color_mode(color_mode mode);
|
||||||
|
bool should_color();
|
||||||
|
|
||||||
|
void log(const details::log_msg &msg) override;
|
||||||
|
void flush() override;
|
||||||
|
void set_pattern(const std::string &pattern) final;
|
||||||
|
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
||||||
|
|
||||||
|
// Formatting codes
|
||||||
|
const string_view_t reset = "\033[m";
|
||||||
|
const string_view_t bold = "\033[1m";
|
||||||
|
const string_view_t dark = "\033[2m";
|
||||||
|
const string_view_t underline = "\033[4m";
|
||||||
|
const string_view_t blink = "\033[5m";
|
||||||
|
const string_view_t reverse = "\033[7m";
|
||||||
|
const string_view_t concealed = "\033[8m";
|
||||||
|
const string_view_t clear_line = "\033[K";
|
||||||
|
|
||||||
|
// Foreground colors
|
||||||
|
const string_view_t black = "\033[30m";
|
||||||
|
const string_view_t red = "\033[31m";
|
||||||
|
const string_view_t green = "\033[32m";
|
||||||
|
const string_view_t yellow = "\033[33m";
|
||||||
|
const string_view_t blue = "\033[34m";
|
||||||
|
const string_view_t magenta = "\033[35m";
|
||||||
|
const string_view_t cyan = "\033[36m";
|
||||||
|
const string_view_t white = "\033[37m";
|
||||||
|
|
||||||
|
/// Background colors
|
||||||
|
const string_view_t on_black = "\033[40m";
|
||||||
|
const string_view_t on_red = "\033[41m";
|
||||||
|
const string_view_t on_green = "\033[42m";
|
||||||
|
const string_view_t on_yellow = "\033[43m";
|
||||||
|
const string_view_t on_blue = "\033[44m";
|
||||||
|
const string_view_t on_magenta = "\033[45m";
|
||||||
|
const string_view_t on_cyan = "\033[46m";
|
||||||
|
const string_view_t on_white = "\033[47m";
|
||||||
|
|
||||||
|
/// Bold colors
|
||||||
|
const string_view_t yellow_bold = "\033[33m\033[1m";
|
||||||
|
const string_view_t red_bold = "\033[31m\033[1m";
|
||||||
|
const string_view_t bold_on_red = "\033[1m\033[41m";
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *target_file_;
|
||||||
|
mutex_t &mutex_;
|
||||||
|
bool should_do_colors_;
|
||||||
|
std::unique_ptr<spdlog::formatter> formatter_;
|
||||||
|
std::array<std::string, level::n_levels> colors_;
|
||||||
|
void print_ccode_(const string_view_t &color_code);
|
||||||
|
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
|
||||||
|
static std::string to_string_(const string_view_t &sv);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class ansicolor_stdout_sink : public ansicolor_sink<ConsoleMutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ansicolor_stdout_sink(color_mode mode = color_mode::automatic);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class ansicolor_stderr_sink : public ansicolor_sink<ConsoleMutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ansicolor_stderr_sink(color_mode mode = color_mode::automatic);
|
||||||
|
};
|
||||||
|
|
||||||
|
using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<details::console_mutex>;
|
||||||
|
using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
|
using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<details::console_mutex>;
|
||||||
|
using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "ansicolor_sink-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink()
|
||||||
|
: formatter_{details::make_unique<spdlog::pattern_formatter>()}
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink(std::unique_ptr<spdlog::formatter> formatter)
|
||||||
|
: formatter_{std::move(formatter)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(mutex_);
|
||||||
|
sink_it_(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::flush()
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(mutex_);
|
||||||
|
flush_();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern(const std::string &pattern)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(mutex_);
|
||||||
|
set_pattern_(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(mutex_);
|
||||||
|
set_formatter_(std::move(sink_formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern_(const std::string &pattern)
|
||||||
|
{
|
||||||
|
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
|
||||||
|
{
|
||||||
|
formatter_ = std::move(sink_formatter);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
//
|
||||||
|
// base sink templated over a mutex (either dummy or real)
|
||||||
|
// concrete implementation should override the sink_it_() and flush_() methods.
|
||||||
|
// locking is taken care of in this class - no locking needed by the
|
||||||
|
// implementers..
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
#include <spdlog/sinks/sink.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
template<typename Mutex>
|
||||||
|
class base_sink : public sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
base_sink();
|
||||||
|
explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
|
||||||
|
~base_sink() override = default;
|
||||||
|
|
||||||
|
base_sink(const base_sink &) = delete;
|
||||||
|
base_sink(base_sink &&) = delete;
|
||||||
|
|
||||||
|
base_sink &operator=(const base_sink &) = delete;
|
||||||
|
base_sink &operator=(base_sink &&) = delete;
|
||||||
|
|
||||||
|
void log(const details::log_msg &msg) final;
|
||||||
|
void flush() final;
|
||||||
|
void set_pattern(const std::string &pattern) final;
|
||||||
|
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// sink formatter
|
||||||
|
std::unique_ptr<spdlog::formatter> formatter_;
|
||||||
|
Mutex mutex_;
|
||||||
|
|
||||||
|
virtual void sink_it_(const details::log_msg &msg) = 0;
|
||||||
|
virtual void flush_() = 0;
|
||||||
|
virtual void set_pattern_(const std::string &pattern);
|
||||||
|
virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
|
||||||
|
};
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "base_sink-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/sinks/basic_file_sink.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(const filename_t &filename, bool truncate)
|
||||||
|
{
|
||||||
|
file_helper_.open(filename, truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE const filename_t &basic_file_sink<Mutex>::filename() const
|
||||||
|
{
|
||||||
|
return file_helper_.filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE void basic_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
file_helper_.write(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE void basic_file_sink<Mutex>::flush_()
|
||||||
|
{
|
||||||
|
file_helper_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/file_helper.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
/*
|
||||||
|
* Trivial file sink with single file as target
|
||||||
|
*/
|
||||||
|
template<typename Mutex>
|
||||||
|
class basic_file_sink final : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit basic_file_sink(const filename_t &filename, bool truncate = false);
|
||||||
|
const filename_t &filename() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override;
|
||||||
|
void flush_() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
details::file_helper file_helper_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using basic_file_sink_mt = basic_file_sink<std::mutex>;
|
||||||
|
using basic_file_sink_st = basic_file_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
//
|
||||||
|
// factory functions
|
||||||
|
//
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "basic_file_sink-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/file_helper.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
#include <spdlog/details/circular_q.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <ctime>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
|
||||||
|
*/
|
||||||
|
struct daily_filename_calculator
|
||||||
|
{
|
||||||
|
// Create filename for the form basename.YYYY-MM-DD
|
||||||
|
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
||||||
|
{
|
||||||
|
filename_t basename, ext;
|
||||||
|
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
||||||
|
return fmt::format(
|
||||||
|
SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rotating file sink based on date.
|
||||||
|
* If truncate != false , the created file will be truncated.
|
||||||
|
* If max_files > 0, retain only the last max_files and delete previous.
|
||||||
|
*/
|
||||||
|
template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
|
||||||
|
class daily_file_sink final : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// create daily file sink which rotates on given time
|
||||||
|
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0)
|
||||||
|
: base_filename_(std::move(base_filename))
|
||||||
|
, rotation_h_(rotation_hour)
|
||||||
|
, rotation_m_(rotation_minute)
|
||||||
|
, truncate_(truncate)
|
||||||
|
, max_files_(max_files)
|
||||||
|
, filenames_q_()
|
||||||
|
{
|
||||||
|
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto now = log_clock::now();
|
||||||
|
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
||||||
|
file_helper_.open(filename, truncate_);
|
||||||
|
rotation_tp_ = next_rotation_tp_();
|
||||||
|
|
||||||
|
if (max_files_ > 0)
|
||||||
|
{
|
||||||
|
init_filenames_q_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename_t filename()
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
return file_helper_.filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
auto time = msg.time;
|
||||||
|
bool should_rotate = time >= rotation_tp_;
|
||||||
|
if (should_rotate)
|
||||||
|
{
|
||||||
|
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
|
||||||
|
file_helper_.open(filename, truncate_);
|
||||||
|
rotation_tp_ = next_rotation_tp_();
|
||||||
|
}
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
file_helper_.write(formatted);
|
||||||
|
|
||||||
|
// Do the cleaning only at the end because it might throw on failure.
|
||||||
|
if (should_rotate && max_files_ > 0)
|
||||||
|
{
|
||||||
|
delete_old_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override
|
||||||
|
{
|
||||||
|
file_helper_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init_filenames_q_()
|
||||||
|
{
|
||||||
|
using details::os::path_exists;
|
||||||
|
|
||||||
|
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
|
||||||
|
std::vector<filename_t> filenames;
|
||||||
|
auto now = log_clock::now();
|
||||||
|
while (filenames.size() < max_files_)
|
||||||
|
{
|
||||||
|
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
||||||
|
if (!path_exists(filename))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
filenames.emplace_back(filename);
|
||||||
|
now -= std::chrono::hours(24);
|
||||||
|
}
|
||||||
|
for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
|
||||||
|
{
|
||||||
|
filenames_q_.push_back(std::move(*iter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tm now_tm(log_clock::time_point tp)
|
||||||
|
{
|
||||||
|
time_t tnow = log_clock::to_time_t(tp);
|
||||||
|
return spdlog::details::os::localtime(tnow);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_clock::time_point next_rotation_tp_()
|
||||||
|
{
|
||||||
|
auto now = log_clock::now();
|
||||||
|
tm date = now_tm(now);
|
||||||
|
date.tm_hour = rotation_h_;
|
||||||
|
date.tm_min = rotation_m_;
|
||||||
|
date.tm_sec = 0;
|
||||||
|
auto rotation_time = log_clock::from_time_t(std::mktime(&date));
|
||||||
|
if (rotation_time > now)
|
||||||
|
{
|
||||||
|
return rotation_time;
|
||||||
|
}
|
||||||
|
return {rotation_time + std::chrono::hours(24)};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the file N rotations ago.
|
||||||
|
// Throw spdlog_ex on failure to delete the old file.
|
||||||
|
void delete_old_()
|
||||||
|
{
|
||||||
|
using details::os::filename_to_str;
|
||||||
|
using details::os::remove_if_exists;
|
||||||
|
|
||||||
|
filename_t current_file = file_helper_.filename();
|
||||||
|
if (filenames_q_.full())
|
||||||
|
{
|
||||||
|
auto old_filename = std::move(filenames_q_.front());
|
||||||
|
filenames_q_.pop_front();
|
||||||
|
bool ok = remove_if_exists(old_filename) == 0;
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
filenames_q_.push_back(std::move(current_file));
|
||||||
|
throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filenames_q_.push_back(std::move(current_file));
|
||||||
|
}
|
||||||
|
|
||||||
|
filename_t base_filename_;
|
||||||
|
int rotation_h_;
|
||||||
|
int rotation_m_;
|
||||||
|
log_clock::time_point rotation_tp_;
|
||||||
|
details::file_helper file_helper_;
|
||||||
|
bool truncate_;
|
||||||
|
uint16_t max_files_;
|
||||||
|
details::circular_q<filename_t> filenames_q_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
||||||
|
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
//
|
||||||
|
// factory functions
|
||||||
|
//
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> daily_logger_mt(
|
||||||
|
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> daily_logger_st(
|
||||||
|
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base_sink.h"
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Distribution sink (mux). Stores a vector of sinks which get called when log
|
||||||
|
// is called
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
class dist_sink : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
dist_sink() = default;
|
||||||
|
explicit dist_sink(std::vector<std::shared_ptr<sink>> sinks)
|
||||||
|
: sinks_(sinks)
|
||||||
|
{}
|
||||||
|
|
||||||
|
dist_sink(const dist_sink &) = delete;
|
||||||
|
dist_sink &operator=(const dist_sink &) = delete;
|
||||||
|
|
||||||
|
void add_sink(std::shared_ptr<sink> sink)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
sinks_.push_back(sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_sink(std::shared_ptr<sink> sink)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
sinks_ = std::move(sinks);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<sink>> &sinks()
|
||||||
|
{
|
||||||
|
return sinks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
if (sink->should_log(msg.level))
|
||||||
|
{
|
||||||
|
sink->log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_pattern_(const std::string &pattern) override
|
||||||
|
{
|
||||||
|
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
|
||||||
|
{
|
||||||
|
base_sink<Mutex>::formatter_ = std::move(sink_formatter);
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
sink->set_formatter(base_sink<Mutex>::formatter_->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<std::shared_ptr<sink>> sinks_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using dist_sink_mt = dist_sink<std::mutex>;
|
||||||
|
using dist_sink_st = dist_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dist_sink.h"
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
// Duplicate message removal sink.
|
||||||
|
// Skip the message if previous one is identical and less than "max_skip_duration" have passed
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// #include <spdlog/sinks/dup_filter_sink.h>
|
||||||
|
//
|
||||||
|
// int main() {
|
||||||
|
// auto dup_filter = std::make_shared<dup_filter_sink_st>(std::chrono::seconds(5));
|
||||||
|
// dup_filter->add_sink(std::make_shared<stdout_color_sink_mt>());
|
||||||
|
// spdlog::logger l("logger", dup_filter);
|
||||||
|
// l.info("Hello");
|
||||||
|
// l.info("Hello");
|
||||||
|
// l.info("Hello");
|
||||||
|
// l.info("Different Hello");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Will produce:
|
||||||
|
// [2019-06-25 17:50:56.511] [logger] [info] Hello
|
||||||
|
// [2019-06-25 17:50:56.512] [logger] [info] Skipped 3 duplicate messages..
|
||||||
|
// [2019-06-25 17:50:56.512] [logger] [info] Different Hello
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
template<typename Mutex>
|
||||||
|
class dup_filter_sink : public dist_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<class Rep, class Period>
|
||||||
|
explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration)
|
||||||
|
: max_skip_duration_{max_skip_duration}
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::chrono::microseconds max_skip_duration_;
|
||||||
|
log_clock::time_point last_msg_time_;
|
||||||
|
std::string last_msg_payload_;
|
||||||
|
size_t skip_counter_ = 0;
|
||||||
|
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
bool filtered = filter_(msg);
|
||||||
|
if (!filtered)
|
||||||
|
{
|
||||||
|
skip_counter_ += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// log the "skipped.." message
|
||||||
|
if (skip_counter_ > 0)
|
||||||
|
{
|
||||||
|
memory_buf_t buf;
|
||||||
|
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
|
||||||
|
details::log_msg skipped_msg{msg.logger_name, msg.level, string_view_t{buf.data(), buf.size()}};
|
||||||
|
dist_sink<Mutex>::sink_it_(skipped_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// log current message
|
||||||
|
dist_sink<Mutex>::sink_it_(msg);
|
||||||
|
last_msg_time_ = msg.time;
|
||||||
|
skip_counter_ = 0;
|
||||||
|
last_msg_payload_.assign(msg.payload.data(), msg.payload.data() + msg.payload.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// return whether the log msg should be displayed (true) or skipped (false)
|
||||||
|
bool filter_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
auto filter_duration = msg.time - last_msg_time_;
|
||||||
|
return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using dup_filter_sink_mt = dup_filter_sink<std::mutex>;
|
||||||
|
using dup_filter_sink_st = dup_filter_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright(c) 2016 Alexander Dalshov.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
|
||||||
|
#include <spdlog/details/windows_include.h>
|
||||||
|
#include <winbase.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
/*
|
||||||
|
* MSVC sink (logging using OutputDebugStringA)
|
||||||
|
*/
|
||||||
|
template<typename Mutex>
|
||||||
|
class msvc_sink : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit msvc_sink() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
using msvc_sink_mt = msvc_sink<std::mutex>;
|
||||||
|
using msvc_sink_st = msvc_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
using windebug_sink_mt = msvc_sink_mt;
|
||||||
|
using windebug_sink_st = msvc_sink_st;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
class null_sink : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &) override {}
|
||||||
|
void flush_() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
using null_sink_mt = null_sink<details::null_mutex>;
|
||||||
|
using null_sink_st = null_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> null_logger_mt(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
auto null_logger = Factory::template create<sinks::null_sink_mt>(logger_name);
|
||||||
|
null_logger->set_level(level::off);
|
||||||
|
return null_logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> null_logger_st(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
auto null_logger = Factory::template create<sinks::null_sink_st>(logger_name);
|
||||||
|
null_logger->set_level(level::off);
|
||||||
|
return null_logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
template<typename Mutex>
|
||||||
|
class ostream_sink final : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ostream_sink(std::ostream &os, bool force_flush = false)
|
||||||
|
: ostream_(os)
|
||||||
|
, force_flush_(force_flush)
|
||||||
|
{}
|
||||||
|
ostream_sink(const ostream_sink &) = delete;
|
||||||
|
ostream_sink &operator=(const ostream_sink &) = delete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
ostream_.write(formatted.data(), static_cast<std::streamsize>(formatted.size()));
|
||||||
|
if (force_flush_)
|
||||||
|
{
|
||||||
|
ostream_.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override
|
||||||
|
{
|
||||||
|
ostream_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &ostream_;
|
||||||
|
bool force_flush_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ostream_sink_mt = ostream_sink<std::mutex>;
|
||||||
|
using ostream_sink_st = ostream_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/sinks/base_sink.h"
|
||||||
|
#include "spdlog/details/circular_q.h"
|
||||||
|
#include "spdlog/details/log_msg_buffer.h"
|
||||||
|
#include "spdlog/details/null_mutex.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
/*
|
||||||
|
* Ring buffer sink
|
||||||
|
*/
|
||||||
|
template<typename Mutex>
|
||||||
|
class ringbuffer_sink final : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ringbuffer_sink(size_t n_items)
|
||||||
|
: q_{n_items}
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<details::log_msg_buffer> last_raw(size_t lim = 0)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
auto items_available = q_.size();
|
||||||
|
auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
|
||||||
|
std::vector<details::log_msg_buffer> ret;
|
||||||
|
ret.reserve(n_items);
|
||||||
|
for (size_t i = (items_available - n_items); i < items_available; i++)
|
||||||
|
{
|
||||||
|
ret.push_back(q_.at(i));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> last_formatted(size_t lim = 0)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
auto items_available = q_.size();
|
||||||
|
auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
ret.reserve(n_items);
|
||||||
|
for (size_t i = (items_available - n_items); i < items_available; i++)
|
||||||
|
{
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(q_.at(i), formatted);
|
||||||
|
ret.push_back(fmt::to_string(formatted));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
q_.push_back(details::log_msg_buffer{msg});
|
||||||
|
}
|
||||||
|
void flush_() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
details::circular_q<details::log_msg_buffer> q_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ringbuffer_sink_mt = ringbuffer_sink<std::mutex>;
|
||||||
|
using ringbuffer_sink_st = ringbuffer_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/sinks/rotating_file_sink.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
|
||||||
|
#include <spdlog/details/file_helper.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
|
||||||
|
filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open)
|
||||||
|
: base_filename_(std::move(base_filename))
|
||||||
|
, max_size_(max_size)
|
||||||
|
, max_files_(max_files)
|
||||||
|
{
|
||||||
|
file_helper_.open(calc_filename(base_filename_, 0));
|
||||||
|
current_size_ = file_helper_.size(); // expensive. called only once
|
||||||
|
if (rotate_on_open && current_size_ > 0)
|
||||||
|
{
|
||||||
|
rotate_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calc filename according to index and file extension if exists.
|
||||||
|
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename, std::size_t index)
|
||||||
|
{
|
||||||
|
if (index == 0u)
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename_t basename, ext;
|
||||||
|
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
||||||
|
return fmt::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::filename()
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
return file_helper_.filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
current_size_ += formatted.size();
|
||||||
|
if (current_size_ > max_size_)
|
||||||
|
{
|
||||||
|
rotate_();
|
||||||
|
current_size_ = formatted.size();
|
||||||
|
}
|
||||||
|
file_helper_.write(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE void rotating_file_sink<Mutex>::flush_()
|
||||||
|
{
|
||||||
|
file_helper_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate files:
|
||||||
|
// log.txt -> log.1.txt
|
||||||
|
// log.1.txt -> log.2.txt
|
||||||
|
// log.2.txt -> log.3.txt
|
||||||
|
// log.3.txt -> delete
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
|
||||||
|
{
|
||||||
|
using details::os::filename_to_str;
|
||||||
|
using details::os::path_exists;
|
||||||
|
file_helper_.close();
|
||||||
|
for (auto i = max_files_; i > 0; --i)
|
||||||
|
{
|
||||||
|
filename_t src = calc_filename(base_filename_, i - 1);
|
||||||
|
if (!path_exists(src))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
filename_t target = calc_filename(base_filename_, i);
|
||||||
|
|
||||||
|
if (!rename_file_(src, target))
|
||||||
|
{
|
||||||
|
// if failed try again after a small delay.
|
||||||
|
// this is a workaround to a windows issue, where very high rotation
|
||||||
|
// rates can cause the rename to fail with permission denied (because of antivirus?).
|
||||||
|
details::os::sleep_for_millis(100);
|
||||||
|
if (!rename_file_(src, target))
|
||||||
|
{
|
||||||
|
file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
|
||||||
|
current_size_ = 0;
|
||||||
|
throw_spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_helper_.reopen(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the target if exists, and rename the src file to target
|
||||||
|
// return true on success, false otherwise.
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, const filename_t &target_filename)
|
||||||
|
{
|
||||||
|
// try to delete the target file in case it already exists.
|
||||||
|
(void)details::os::remove(target_filename);
|
||||||
|
return details::os::rename(src_filename, target_filename) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/file_helper.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Rotating file sink based on size
|
||||||
|
//
|
||||||
|
template<typename Mutex>
|
||||||
|
class rotating_file_sink final : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false);
|
||||||
|
static filename_t calc_filename(const filename_t &filename, std::size_t index);
|
||||||
|
filename_t filename();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override;
|
||||||
|
void flush_() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Rotate files:
|
||||||
|
// log.txt -> log.1.txt
|
||||||
|
// log.1.txt -> log.2.txt
|
||||||
|
// log.2.txt -> log.3.txt
|
||||||
|
// log.3.txt -> delete
|
||||||
|
void rotate_();
|
||||||
|
|
||||||
|
// delete the target if exists, and rename the src file to target
|
||||||
|
// return true on success, false otherwise.
|
||||||
|
bool rename_file_(const filename_t &src_filename, const filename_t &target_filename);
|
||||||
|
|
||||||
|
filename_t base_filename_;
|
||||||
|
std::size_t max_size_;
|
||||||
|
std::size_t max_files_;
|
||||||
|
std::size_t current_size_;
|
||||||
|
details::file_helper file_helper_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
|
||||||
|
using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
//
|
||||||
|
// factory functions
|
||||||
|
//
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> rotating_logger_mt(
|
||||||
|
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files, rotate_on_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> rotating_logger_st(
|
||||||
|
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files, rotate_on_open);
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "rotating_file_sink-inl.h"
|
||||||
|
#endif
|
||||||
25
f4mp_originalcode/F4MPClient/include/spdlog/sinks/sink-inl.h
Normal file
25
f4mp_originalcode/F4MPClient/include/spdlog/sinks/sink-inl.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/sinks/sink.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
|
||||||
|
SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const
|
||||||
|
{
|
||||||
|
return msg_level >= level_.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void spdlog::sinks::sink::set_level(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
level_.store(log_level, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::level::level_enum spdlog::sinks::sink::level() const
|
||||||
|
{
|
||||||
|
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
35
f4mp_originalcode/F4MPClient/include/spdlog/sinks/sink.h
Normal file
35
f4mp_originalcode/F4MPClient/include/spdlog/sinks/sink.h
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/log_msg.h>
|
||||||
|
#include <spdlog/formatter.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
namespace sinks {
|
||||||
|
class SPDLOG_API sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~sink() = default;
|
||||||
|
virtual void log(const details::log_msg &msg) = 0;
|
||||||
|
virtual void flush() = 0;
|
||||||
|
virtual void set_pattern(const std::string &pattern) = 0;
|
||||||
|
virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
|
||||||
|
|
||||||
|
void set_level(level::level_enum log_level);
|
||||||
|
level::level_enum level() const;
|
||||||
|
bool should_log(level::level_enum msg_level) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// sink log level - default is all
|
||||||
|
level_t level_{level::trace};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "sink-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/logger.h>
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
template<typename Factory>
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::stdout_color_sink_mt>(logger_name, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory>
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::stdout_color_sink_st>(logger_name, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory>
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::stderr_color_sink_mt>(logger_name, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory>
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::stderr_color_sink_st>(logger_name, mode);
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <spdlog/sinks/wincolor_sink.h>
|
||||||
|
#else
|
||||||
|
#include <spdlog/sinks/ansicolor_sink.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
#ifdef _WIN32
|
||||||
|
using stdout_color_sink_mt = wincolor_stdout_sink_mt;
|
||||||
|
using stdout_color_sink_st = wincolor_stdout_sink_st;
|
||||||
|
using stderr_color_sink_mt = wincolor_stderr_sink_mt;
|
||||||
|
using stderr_color_sink_st = wincolor_stderr_sink_st;
|
||||||
|
#else
|
||||||
|
using stdout_color_sink_mt = ansicolor_stdout_sink_mt;
|
||||||
|
using stdout_color_sink_st = ansicolor_stdout_sink_st;
|
||||||
|
using stderr_color_sink_mt = ansicolor_stderr_sink_mt;
|
||||||
|
using stderr_color_sink_st = ansicolor_stderr_sink_st;
|
||||||
|
#endif
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "stdout_color_sinks-inl.h"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/sinks/stdout_sinks.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/details/console_globals.h>
|
||||||
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
|
||||||
|
: mutex_(ConsoleMutex::mutex())
|
||||||
|
, file_(file)
|
||||||
|
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
memory_buf_t formatted;
|
||||||
|
formatter_->format(msg, formatted);
|
||||||
|
fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
||||||
|
fflush(file_); // flush every line to terminal
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::flush()
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
fflush(file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_pattern(const std::string &pattern)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
formatter_ = std::move(sink_formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stdout sink
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE stdout_sink<ConsoleMutex>::stdout_sink()
|
||||||
|
: stdout_sink_base<ConsoleMutex>(stdout)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// stderr sink
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE stderr_sink<ConsoleMutex>::stderr_sink()
|
||||||
|
: stdout_sink_base<ConsoleMutex>(stderr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
// factory methods
|
||||||
|
template<typename Factory>
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::stdout_sink_mt>(logger_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory>
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::stdout_sink_st>(logger_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory>
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::stderr_sink_mt>(logger_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory>
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::stderr_sink_st>(logger_name);
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/console_globals.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
#include <spdlog/sinks/sink.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class stdout_sink_base : public sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using mutex_t = typename ConsoleMutex::mutex_t;
|
||||||
|
explicit stdout_sink_base(FILE *file);
|
||||||
|
~stdout_sink_base() override = default;
|
||||||
|
|
||||||
|
stdout_sink_base(const stdout_sink_base &other) = delete;
|
||||||
|
stdout_sink_base(stdout_sink_base &&other) = delete;
|
||||||
|
|
||||||
|
stdout_sink_base &operator=(const stdout_sink_base &other) = delete;
|
||||||
|
stdout_sink_base &operator=(stdout_sink_base &&other) = delete;
|
||||||
|
|
||||||
|
void log(const details::log_msg &msg) override;
|
||||||
|
void flush() override;
|
||||||
|
void set_pattern(const std::string &pattern) override;
|
||||||
|
|
||||||
|
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutex_t &mutex_;
|
||||||
|
FILE *file_;
|
||||||
|
std::unique_ptr<spdlog::formatter> formatter_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class stdout_sink : public stdout_sink_base<ConsoleMutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
stdout_sink();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class stderr_sink : public stdout_sink_base<ConsoleMutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
stderr_sink();
|
||||||
|
};
|
||||||
|
|
||||||
|
using stdout_sink_mt = stdout_sink<details::console_mutex>;
|
||||||
|
using stdout_sink_st = stdout_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
|
using stderr_sink_mt = stderr_sink<details::console_mutex>;
|
||||||
|
using stderr_sink_st = stderr_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
// factory methods
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name);
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name);
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name);
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "stdout_sinks-inl.h"
|
||||||
|
#endif
|
||||||
109
f4mp_originalcode/F4MPClient/include/spdlog/sinks/syslog_sink.h
Normal file
109
f4mp_originalcode/F4MPClient/include/spdlog/sinks/syslog_sink.h
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
/**
|
||||||
|
* Sink that write to syslog using the `syscall()` library call.
|
||||||
|
*/
|
||||||
|
template<typename Mutex>
|
||||||
|
class syslog_sink : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
syslog_sink(std::string ident, int syslog_option, int syslog_facility, bool enable_formatting)
|
||||||
|
: enable_formatting_{enable_formatting}
|
||||||
|
, syslog_levels_{{/* spdlog::level::trace */ LOG_DEBUG,
|
||||||
|
/* spdlog::level::debug */ LOG_DEBUG,
|
||||||
|
/* spdlog::level::info */ LOG_INFO,
|
||||||
|
/* spdlog::level::warn */ LOG_WARNING,
|
||||||
|
/* spdlog::level::err */ LOG_ERR,
|
||||||
|
/* spdlog::level::critical */ LOG_CRIT,
|
||||||
|
/* spdlog::level::off */ LOG_INFO}}
|
||||||
|
, ident_{std::move(ident)}
|
||||||
|
{
|
||||||
|
// set ident to be program name if empty
|
||||||
|
::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility);
|
||||||
|
}
|
||||||
|
|
||||||
|
~syslog_sink() override
|
||||||
|
{
|
||||||
|
::closelog();
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog_sink(const syslog_sink &) = delete;
|
||||||
|
syslog_sink &operator=(const syslog_sink &) = delete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
string_view_t payload;
|
||||||
|
memory_buf_t formatted;
|
||||||
|
if (enable_formatting_)
|
||||||
|
{
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
payload = string_view_t(formatted.data(), formatted.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
payload = msg.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t length = payload.size();
|
||||||
|
// limit to max int
|
||||||
|
if (length > static_cast<size_t>(std::numeric_limits<int>::max()))
|
||||||
|
{
|
||||||
|
length = static_cast<size_t>(std::numeric_limits<int>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
::syslog(syslog_prio_from_level(msg), "%.*s", static_cast<int>(length), payload.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override {}
|
||||||
|
bool enable_formatting_ = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using levels_array = std::array<int, 7>;
|
||||||
|
levels_array syslog_levels_;
|
||||||
|
// must store the ident because the man says openlog might use the pointer as
|
||||||
|
// is and not a string copy
|
||||||
|
const std::string ident_;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Simply maps spdlog's log level to syslog priority level.
|
||||||
|
//
|
||||||
|
int syslog_prio_from_level(const details::log_msg &msg) const
|
||||||
|
{
|
||||||
|
return syslog_levels_.at(static_cast<levels_array::size_type>(msg.level));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using syslog_sink_mt = syslog_sink<std::mutex>;
|
||||||
|
using syslog_sink_st = syslog_sink<details::null_mutex>;
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
// Create and register a syslog logger
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> syslog_logger_mt(const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0,
|
||||||
|
int syslog_facility = LOG_USER, bool enable_formatting = false)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::syslog_sink_mt>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> syslog_logger_st(const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0,
|
||||||
|
int syslog_facility = LOG_USER, bool enable_formatting = false)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::syslog_sink_st>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
103
f4mp_originalcode/F4MPClient/include/spdlog/sinks/systemd_sink.h
Normal file
103
f4mp_originalcode/F4MPClient/include/spdlog/sinks/systemd_sink.h
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright(c) 2019 ZVYAGIN.Alexander@gmail.com
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#ifndef SD_JOURNAL_SUPPRESS_LOCATION
|
||||||
|
#define SD_JOURNAL_SUPPRESS_LOCATION
|
||||||
|
#endif
|
||||||
|
#include <systemd/sd-journal.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sink that write to systemd journal using the `sd_journal_send()` library call.
|
||||||
|
*
|
||||||
|
* Locking is not needed, as `sd_journal_send()` itself is thread-safe.
|
||||||
|
*/
|
||||||
|
template<typename Mutex>
|
||||||
|
class systemd_sink : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//
|
||||||
|
systemd_sink()
|
||||||
|
: syslog_levels_{{/* spdlog::level::trace */ LOG_DEBUG,
|
||||||
|
/* spdlog::level::debug */ LOG_DEBUG,
|
||||||
|
/* spdlog::level::info */ LOG_INFO,
|
||||||
|
/* spdlog::level::warn */ LOG_WARNING,
|
||||||
|
/* spdlog::level::err */ LOG_ERR,
|
||||||
|
/* spdlog::level::critical */ LOG_CRIT,
|
||||||
|
/* spdlog::level::off */ LOG_INFO}}
|
||||||
|
{}
|
||||||
|
|
||||||
|
~systemd_sink() override {}
|
||||||
|
|
||||||
|
systemd_sink(const systemd_sink &) = delete;
|
||||||
|
systemd_sink &operator=(const systemd_sink &) = delete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using levels_array = std::array<int, 7>;
|
||||||
|
levels_array syslog_levels_;
|
||||||
|
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
size_t length = msg.payload.size();
|
||||||
|
// limit to max int
|
||||||
|
if (length > static_cast<size_t>(std::numeric_limits<int>::max()))
|
||||||
|
{
|
||||||
|
length = static_cast<size_t>(std::numeric_limits<int>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not send source location if not available
|
||||||
|
if (msg.source.empty())
|
||||||
|
{
|
||||||
|
// Note: function call inside '()' to avoid macro expansion
|
||||||
|
err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), msg.payload.data(), "PRIORITY=%d", syslog_level(msg.level),
|
||||||
|
"SYSLOG_IDENTIFIER=%.*s", static_cast<int>(msg.logger_name.size()), msg.logger_name.data(), nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), msg.payload.data(), "PRIORITY=%d", syslog_level(msg.level),
|
||||||
|
"SYSLOG_IDENTIFIER=%.*s", static_cast<int>(msg.logger_name.size()), msg.logger_name.data(), "CODE_FILE=%s",
|
||||||
|
msg.source.filename, "CODE_LINE=%d", msg.source.line, "CODE_FUNC=%s", msg.source.funcname, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("Failed writing to systemd", errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int syslog_level(level::level_enum l)
|
||||||
|
{
|
||||||
|
return syslog_levels_.at(static_cast<levels_array::size_type>(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
using systemd_sink_mt = systemd_sink<std::mutex>;
|
||||||
|
using systemd_sink_st = systemd_sink<details::null_mutex>;
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
// Create and register a syslog logger
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> systemd_logger_mt(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::systemd_sink_mt>(logger_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> systemd_logger_st(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::systemd_sink_st>(logger_name);
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
81
f4mp_originalcode/F4MPClient/include/spdlog/sinks/tcp_sink.h
Normal file
81
f4mp_originalcode/F4MPClient/include/spdlog/sinks/tcp_sink.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <spdlog/details/tcp_client-windows.h>
|
||||||
|
#else
|
||||||
|
#include <spdlog/details/tcp_client.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Simple tcp client sink
|
||||||
|
// Connects to remote address and send the formatted log.
|
||||||
|
// Will attempt to reconnect if connection drops.
|
||||||
|
// If more complicated behaviour is needed (i.e get responses), you can inherit it and override the sink_it_ method.
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
struct tcp_sink_config
|
||||||
|
{
|
||||||
|
std::string server_host;
|
||||||
|
int server_port;
|
||||||
|
bool lazy_connect = false; // if true connect on first log call instead of on construction
|
||||||
|
|
||||||
|
tcp_sink_config(std::string host, int port)
|
||||||
|
: server_host{std::move(host)}
|
||||||
|
, server_port{port}
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
class tcp_sink : public spdlog::sinks::base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// connect to tcp host/port or throw if failed
|
||||||
|
// host can be hostname or ip address
|
||||||
|
|
||||||
|
explicit tcp_sink(tcp_sink_config sink_config)
|
||||||
|
: config_{std::move(sink_config)}
|
||||||
|
{
|
||||||
|
if (!config_.lazy_connect)
|
||||||
|
{
|
||||||
|
this->client_.connect(config_.server_host, config_.server_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~tcp_sink() override = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const spdlog::details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
spdlog::memory_buf_t formatted;
|
||||||
|
spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
if (!client_.is_connected())
|
||||||
|
{
|
||||||
|
client_.connect(config_.server_host, config_.server_port);
|
||||||
|
}
|
||||||
|
client_.send(formatted.data(), formatted.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override {}
|
||||||
|
tcp_sink_config config_;
|
||||||
|
details::tcp_client client_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using tcp_sink_mt = tcp_sink<std::mutex>;
|
||||||
|
using tcp_sink_st = tcp_sink<spdlog::details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
// Writing to Windows Event Log requires the registry entries below to be present, with the following modifications:
|
||||||
|
// 1. <log_name> should be replaced with your log name (e.g. your application name)
|
||||||
|
// 2. <source_name> should be replaced with the specific source name and the key should be duplicated for
|
||||||
|
// each source used in the application
|
||||||
|
//
|
||||||
|
// Since typically modifications of this kind require elevation, it's better to do it as a part of setup procedure.
|
||||||
|
// The snippet below uses mscoree.dll as the message file as it exists on most of the Windows systems anyway and
|
||||||
|
// happens to contain the needed resource.
|
||||||
|
//
|
||||||
|
// You can also specify a custom message file if needed.
|
||||||
|
// Please refer to Event Log functions descriptions in MSDN for more details on custom message files.
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Windows Registry Editor Version 5.00
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>]
|
||||||
|
|
||||||
|
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>\<source_name>]
|
||||||
|
"TypesSupported"=dword:00000007
|
||||||
|
"EventMessageFile"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\
|
||||||
|
00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\
|
||||||
|
5c,00,6d,00,73,00,63,00,6f,00,72,00,65,00,65,00,2e,00,64,00,6c,00,6c,00,00,\
|
||||||
|
00
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
|
||||||
|
#include <spdlog/details/windows_include.h>
|
||||||
|
#include <winbase.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
namespace win_eventlog {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
/** Windows error */
|
||||||
|
struct win32_error : public spdlog_ex
|
||||||
|
{
|
||||||
|
/** Formats an error report line: "user-message: error-code (system message)" */
|
||||||
|
static std::string format(std::string const &user_message, DWORD error_code = GetLastError())
|
||||||
|
{
|
||||||
|
std::string system_message;
|
||||||
|
|
||||||
|
LPSTR format_message_result{};
|
||||||
|
auto format_message_succeeded =
|
||||||
|
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
||||||
|
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result, 0, nullptr);
|
||||||
|
|
||||||
|
if (format_message_succeeded && format_message_result)
|
||||||
|
{
|
||||||
|
system_message = fmt::format(" ({})", format_message_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format_message_result)
|
||||||
|
{
|
||||||
|
LocalFree((HLOCAL)format_message_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt::format("{}: {}{}", user_message, error_code, system_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit win32_error(std::string const &func_name, DWORD error = GetLastError())
|
||||||
|
: spdlog_ex(format(func_name, error))
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Wrapper for security identifiers (SID) on Windows */
|
||||||
|
struct sid_t
|
||||||
|
{
|
||||||
|
std::vector<char> buffer_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
sid_t() {}
|
||||||
|
|
||||||
|
/** creates a wrapped SID copy */
|
||||||
|
static sid_t duplicate_sid(PSID psid)
|
||||||
|
{
|
||||||
|
if (!::IsValidSid(psid))
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("sid_t::sid_t(): invalid SID received");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const sid_length{::GetLengthSid(psid)};
|
||||||
|
|
||||||
|
sid_t result;
|
||||||
|
result.buffer_.resize(sid_length);
|
||||||
|
if (!::CopySid(sid_length, (PSID)result.as_sid(), psid))
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(win32_error("CopySid"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Retrieves pointer to the internal buffer contents as SID* */
|
||||||
|
SID *as_sid() const
|
||||||
|
{
|
||||||
|
return buffer_.empty() ? nullptr : (SID *)buffer_.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get SID for the current user */
|
||||||
|
static sid_t get_current_user_sid()
|
||||||
|
{
|
||||||
|
/* create and init RAII holder for process token */
|
||||||
|
struct process_token_t
|
||||||
|
{
|
||||||
|
HANDLE token_handle_ = INVALID_HANDLE_VALUE;
|
||||||
|
explicit process_token_t(HANDLE process)
|
||||||
|
{
|
||||||
|
if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle_))
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(win32_error("OpenProcessToken"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~process_token_t()
|
||||||
|
{
|
||||||
|
::CloseHandle(token_handle_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} current_process_token(::GetCurrentProcess()); // GetCurrentProcess returns pseudohandle, no leak here!
|
||||||
|
|
||||||
|
// Get the required size, this is expected to fail with ERROR_INSUFFICIENT_BUFFER and return the token size
|
||||||
|
DWORD tusize = 0;
|
||||||
|
if (::GetTokenInformation(current_process_token.token_handle_, TokenUser, NULL, 0, &tusize))
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(win32_error("GetTokenInformation should fail"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// get user token
|
||||||
|
std::vector<unsigned char> buffer(static_cast<size_t>(tusize));
|
||||||
|
if (!::GetTokenInformation(current_process_token.token_handle_, TokenUser, (LPVOID)buffer.data(), tusize, &tusize))
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(win32_error("GetTokenInformation"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a wrapper of the SID data as stored in the user token
|
||||||
|
return sid_t::duplicate_sid(((TOKEN_USER *)buffer.data())->User.Sid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct eventlog
|
||||||
|
{
|
||||||
|
static WORD get_event_type(details::log_msg const &msg)
|
||||||
|
{
|
||||||
|
switch (msg.level)
|
||||||
|
{
|
||||||
|
case level::trace:
|
||||||
|
case level::debug:
|
||||||
|
return EVENTLOG_SUCCESS;
|
||||||
|
|
||||||
|
case level::info:
|
||||||
|
return EVENTLOG_INFORMATION_TYPE;
|
||||||
|
|
||||||
|
case level::warn:
|
||||||
|
return EVENTLOG_WARNING_TYPE;
|
||||||
|
|
||||||
|
case level::err:
|
||||||
|
case level::critical:
|
||||||
|
case level::off:
|
||||||
|
return EVENTLOG_ERROR_TYPE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return EVENTLOG_INFORMATION_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static WORD get_event_category(details::log_msg const &msg)
|
||||||
|
{
|
||||||
|
return (WORD)msg.level;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows Event Log sink
|
||||||
|
*/
|
||||||
|
template<typename Mutex>
|
||||||
|
class win_eventlog_sink : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
HANDLE hEventLog_{NULL};
|
||||||
|
internal::sid_t current_user_sid_;
|
||||||
|
std::string source_;
|
||||||
|
WORD event_id_;
|
||||||
|
|
||||||
|
HANDLE event_log_handle()
|
||||||
|
{
|
||||||
|
if (!hEventLog_)
|
||||||
|
{
|
||||||
|
hEventLog_ = ::RegisterEventSource(nullptr, source_.c_str());
|
||||||
|
if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hEventLog_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
using namespace internal;
|
||||||
|
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
formatted.push_back('\0');
|
||||||
|
LPCSTR lp_str = static_cast<LPCSTR>(formatted.data());
|
||||||
|
|
||||||
|
auto succeeded = ::ReportEvent(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
|
||||||
|
current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr);
|
||||||
|
|
||||||
|
if (!succeeded)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(win32_error("ReportEvent"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */)
|
||||||
|
: source_(source)
|
||||||
|
, event_id_(event_id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
current_user_sid_ = internal::sid_t::get_current_user_sid();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// get_current_user_sid() is unlikely to fail and if it does, we can still proceed without
|
||||||
|
// current_user_sid but in the event log the record will have no user name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~win_eventlog_sink()
|
||||||
|
{
|
||||||
|
if (hEventLog_)
|
||||||
|
DeregisterEventSource(hEventLog_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace win_eventlog
|
||||||
|
|
||||||
|
using win_eventlog_sink_mt = win_eventlog::win_eventlog_sink<std::mutex>;
|
||||||
|
using win_eventlog_sink_st = win_eventlog::win_eventlog_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/sinks/wincolor_sink.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(HANDLE out_handle, color_mode mode)
|
||||||
|
: out_handle_(out_handle)
|
||||||
|
, mutex_(ConsoleMutex::mutex())
|
||||||
|
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
||||||
|
{
|
||||||
|
// check if out_handle is points to the actual console.
|
||||||
|
// ::GetConsoleMode() should return 0 if it is redirected or not valid console handle.
|
||||||
|
DWORD console_mode;
|
||||||
|
in_console_ = ::GetConsoleMode(out_handle, &console_mode) != 0;
|
||||||
|
|
||||||
|
set_color_mode(mode);
|
||||||
|
colors_[level::trace] = WHITE;
|
||||||
|
colors_[level::debug] = CYAN;
|
||||||
|
colors_[level::info] = GREEN;
|
||||||
|
colors_[level::warn] = YELLOW | BOLD;
|
||||||
|
colors_[level::err] = RED | BOLD; // red bold
|
||||||
|
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
|
||||||
|
colors_[level::off] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE wincolor_sink<ConsoleMutex>::~wincolor_sink()
|
||||||
|
{
|
||||||
|
this->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the color for the given level
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, WORD color)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
colors_[level] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
msg.color_range_start = 0;
|
||||||
|
msg.color_range_end = 0;
|
||||||
|
memory_buf_t formatted;
|
||||||
|
formatter_->format(msg, formatted);
|
||||||
|
if (!in_console_)
|
||||||
|
{
|
||||||
|
write_to_file_(formatted);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
||||||
|
{
|
||||||
|
// before color range
|
||||||
|
print_range_(formatted, 0, msg.color_range_start);
|
||||||
|
|
||||||
|
// in color range
|
||||||
|
auto orig_attribs = set_foreground_color_(colors_[msg.level]);
|
||||||
|
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
||||||
|
// reset to orig colors
|
||||||
|
::SetConsoleTextAttribute(out_handle_, orig_attribs);
|
||||||
|
print_range_(formatted, msg.color_range_end, formatted.size());
|
||||||
|
}
|
||||||
|
else // print without colors if color range is invalid (or color is disabled)
|
||||||
|
{
|
||||||
|
print_range_(formatted, 0, formatted.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::flush()
|
||||||
|
{
|
||||||
|
// windows console always flushed?
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
formatter_ = std::move(sink_formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case color_mode::always:
|
||||||
|
case color_mode::automatic:
|
||||||
|
should_do_colors_ = true;
|
||||||
|
break;
|
||||||
|
case color_mode::never:
|
||||||
|
should_do_colors_ = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
should_do_colors_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set foreground color and return the orig console attributes (for resetting later)
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
WORD SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(WORD attribs)
|
||||||
|
{
|
||||||
|
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
|
||||||
|
::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
|
||||||
|
WORD back_color = orig_buffer_info.wAttributes;
|
||||||
|
// retrieve the current background color
|
||||||
|
back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
|
||||||
|
// keep the background color unchanged
|
||||||
|
::SetConsoleTextAttribute(out_handle_, attribs | back_color);
|
||||||
|
return orig_buffer_info.wAttributes; // return orig attribs
|
||||||
|
}
|
||||||
|
|
||||||
|
// print a range of formatted message to console
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
|
||||||
|
{
|
||||||
|
auto size = static_cast<DWORD>(end - start);
|
||||||
|
::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
|
||||||
|
{
|
||||||
|
if (out_handle_ == nullptr) // no console and no file redirect
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto size = static_cast<DWORD>(formatted.size());
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD total_written = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
DWORD bytes_written = 0;
|
||||||
|
bool ok = ::WriteFile(out_handle_, formatted.data() + total_written, size - total_written, &bytes_written, nullptr) != 0;
|
||||||
|
if (!ok || bytes_written == 0)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError()));
|
||||||
|
}
|
||||||
|
total_written += bytes_written;
|
||||||
|
} while (total_written < size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wincolor_stdout_sink
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE wincolor_stdout_sink<ConsoleMutex>::wincolor_stdout_sink(color_mode mode)
|
||||||
|
: wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_OUTPUT_HANDLE), mode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// wincolor_stderr_sink
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode)
|
||||||
|
: wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/console_globals.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/sinks/sink.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <spdlog/details/windows_include.h>
|
||||||
|
#include <wincon.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
/*
|
||||||
|
* Windows color console sink. Uses WriteConsoleA to write to the console with
|
||||||
|
* colors
|
||||||
|
*/
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class wincolor_sink : public sink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const WORD BOLD = FOREGROUND_INTENSITY;
|
||||||
|
const WORD RED = FOREGROUND_RED;
|
||||||
|
const WORD GREEN = FOREGROUND_GREEN;
|
||||||
|
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||||
|
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||||
|
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
|
||||||
|
|
||||||
|
wincolor_sink(HANDLE out_handle, color_mode mode);
|
||||||
|
~wincolor_sink() override;
|
||||||
|
|
||||||
|
wincolor_sink(const wincolor_sink &other) = delete;
|
||||||
|
wincolor_sink &operator=(const wincolor_sink &other) = delete;
|
||||||
|
|
||||||
|
// change the color for the given level
|
||||||
|
void set_color(level::level_enum level, WORD color);
|
||||||
|
void log(const details::log_msg &msg) final override;
|
||||||
|
void flush() final override;
|
||||||
|
void set_pattern(const std::string &pattern) override final;
|
||||||
|
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
|
||||||
|
void set_color_mode(color_mode mode);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using mutex_t = typename ConsoleMutex::mutex_t;
|
||||||
|
HANDLE out_handle_;
|
||||||
|
mutex_t &mutex_;
|
||||||
|
bool in_console_;
|
||||||
|
bool should_do_colors_;
|
||||||
|
std::unique_ptr<spdlog::formatter> formatter_;
|
||||||
|
std::array<WORD, level::n_levels> colors_;
|
||||||
|
|
||||||
|
// set foreground color and return the orig console attributes (for resetting later)
|
||||||
|
WORD set_foreground_color_(WORD attribs);
|
||||||
|
|
||||||
|
// print a range of formatted message to console
|
||||||
|
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
|
||||||
|
|
||||||
|
// in case we are redirected to file (not in console mode)
|
||||||
|
void write_to_file_(const memory_buf_t &formatted);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class wincolor_stdout_sink : public wincolor_sink<ConsoleMutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit wincolor_stdout_sink(color_mode mode = color_mode::automatic);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
class wincolor_stderr_sink : public wincolor_sink<ConsoleMutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit wincolor_stderr_sink(color_mode mode = color_mode::automatic);
|
||||||
|
};
|
||||||
|
|
||||||
|
using wincolor_stdout_sink_mt = wincolor_stdout_sink<details::console_mutex>;
|
||||||
|
using wincolor_stdout_sink_st = wincolor_stdout_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
|
using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
|
||||||
|
using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "wincolor_sink-inl.h"
|
||||||
|
#endif
|
||||||
115
f4mp_originalcode/F4MPClient/include/spdlog/spdlog-inl.h
Normal file
115
f4mp_originalcode/F4MPClient/include/spdlog/spdlog-inl.h
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
SPDLOG_INLINE void initialize_logger(std::shared_ptr<logger> logger)
|
||||||
|
{
|
||||||
|
details::registry::instance().initialize_logger(std::move(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> get(const std::string &name)
|
||||||
|
{
|
||||||
|
return details::registry::instance().get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void set_formatter(std::unique_ptr<spdlog::formatter> formatter)
|
||||||
|
{
|
||||||
|
details::registry::instance().set_formatter(std::move(formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void set_pattern(std::string pattern, pattern_time_type time_type)
|
||||||
|
{
|
||||||
|
set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(std::move(pattern), time_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void enable_backtrace(size_t n_messages)
|
||||||
|
{
|
||||||
|
details::registry::instance().enable_backtrace(n_messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void disable_backtrace()
|
||||||
|
{
|
||||||
|
details::registry::instance().disable_backtrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void dump_backtrace()
|
||||||
|
{
|
||||||
|
default_logger_raw()->dump_backtrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void set_level(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
details::registry::instance().set_level(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void flush_on(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
details::registry::instance().flush_on(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void flush_every(std::chrono::seconds interval)
|
||||||
|
{
|
||||||
|
details::registry::instance().flush_every(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void set_error_handler(void (*handler)(const std::string &msg))
|
||||||
|
{
|
||||||
|
details::registry::instance().set_error_handler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void register_logger(std::shared_ptr<logger> logger)
|
||||||
|
{
|
||||||
|
details::registry::instance().register_logger(std::move(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun)
|
||||||
|
{
|
||||||
|
details::registry::instance().apply_all(fun);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void drop(const std::string &name)
|
||||||
|
{
|
||||||
|
details::registry::instance().drop(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void drop_all()
|
||||||
|
{
|
||||||
|
details::registry::instance().drop_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void shutdown()
|
||||||
|
{
|
||||||
|
details::registry::instance().shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void set_automatic_registration(bool automatic_registration)
|
||||||
|
{
|
||||||
|
details::registry::instance().set_automatic_registration(automatic_registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<spdlog::logger> default_logger()
|
||||||
|
{
|
||||||
|
return details::registry::instance().default_logger();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::logger *default_logger_raw()
|
||||||
|
{
|
||||||
|
return details::registry::instance().get_default_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void set_default_logger(std::shared_ptr<spdlog::logger> default_logger)
|
||||||
|
{
|
||||||
|
details::registry::instance().set_default_logger(std::move(default_logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
289
f4mp_originalcode/F4MPClient/include/spdlog/spdlog.h
Normal file
289
f4mp_originalcode/F4MPClient/include/spdlog/spdlog.h
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
// spdlog main header file.
|
||||||
|
// see example.cpp for usage example
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#define SPDLOG_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/registry.h>
|
||||||
|
#include <spdlog/logger.h>
|
||||||
|
#include <spdlog/version.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
using default_factory = synchronous_factory;
|
||||||
|
|
||||||
|
// Create and register a logger with a templated sink type
|
||||||
|
// The logger's level, formatter and flush level will be set according the
|
||||||
|
// global settings.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
|
{
|
||||||
|
return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize and register a logger,
|
||||||
|
// formatter and flush level will be set according the global settings.
|
||||||
|
//
|
||||||
|
// Useful for initializing manually created loggers with the global settings.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// auto mylogger = std::make_shared<spdlog::logger>("mylogger", ...);
|
||||||
|
// spdlog::initialize_logger(mylogger);
|
||||||
|
SPDLOG_API void initialize_logger(std::shared_ptr<logger> logger);
|
||||||
|
|
||||||
|
// Return an existing logger or nullptr if a logger with such name doesn't
|
||||||
|
// exist.
|
||||||
|
// example: spdlog::get("my_logger")->info("hello {}", "world");
|
||||||
|
SPDLOG_API std::shared_ptr<logger> get(const std::string &name);
|
||||||
|
|
||||||
|
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||||
|
SPDLOG_API void set_formatter(std::unique_ptr<spdlog::formatter> formatter);
|
||||||
|
|
||||||
|
// Set global format string.
|
||||||
|
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
|
||||||
|
SPDLOG_API void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
|
||||||
|
|
||||||
|
// enable global backtrace support
|
||||||
|
SPDLOG_API void enable_backtrace(size_t n_messages);
|
||||||
|
|
||||||
|
// disable global backtrace support
|
||||||
|
SPDLOG_API void disable_backtrace();
|
||||||
|
|
||||||
|
// call dump backtrace on default logger
|
||||||
|
SPDLOG_API void dump_backtrace();
|
||||||
|
|
||||||
|
// Set global logging level
|
||||||
|
SPDLOG_API void set_level(level::level_enum log_level);
|
||||||
|
|
||||||
|
// Set global flush level
|
||||||
|
SPDLOG_API void flush_on(level::level_enum log_level);
|
||||||
|
|
||||||
|
// Start/Restart a periodic flusher thread
|
||||||
|
// Warning: Use only if all your loggers are thread safe!
|
||||||
|
SPDLOG_API void flush_every(std::chrono::seconds interval);
|
||||||
|
|
||||||
|
// Set global error handler
|
||||||
|
SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
|
||||||
|
|
||||||
|
// Register the given logger with the given name
|
||||||
|
SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
|
||||||
|
|
||||||
|
// Apply a user defined function on all registered loggers
|
||||||
|
// Example:
|
||||||
|
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
|
||||||
|
SPDLOG_API void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
|
||||||
|
|
||||||
|
// Drop the reference to the given logger
|
||||||
|
SPDLOG_API void drop(const std::string &name);
|
||||||
|
|
||||||
|
// Drop all references from the registry
|
||||||
|
SPDLOG_API void drop_all();
|
||||||
|
|
||||||
|
// stop any running threads started by spdlog and clean registry loggers
|
||||||
|
SPDLOG_API void shutdown();
|
||||||
|
|
||||||
|
// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
|
||||||
|
SPDLOG_API void set_automatic_registration(bool automatic_registration);
|
||||||
|
|
||||||
|
// API for using default logger (stdout_color_mt),
|
||||||
|
// e.g: spdlog::info("Message {}", 1);
|
||||||
|
//
|
||||||
|
// The default logger object can be accessed using the spdlog::default_logger():
|
||||||
|
// For example, to add another sink to it:
|
||||||
|
// spdlog::default_logger()->sinks()->push_back(some_sink);
|
||||||
|
//
|
||||||
|
// The default logger can replaced using spdlog::set_default_logger(new_logger).
|
||||||
|
// For example, to replace it with a file logger.
|
||||||
|
//
|
||||||
|
// IMPORTANT:
|
||||||
|
// The default API is thread safe (for _mt loggers), but:
|
||||||
|
// set_default_logger() *should not* be used concurrently with the default API.
|
||||||
|
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
|
|
||||||
|
SPDLOG_API std::shared_ptr<spdlog::logger> default_logger();
|
||||||
|
|
||||||
|
SPDLOG_API spdlog::logger *default_logger_raw();
|
||||||
|
|
||||||
|
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->log(source, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
inline void log(level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
inline void trace(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->trace(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
inline void debug(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->debug(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
inline void info(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->info(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
inline void warn(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->warn(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
inline void error(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->error(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FormatString, typename... Args>
|
||||||
|
inline void critical(const FormatString &fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->critical(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void log(source_loc source, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->log(source, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->log(lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void trace(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->trace(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void debug(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->debug(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void info(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->info(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void warn(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->warn(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void error(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void critical(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->critical(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
//
|
||||||
|
// enable/disable log calls at compile time according to global level.
|
||||||
|
//
|
||||||
|
// define SPDLOG_ACTIVE_LEVEL to one of those (before including spdlog.h):
|
||||||
|
// SPDLOG_LEVEL_TRACE,
|
||||||
|
// SPDLOG_LEVEL_DEBUG,
|
||||||
|
// SPDLOG_LEVEL_INFO,
|
||||||
|
// SPDLOG_LEVEL_WARN,
|
||||||
|
// SPDLOG_LEVEL_ERROR,
|
||||||
|
// SPDLOG_LEVEL_CRITICAL,
|
||||||
|
// SPDLOG_LEVEL_OFF
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SPDLOG_LOGGER_CALL(logger, level, ...) (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
|
||||||
|
#define SPDLOG_LOGGER_TRACE(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__)
|
||||||
|
#define SPDLOG_TRACE(...) SPDLOG_LOGGER_TRACE(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_TRACE(logger, ...) (void)0
|
||||||
|
#define SPDLOG_TRACE(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
|
||||||
|
#define SPDLOG_LOGGER_DEBUG(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__)
|
||||||
|
#define SPDLOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0
|
||||||
|
#define SPDLOG_DEBUG(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO
|
||||||
|
#define SPDLOG_LOGGER_INFO(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__)
|
||||||
|
#define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_INFO(logger, ...) (void)0
|
||||||
|
#define SPDLOG_INFO(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN
|
||||||
|
#define SPDLOG_LOGGER_WARN(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__)
|
||||||
|
#define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_WARN(logger, ...) (void)0
|
||||||
|
#define SPDLOG_WARN(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR
|
||||||
|
#define SPDLOG_LOGGER_ERROR(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__)
|
||||||
|
#define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_ERROR(logger, ...) (void)0
|
||||||
|
#define SPDLOG_ERROR(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL
|
||||||
|
#define SPDLOG_LOGGER_CRITICAL(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__)
|
||||||
|
#define SPDLOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0
|
||||||
|
#define SPDLOG_CRITICAL(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog-inl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // SPDLOG_H
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue